* [PATCH 01/29] hw/core: Add Remote Port protocol packet definition
2026-02-05 19:57 [PATCH 00/29] hw/core: Introduce Remote Port Co-simulation Protocol Ruslan Ruslichenko
@ 2026-02-05 19:57 ` Ruslan Ruslichenko
2026-02-05 20:38 ` Daniel P. Berrangé
2026-02-05 19:57 ` [PATCH 02/29] hw/core: Add Remote Port header helpers Ruslan Ruslichenko
` (27 subsequent siblings)
28 siblings, 1 reply; 41+ messages in thread
From: Ruslan Ruslichenko @ 2026-02-05 19:57 UTC (permalink / raw)
To: qemu-devel
Cc: qemu-arm, peter.maydell, artem_mygaiev, volodymyr_babchuk,
takahiro.nakata.wr, Edgar E . Iglesias, francisco.iglesias,
Ruslan_Ruslichenko, Edgar E . Iglesias
From: Ruslan Ruslichenko <Ruslan_Ruslichenko@epam.com>
Introduce packet definition for Remote Port protocol.
Remote Port is a socket based inter-simulation protocol
designed to connect QEMU to external simulator (such as
SystemC models, RTL simulators, etc) for hardware co-
simulation.
The protocol supports bidirectional communication for:
- Connection setup, version negotiation (Hello/Cfg packets)
- Memory and MMIO transactions (Read/Write packets)
- Interrupts and GPIO signaling (Interrupt packets)
- Time synchronization (Sync packets)
The patch introduces header file with packet definition
used by protocol.
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>
---
include/hw/core/remote-port-proto.h | 305 ++++++++++++++++++++++++++++
1 file changed, 305 insertions(+)
create mode 100644 include/hw/core/remote-port-proto.h
diff --git a/include/hw/core/remote-port-proto.h b/include/hw/core/remote-port-proto.h
new file mode 100644
index 0000000000..cbe1498df0
--- /dev/null
+++ b/include/hw/core/remote-port-proto.h
@@ -0,0 +1,305 @@
+// SPDX-License-Identifier: MIT
+/*
+ * QEMU remote port protocol parts.
+ *
+ * Copyright (c) 2013 Xilinx Inc
+ * Written by Edgar E. Iglesias <edgar.iglesias@xilinx.com>
+ *
+ * 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__
+
+/*
+ * 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),
+
+ /*
+ * The access targets the I/O address space.
+ */
+ RP_BUS_ATTR_IO_ACCESS = (1 << 4),
+
+ /*
+ * 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;
+ };
+};
+
+#endif
--
2.43.0
^ permalink raw reply related [flat|nested] 41+ messages in thread* Re: [PATCH 01/29] hw/core: Add Remote Port protocol packet definition
2026-02-05 19:57 ` [PATCH 01/29] hw/core: Add Remote Port protocol packet definition Ruslan Ruslichenko
@ 2026-02-05 20:38 ` Daniel P. Berrangé
2026-02-06 17:45 ` Ruslan Ruslichenko
0 siblings, 1 reply; 41+ messages in thread
From: Daniel P. Berrangé @ 2026-02-05 20:38 UTC (permalink / raw)
To: Ruslan Ruslichenko
Cc: qemu-devel, qemu-arm, peter.maydell, artem_mygaiev,
volodymyr_babchuk, takahiro.nakata.wr, Edgar E . Iglesias,
francisco.iglesias, Ruslan_Ruslichenko, Edgar E . Iglesias
On Thu, Feb 05, 2026 at 08:57:56PM +0100, Ruslan Ruslichenko wrote:
> From: Ruslan Ruslichenko <Ruslan_Ruslichenko@epam.com>
>
> Introduce packet definition for Remote Port protocol.
>
> Remote Port is a socket based inter-simulation protocol
> designed to connect QEMU to external simulator (such as
> SystemC models, RTL simulators, etc) for hardware co-
> simulation.
>
> The protocol supports bidirectional communication for:
> - Connection setup, version negotiation (Hello/Cfg packets)
> - Memory and MMIO transactions (Read/Write packets)
> - Interrupts and GPIO signaling (Interrupt packets)
> - Time synchronization (Sync packets)
>
> The patch introduces header file with packet definition
> used by protocol.
>
> 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>
> ---
> include/hw/core/remote-port-proto.h | 305 ++++++++++++++++++++++++++++
> 1 file changed, 305 insertions(+)
> create mode 100644 include/hw/core/remote-port-proto.h
>
> diff --git a/include/hw/core/remote-port-proto.h b/include/hw/core/remote-port-proto.h
> new file mode 100644
> index 0000000000..cbe1498df0
> --- /dev/null
> +++ b/include/hw/core/remote-port-proto.h
> @@ -0,0 +1,305 @@
> +// SPDX-License-Identifier: MIT
Preferrably stick this inside the comment block below instead of
mixing C and C++ style comments.
> +/*
> + * QEMU remote port protocol parts.
> + *
> + * Copyright (c) 2013 Xilinx Inc
> + * Written by Edgar E. Iglesias <edgar.iglesias@xilinx.com>
> + *
> + * 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.
Including this is OK, since IIUC in this case you're carrying on
with someone else's code, and so aren't permitted to remove their
license comment.
If there are any files later in the series that you wrote yourself
from scratch though, they can omit any license comments, only the
SPDX is need.
> + */
> +#ifndef REMOTE_PORT_PROTO_H__
> +#define REMOTE_PORT_PROTO_H__
> +
> +/*
> + * 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
Is this comment & conditional #define still relevant today ?
For the rest of QEMU we just use QEMU_PACKED whose definition matches
the #else clause. Ideally change the patch to use QEMU_PACKED, if not,
mention in the commit message why it can't be used.
With regards,
Daniel
--
|: https://berrange.com -o- https://www.flickr.com/photos/dberrange :|
|: https://libvirt.org -o- https://fstop138.berrange.com :|
|: https://entangle-photo.org -o- https://www.instagram.com/dberrange :|
^ permalink raw reply [flat|nested] 41+ messages in thread
* Re: [PATCH 01/29] hw/core: Add Remote Port protocol packet definition
2026-02-05 20:38 ` Daniel P. Berrangé
@ 2026-02-06 17:45 ` Ruslan Ruslichenko
2026-02-06 18:06 ` Daniel P. Berrangé
0 siblings, 1 reply; 41+ messages in thread
From: Ruslan Ruslichenko @ 2026-02-06 17:45 UTC (permalink / raw)
To: Daniel P. Berrangé
Cc: qemu-devel, qemu-arm, peter.maydell, artem_mygaiev,
volodymyr_babchuk, takahiro.nakata.wr, Edgar E . Iglesias,
francisco.iglesias, Ruslan_Ruslichenko, Edgar E . Iglesias
On Thu, Feb 5, 2026 at 9:39 PM Daniel P. Berrangé <berrange@redhat.com> wrote:
>
> On Thu, Feb 05, 2026 at 08:57:56PM +0100, Ruslan Ruslichenko wrote:
> > From: Ruslan Ruslichenko <Ruslan_Ruslichenko@epam.com>
> >
> > Introduce packet definition for Remote Port protocol.
> >
> > Remote Port is a socket based inter-simulation protocol
> > designed to connect QEMU to external simulator (such as
> > SystemC models, RTL simulators, etc) for hardware co-
> > simulation.
> >
> > The protocol supports bidirectional communication for:
> > - Connection setup, version negotiation (Hello/Cfg packets)
> > - Memory and MMIO transactions (Read/Write packets)
> > - Interrupts and GPIO signaling (Interrupt packets)
> > - Time synchronization (Sync packets)
> >
> > The patch introduces header file with packet definition
> > used by protocol.
> >
> > 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>
> > ---
> > include/hw/core/remote-port-proto.h | 305 ++++++++++++++++++++++++++++
> > 1 file changed, 305 insertions(+)
> > create mode 100644 include/hw/core/remote-port-proto.h
> >
> > diff --git a/include/hw/core/remote-port-proto.h b/include/hw/core/remote-port-proto.h
> > new file mode 100644
> > index 0000000000..cbe1498df0
> > --- /dev/null
> > +++ b/include/hw/core/remote-port-proto.h
> > @@ -0,0 +1,305 @@
> > +// SPDX-License-Identifier: MIT
>
> Preferrably stick this inside the comment block below instead of
> mixing C and C++ style comments.
>
> > +/*
> > + * QEMU remote port protocol parts.
> > + *
> > + * Copyright (c) 2013 Xilinx Inc
> > + * Written by Edgar E. Iglesias <edgar.iglesias@xilinx.com>
> > + *
> > + * 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.
>
> Including this is OK, since IIUC in this case you're carrying on
> with someone else's code, and so aren't permitted to remove their
> license comment.
>
> If there are any files later in the series that you wrote yourself
> from scratch though, they can omit any license comments, only the
> SPDX is need.
>
Understood.
Basically all headers in the patch series were already present, which
is the reason I left them.
>
> > + */
> > +#ifndef REMOTE_PORT_PROTO_H__
> > +#define REMOTE_PORT_PROTO_H__
> > +
> > +/*
> > + * 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
>
> Is this comment & conditional #define still relevant today ?
>
> For the rest of QEMU we just use QEMU_PACKED whose definition matches
> the #else clause. Ideally change the patch to use QEMU_PACKED, if not,
> mention in the commit message why it can't be used.
>
I will double check. Might be some old mingw version which is not actual today.
>
> With regards,
> Daniel
> --
> |: https://berrange.com -o- https://www.flickr.com/photos/dberrange :|
> |: https://libvirt.org -o- https://fstop138.berrange.com :|
> |: https://entangle-photo.org -o- https://www.instagram.com/dberrange :|
>
--
BR,
Ruslan
^ permalink raw reply [flat|nested] 41+ messages in thread
* Re: [PATCH 01/29] hw/core: Add Remote Port protocol packet definition
2026-02-06 17:45 ` Ruslan Ruslichenko
@ 2026-02-06 18:06 ` Daniel P. Berrangé
0 siblings, 0 replies; 41+ messages in thread
From: Daniel P. Berrangé @ 2026-02-06 18:06 UTC (permalink / raw)
To: Ruslan Ruslichenko
Cc: qemu-devel, qemu-arm, peter.maydell, artem_mygaiev,
volodymyr_babchuk, takahiro.nakata.wr, Edgar E . Iglesias,
francisco.iglesias, Ruslan_Ruslichenko, Edgar E . Iglesias
On Fri, Feb 06, 2026 at 06:45:37PM +0100, Ruslan Ruslichenko wrote:
> On Thu, Feb 5, 2026 at 9:39 PM Daniel P. Berrangé <berrange@redhat.com> wrote:
> >
> > On Thu, Feb 05, 2026 at 08:57:56PM +0100, Ruslan Ruslichenko wrote:
> > > From: Ruslan Ruslichenko <Ruslan_Ruslichenko@epam.com>
> > >
> > > Introduce packet definition for Remote Port protocol.
> > >
> > > Remote Port is a socket based inter-simulation protocol
> > > designed to connect QEMU to external simulator (such as
> > > SystemC models, RTL simulators, etc) for hardware co-
> > > simulation.
> > >
> > > The protocol supports bidirectional communication for:
> > > - Connection setup, version negotiation (Hello/Cfg packets)
> > > - Memory and MMIO transactions (Read/Write packets)
> > > - Interrupts and GPIO signaling (Interrupt packets)
> > > - Time synchronization (Sync packets)
> > >
> > > The patch introduces header file with packet definition
> > > used by protocol.
> > >
> > > 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>
> > > ---
> > > include/hw/core/remote-port-proto.h | 305 ++++++++++++++++++++++++++++
> > > 1 file changed, 305 insertions(+)
> > > create mode 100644 include/hw/core/remote-port-proto.h
> > >
> > > diff --git a/include/hw/core/remote-port-proto.h b/include/hw/core/remote-port-proto.h
> > > new file mode 100644
> > > index 0000000000..cbe1498df0
> > > --- /dev/null
> > > +++ b/include/hw/core/remote-port-proto.h
> > > @@ -0,0 +1,305 @@
> > > +// SPDX-License-Identifier: MIT
> >
> > Preferrably stick this inside the comment block below instead of
> > mixing C and C++ style comments.
> >
> > > +/*
> > > + * QEMU remote port protocol parts.
> > > + *
> > > + * Copyright (c) 2013 Xilinx Inc
> > > + * Written by Edgar E. Iglesias <edgar.iglesias@xilinx.com>
> > > + *
> > > + * 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.
> >
> > Including this is OK, since IIUC in this case you're carrying on
> > with someone else's code, and so aren't permitted to remove their
> > license comment.
> >
> > If there are any files later in the series that you wrote yourself
> > from scratch though, they can omit any license comments, only the
> > SPDX is need.
> >
>
> Understood.
> Basically all headers in the patch series were already present, which
> is the reason I left them.
>
> >
> > > + */
> > > +#ifndef REMOTE_PORT_PROTO_H__
> > > +#define REMOTE_PORT_PROTO_H__
> > > +
> > > +/*
> > > + * 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
> >
> > Is this comment & conditional #define still relevant today ?
> >
> > For the rest of QEMU we just use QEMU_PACKED whose definition matches
> > the #else clause. Ideally change the patch to use QEMU_PACKED, if not,
> > mention in the commit message why it can't be used.
> >
>
> I will double check. Might be some old mingw version which is not actual today.
We have CI jobs for mingw on gitlab. If you fork the repo, and push your
branch to your gitlab fork, while setting "QEMU_CI=2", it'll run the
CI jobs. The file docs/devel/testing/ci-jobs.rst.inc has more info.
If it passes with the CI (current mingw in Fedora, and with latest Msys2 on
Windows) then that's sufficient. We don't try to support older versions.
With regards,
Daniel
--
|: https://berrange.com -o- https://www.flickr.com/photos/dberrange :|
|: https://libvirt.org -o- https://fstop138.berrange.com :|
|: https://entangle-photo.org -o- https://www.instagram.com/dberrange :|
^ permalink raw reply [flat|nested] 41+ messages in thread
* [PATCH 02/29] hw/core: Add Remote Port header helpers
2026-02-05 19:57 [PATCH 00/29] hw/core: Introduce Remote Port Co-simulation Protocol Ruslan Ruslichenko
2026-02-05 19:57 ` [PATCH 01/29] hw/core: Add Remote Port protocol packet definition Ruslan Ruslichenko
@ 2026-02-05 19:57 ` Ruslan Ruslichenko
2026-02-05 19:57 ` [PATCH 03/29] hw/core: Add Remote Port session state and hello protocol Ruslan Ruslichenko
` (26 subsequent siblings)
28 siblings, 0 replies; 41+ messages in thread
From: Ruslan Ruslichenko @ 2026-02-05 19:57 UTC (permalink / raw)
To: qemu-devel
Cc: qemu-arm, peter.maydell, artem_mygaiev, volodymyr_babchuk,
takahiro.nakata.wr, Edgar E . Iglesias, francisco.iglesias,
Ruslan_Ruslichenko, Edgar E . Iglesias
From: Ruslan Ruslichenko <Ruslan_Ruslichenko@epam.com>
Introduce core packet handling logic for Remote Port
protocol. This includes helpers for encoding and decoding
packet headers and payload.
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 | 82 +++++++++++++++++++++++++++++
include/hw/core/remote-port-proto.h | 8 +++
2 files changed, 90 insertions(+)
create mode 100644 hw/core/remote-port-proto.c
diff --git a/hw/core/remote-port-proto.c b/hw/core/remote-port-proto.c
new file mode 100644
index 0000000000..1653fe7b78
--- /dev/null
+++ b/hw/core/remote-port-proto.c
@@ -0,0 +1,82 @@
+// SPDX-License-Identifier: MIT
+/*
+ * Remote-port protocol
+ *
+ * Copyright (c) 2013 Xilinx Inc
+ * Written by Edgar E. Iglesias <edgar.iglesias@xilinx.com>
+ *
+ * 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.
+ */
+
+#include "qemu/osdep.h"
+#include "qemu/bswap.h"
+#include "hw/core/remote-port-proto.h"
+
+static const char *rp_cmd_names[RP_CMD_max + 1] = {
+ [RP_CMD_nop] = "nop",
+ [RP_CMD_hello] = "hello",
+ [RP_CMD_cfg] = "cfg",
+ [RP_CMD_read] = "read",
+ [RP_CMD_write] = "write",
+ [RP_CMD_interrupt] = "interrupt",
+ [RP_CMD_sync] = "sync",
+ [RP_CMD_ats_req] = "ats_request",
+ [RP_CMD_ats_inv] = "ats_invalidation",
+};
+
+const char *rp_cmd_to_string(enum rp_cmd cmd)
+{
+ assert(cmd <= RP_CMD_max);
+ return rp_cmd_names[cmd];
+}
+
+int rp_decode_hdr(struct rp_pkt *pkt)
+{
+ int used = 0;
+
+ pkt->hdr.cmd = be32_to_cpu(pkt->hdr.cmd);
+ pkt->hdr.len = be32_to_cpu(pkt->hdr.len);
+ pkt->hdr.id = be32_to_cpu(pkt->hdr.id);
+ pkt->hdr.flags = be32_to_cpu(pkt->hdr.flags);
+ pkt->hdr.dev = be32_to_cpu(pkt->hdr.dev);
+ used += sizeof pkt->hdr;
+ return used;
+}
+
+int rp_decode_payload(struct rp_pkt *pkt)
+{
+ int used = 0;
+
+ switch (pkt->hdr.cmd) {
+ /* TBD */
+ default:
+ break;
+ }
+ return used;
+}
+
+void rp_encode_hdr(struct rp_pkt_hdr *hdr, uint32_t cmd, uint32_t id,
+ uint32_t dev, uint32_t len, uint32_t flags)
+{
+ hdr->cmd = cpu_to_be32(cmd);
+ hdr->len = cpu_to_be32(len);
+ hdr->id = cpu_to_be32(id);
+ hdr->dev = cpu_to_be32(dev);
+ hdr->flags = cpu_to_be32(flags);
+}
diff --git a/include/hw/core/remote-port-proto.h b/include/hw/core/remote-port-proto.h
index cbe1498df0..1d8e1b25da 100644
--- a/include/hw/core/remote-port-proto.h
+++ b/include/hw/core/remote-port-proto.h
@@ -302,4 +302,12 @@ struct rp_pkt {
};
};
+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);
+
#endif
--
2.43.0
^ permalink raw reply related [flat|nested] 41+ messages in thread* [PATCH 03/29] hw/core: Add Remote Port session state and hello protocol
2026-02-05 19:57 [PATCH 00/29] hw/core: Introduce Remote Port Co-simulation Protocol Ruslan Ruslichenko
2026-02-05 19:57 ` [PATCH 01/29] hw/core: Add Remote Port protocol packet definition Ruslan Ruslichenko
2026-02-05 19:57 ` [PATCH 02/29] hw/core: Add Remote Port header helpers Ruslan Ruslichenko
@ 2026-02-05 19:57 ` Ruslan Ruslichenko
2026-02-05 19:57 ` [PATCH 04/29] hw/core: Implement Remote Port bus access helpers Ruslan Ruslichenko
` (25 subsequent siblings)
28 siblings, 0 replies; 41+ messages in thread
From: Ruslan Ruslichenko @ 2026-02-05 19:57 UTC (permalink / raw)
To: qemu-devel
Cc: qemu-arm, peter.maydell, artem_mygaiev, volodymyr_babchuk,
takahiro.nakata.wr, Edgar E . Iglesias, francisco.iglesias,
Ruslan_Ruslichenko, Edgar E . Iglesias
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
^ permalink raw reply related [flat|nested] 41+ messages in thread* [PATCH 04/29] hw/core: Implement Remote Port bus access helpers
2026-02-05 19:57 [PATCH 00/29] hw/core: Introduce Remote Port Co-simulation Protocol Ruslan Ruslichenko
` (2 preceding siblings ...)
2026-02-05 19:57 ` [PATCH 03/29] hw/core: Add Remote Port session state and hello protocol Ruslan Ruslichenko
@ 2026-02-05 19:57 ` Ruslan Ruslichenko
2026-02-05 19:58 ` [PATCH 05/29] hw/core: Implement Remote Port irq, sync and ATS helpers Ruslan Ruslichenko
` (24 subsequent siblings)
28 siblings, 0 replies; 41+ messages in thread
From: Ruslan Ruslichenko @ 2026-02-05 19:57 UTC (permalink / raw)
To: qemu-devel
Cc: qemu-arm, peter.maydell, artem_mygaiev, volodymyr_babchuk,
takahiro.nakata.wr, Edgar E . Iglesias, francisco.iglesias,
Ruslan_Ruslichenko, Edgar E . Iglesias
From: Ruslan Ruslichenko <Ruslan_Ruslichenko@epam.com>
Add bus access logic for Remote Port memory transactions.
The patch add support for read and write commands, which
enables data communication via the protocol.
This includes support for both legacy and extended packet
formats (such as 64-bit Master IDs) based on peer's
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 | 152 ++++++++++++++++++++++++++++
include/hw/core/remote-port-proto.h | 133 ++++++++++++++++++++++++
2 files changed, 285 insertions(+)
diff --git a/hw/core/remote-port-proto.c b/hw/core/remote-port-proto.c
index 969dfc3cb2..33a6769087 100644
--- a/hw/core/remote-port-proto.c
+++ b/hw/core/remote-port-proto.c
@@ -62,6 +62,8 @@ int rp_decode_hdr(struct rp_pkt *pkt)
int rp_decode_payload(struct rp_pkt *pkt)
{
int used = 0;
+ /* Master_id has an odd decoding due to historical reasons. */
+ uint64_t master_id;
switch (pkt->hdr.cmd) {
case RP_CMD_hello:
@@ -101,6 +103,34 @@ int rp_decode_payload(struct rp_pkt *pkt)
*/
used = pkt->hdr.len;
break;
+ case RP_CMD_write:
+ case RP_CMD_read:
+ assert(pkt->hdr.len >= sizeof pkt->busaccess - sizeof pkt->hdr);
+ pkt->busaccess.timestamp = be64_to_cpu(pkt->busaccess.timestamp);
+ pkt->busaccess.addr = be64_to_cpu(pkt->busaccess.addr);
+ pkt->busaccess.attributes = be64_to_cpu(pkt->busaccess.attributes);
+ pkt->busaccess.len = be32_to_cpu(pkt->busaccess.len);
+ pkt->busaccess.width = be32_to_cpu(pkt->busaccess.width);
+ pkt->busaccess.stream_width = be32_to_cpu(pkt->busaccess.stream_width);
+ master_id = be16_to_cpu(pkt->busaccess.master_id);
+
+ used += sizeof pkt->busaccess - sizeof pkt->hdr;
+
+ if (pkt->busaccess.attributes & RP_BUS_ATTR_EXT_BASE) {
+ struct rp_pkt_busaccess_ext_base *pext = &pkt->busaccess_ext_base;
+
+ assert(pkt->hdr.len >= sizeof *pext - sizeof pkt->hdr);
+ master_id |= (uint64_t)be16_to_cpu(pext->master_id_31_16) << 16;
+ master_id |= (uint64_t)be32_to_cpu(pext->master_id_63_32) << 32;
+ pext->data_offset = be32_to_cpu(pext->data_offset);
+ pext->next_offset = be32_to_cpu(pext->next_offset);
+ pext->byte_enable_offset = be32_to_cpu(pext->byte_enable_offset);
+ pext->byte_enable_len = be32_to_cpu(pext->byte_enable_len);
+
+ used += sizeof *pext - sizeof pkt->busaccess;
+ }
+ pkt->busaccess.master_id = master_id;
+ break;
default:
break;
}
@@ -143,6 +173,127 @@ size_t rp_encode_hello_caps(uint32_t id, uint32_t dev, struct rp_pkt_hello *pkt,
return sizeof *pkt;
}
+static void rp_encode_busaccess_common(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)
+{
+ pkt->timestamp = cpu_to_be64(clk);
+ pkt->master_id = cpu_to_be16(master_id);
+ pkt->addr = cpu_to_be64(addr);
+ pkt->attributes = cpu_to_be64(attr);
+ pkt->len = cpu_to_be32(size);
+ pkt->width = cpu_to_be32(width);
+ pkt->stream_width = cpu_to_be32(stream_width);
+}
+
+size_t 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)
+{
+ rp_encode_hdr(&pkt->hdr, RP_CMD_read, id, dev,
+ sizeof *pkt - sizeof pkt->hdr, 0);
+ rp_encode_busaccess_common(pkt, clk, master_id, addr, attr,
+ size, width, stream_width);
+ return sizeof *pkt;
+}
+
+size_t 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)
+{
+ rp_encode_hdr(&pkt->hdr, RP_CMD_read, id, dev,
+ sizeof *pkt - sizeof pkt->hdr + size, RP_PKT_FLAGS_response);
+ rp_encode_busaccess_common(pkt, clk, master_id, addr, attr,
+ size, width, stream_width);
+ return sizeof *pkt + size;
+}
+
+size_t 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)
+{
+ rp_encode_hdr(&pkt->hdr, RP_CMD_write, id, dev,
+ sizeof *pkt - sizeof pkt->hdr + size, 0);
+ rp_encode_busaccess_common(pkt, clk, master_id, addr, attr,
+ size, width, stream_width);
+ return sizeof *pkt;
+}
+
+size_t 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)
+{
+ rp_encode_hdr(&pkt->hdr, RP_CMD_write, id, dev,
+ sizeof *pkt - sizeof pkt->hdr, RP_PKT_FLAGS_response);
+ rp_encode_busaccess_common(pkt, clk, master_id, addr, attr,
+ size, width, stream_width);
+ return sizeof *pkt;
+}
+
+/* New API for extended header. */
+size_t rp_encode_busaccess(struct rp_peer_state *peer,
+ struct rp_pkt_busaccess_ext_base *pkt,
+ struct rp_encode_busaccess_in *in) {
+ struct rp_pkt_busaccess *pkt_v4_0 = (void *) pkt;
+ uint32_t hsize = 0;
+ uint32_t ret_size = 0;
+
+ /* Allocate packet space. */
+ if (in->cmd == RP_CMD_write && !(in->flags & RP_PKT_FLAGS_response)) {
+ hsize = in->size;
+ }
+ if (in->cmd == RP_CMD_read && (in->flags & RP_PKT_FLAGS_response)) {
+ hsize = in->size;
+ ret_size = in->size;
+ }
+
+ /*
+ * If peer does not support the busaccess base extensions, use the
+ * old layout. For responses, what matters is if we're responding
+ * to a packet with the extensions.
+ */
+ if (!peer->caps.busaccess_ext_base && !(in->attr & RP_BUS_ATTR_EXT_BASE)) {
+ /* Old layout. */
+ assert(in->master_id < UINT16_MAX);
+
+ rp_encode_hdr(&pkt->hdr, in->cmd, in->id, in->dev,
+ sizeof *pkt_v4_0 - sizeof pkt->hdr + hsize, in->flags);
+ rp_encode_busaccess_common(pkt_v4_0, in->clk, in->master_id,
+ in->addr, in->attr,
+ in->size, in->width, in->stream_width);
+ return sizeof *pkt_v4_0 + ret_size;
+ }
+
+ /* Encode the extended fields. */
+ pkt->master_id_31_16 = cpu_to_be16(in->master_id >> 16);
+ pkt->master_id_63_32 = cpu_to_be32(in->master_id >> 32);
+
+ /* We always put data right after the header. */
+ pkt->data_offset = cpu_to_be32(sizeof *pkt);
+ pkt->next_offset = 0;
+
+ pkt->byte_enable_offset = cpu_to_be32(sizeof *pkt + hsize);
+ pkt->byte_enable_len = cpu_to_be32(in->byte_enable_len);
+ hsize += in->byte_enable_len;
+
+ rp_encode_hdr(&pkt->hdr, in->cmd, in->id, in->dev,
+ sizeof *pkt - sizeof pkt->hdr + hsize, in->flags);
+ rp_encode_busaccess_common(pkt_v4_0, in->clk, in->master_id, in->addr,
+ in->attr | RP_BUS_ATTR_EXT_BASE,
+ in->size, in->width, in->stream_width);
+
+ return sizeof *pkt + ret_size;
+}
+
void rp_process_caps(struct rp_peer_state *peer,
void *caps, size_t caps_len)
{
@@ -172,6 +323,7 @@ void rp_process_caps(struct rp_peer_state *peer,
}
}
+
void rp_dpkt_alloc(RemotePortDynPkt *dpkt, size_t size)
{
if (dpkt->size < size) {
diff --git a/include/hw/core/remote-port-proto.h b/include/hw/core/remote-port-proto.h
index ca1ab2f1d3..f65f82195d 100644
--- a/include/hw/core/remote-port-proto.h
+++ b/include/hw/core/remote-port-proto.h
@@ -350,6 +350,134 @@ rp_encode_hello(uint32_t id, uint32_t dev, struct rp_pkt_hello *pkt,
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);
+
void rp_process_caps(struct rp_peer_state *peer,
void *caps, size_t caps_len);
@@ -382,4 +510,9 @@ 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
--
2.43.0
^ permalink raw reply related [flat|nested] 41+ messages in thread* [PATCH 05/29] hw/core: Implement Remote Port irq, sync and ATS helpers
2026-02-05 19:57 [PATCH 00/29] hw/core: Introduce Remote Port Co-simulation Protocol Ruslan Ruslichenko
` (3 preceding siblings ...)
2026-02-05 19:57 ` [PATCH 04/29] hw/core: Implement Remote Port bus access helpers Ruslan Ruslichenko
@ 2026-02-05 19:58 ` Ruslan Ruslichenko
2026-02-05 19:58 ` [PATCH 06/29] system/vl: Introduce -machine-path command line option Ruslan Ruslichenko
` (23 subsequent siblings)
28 siblings, 0 replies; 41+ messages in thread
From: Ruslan Ruslichenko @ 2026-02-05 19:58 UTC (permalink / raw)
To: qemu-devel
Cc: qemu-arm, peter.maydell, artem_mygaiev, volodymyr_babchuk,
takahiro.nakata.wr, Edgar E . Iglesias, francisco.iglesias,
Ruslan_Ruslichenko, Edgar E . Iglesias
From: Ruslan Ruslichenko <Ruslan_Ruslichenko@epam.com>
The patch add support for encoding and decoding of following
commands:
- RP_CMD_interrupt: propagate gpio and interrupt lines changes
to external peers
- RP_CMD_sync: support for synchronization between QEMU and the
remote model
- RP_CMD_ats_*: handling iommu transactoin request and invalidation
accross the link.
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 | 100 ++++++++++++++++++++++++++++
include/hw/core/remote-port-proto.h | 29 ++++++++
2 files changed, 129 insertions(+)
diff --git a/hw/core/remote-port-proto.c b/hw/core/remote-port-proto.c
index 33a6769087..65551ad281 100644
--- a/hw/core/remote-port-proto.c
+++ b/hw/core/remote-port-proto.c
@@ -131,6 +131,24 @@ int rp_decode_payload(struct rp_pkt *pkt)
}
pkt->busaccess.master_id = master_id;
break;
+ case RP_CMD_interrupt:
+ pkt->interrupt.timestamp = be64_to_cpu(pkt->interrupt.timestamp);
+ pkt->interrupt.vector = be64_to_cpu(pkt->interrupt.vector);
+ pkt->interrupt.line = be32_to_cpu(pkt->interrupt.line);
+ pkt->interrupt.val = pkt->interrupt.val;
+ used += pkt->hdr.len;
+ break;
+ case RP_CMD_sync:
+ pkt->sync.timestamp = be64_to_cpu(pkt->interrupt.timestamp);
+ used += pkt->hdr.len;
+ break;
+ case RP_CMD_ats_req:
+ case RP_CMD_ats_inv:
+ pkt->ats.attributes = be64_to_cpu(pkt->ats.attributes);
+ pkt->ats.addr = be64_to_cpu(pkt->ats.addr);
+ pkt->ats.len = be64_to_cpu(pkt->ats.len);
+ pkt->ats.result = be32_to_cpu(pkt->ats.result);
+ break;
default:
break;
}
@@ -294,6 +312,88 @@ size_t rp_encode_busaccess(struct rp_peer_state *peer,
return sizeof *pkt + ret_size;
}
+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)
+{
+ rp_encode_hdr(&pkt->hdr, RP_CMD_interrupt, id, dev,
+ sizeof *pkt - sizeof pkt->hdr, flags);
+ pkt->timestamp = cpu_to_be64(clk);
+ pkt->vector = cpu_to_be64(vector);
+ pkt->line = cpu_to_be32(line);
+ pkt->val = val;
+ return sizeof *pkt;
+}
+
+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)
+{
+ return rp_encode_interrupt_f(id, dev, pkt, clk, line, vector, val, 0);
+}
+
+static size_t rp_encode_ats_common(uint32_t cmd, uint32_t id, uint32_t dev,
+ struct rp_pkt_ats *pkt,
+ int64_t clk, uint64_t attr, uint64_t addr,
+ uint64_t len, uint64_t result, uint32_t flags)
+{
+ rp_encode_hdr(&pkt->hdr, cmd, id, dev,
+ sizeof *pkt - sizeof pkt->hdr, flags);
+ pkt->timestamp = cpu_to_be64(clk);
+ pkt->attributes = cpu_to_be64(attr);
+ pkt->addr = cpu_to_be64(addr);
+ pkt->len = cpu_to_be64(len);
+ pkt->result = cpu_to_be32(result);
+ return sizeof *pkt;
+}
+
+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 len, uint64_t result, uint32_t flags)
+{
+ return rp_encode_ats_common(RP_CMD_ats_req, id, dev,
+ pkt, clk, attr,
+ addr, len, result, 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 len, uint64_t result, uint32_t flags)
+{
+ return rp_encode_ats_common(RP_CMD_ats_inv, id, dev,
+ pkt, clk, attr,
+ addr, len, result, flags);
+}
+
+static size_t rp_encode_sync_common(uint32_t id, uint32_t dev,
+ struct rp_pkt_sync *pkt,
+ int64_t clk, uint32_t flags)
+{
+ rp_encode_hdr(&pkt->hdr, RP_CMD_sync, id, dev,
+ sizeof *pkt - sizeof pkt->hdr, flags);
+ pkt->timestamp = cpu_to_be64(clk);
+ return sizeof *pkt;
+}
+
+size_t rp_encode_sync(uint32_t id, uint32_t dev,
+ struct rp_pkt_sync *pkt,
+ int64_t clk)
+{
+ return rp_encode_sync_common(id, dev, pkt, clk, 0);
+}
+
+size_t rp_encode_sync_resp(uint32_t id, uint32_t dev,
+ struct rp_pkt_sync *pkt,
+ int64_t clk)
+{
+ return rp_encode_sync_common(id, dev, pkt, clk, RP_PKT_FLAGS_response);
+}
+
void rp_process_caps(struct rp_peer_state *peer,
void *caps, size_t caps_len)
{
diff --git a/include/hw/core/remote-port-proto.h b/include/hw/core/remote-port-proto.h
index f65f82195d..7322e0e357 100644
--- a/include/hw/core/remote-port-proto.h
+++ b/include/hw/core/remote-port-proto.h
@@ -478,6 +478,35 @@ 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);
--
2.43.0
^ permalink raw reply related [flat|nested] 41+ messages in thread* [PATCH 06/29] system/vl: Introduce -machine-path command line option
2026-02-05 19:57 [PATCH 00/29] hw/core: Introduce Remote Port Co-simulation Protocol Ruslan Ruslichenko
` (4 preceding siblings ...)
2026-02-05 19:58 ` [PATCH 05/29] hw/core: Implement Remote Port irq, sync and ATS helpers Ruslan Ruslichenko
@ 2026-02-05 19:58 ` Ruslan Ruslichenko
2026-02-05 20:17 ` Daniel P. Berrangé
2026-02-05 19:58 ` [PATCH 07/29] hw/core: Add Remote Port object skeleton Ruslan Ruslichenko
` (22 subsequent siblings)
28 siblings, 1 reply; 41+ messages in thread
From: Ruslan Ruslichenko @ 2026-02-05 19:58 UTC (permalink / raw)
To: qemu-devel
Cc: qemu-arm, peter.maydell, artem_mygaiev, volodymyr_babchuk,
takahiro.nakata.wr, Edgar E . Iglesias, francisco.iglesias,
Ruslan_Ruslichenko, Paolo Bonzini, Edgar E . Iglesias
From: Ruslan Ruslichenko <Ruslan_Ruslichenko@epam.com>
This patch adds a new command line option `-machine-path` to specify
a directory path for shared machine resources.
This path is intended to serve as a base directory for devices or
subsystems that need to automatically create coordination files,
such as Unix domain sockets, particularly in multi-process simulation
environments.
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>
---
include/system/system.h | 2 ++
qemu-options.hx | 10 ++++++++++
system/vl.c | 4 ++++
3 files changed, 16 insertions(+)
diff --git a/include/system/system.h b/include/system/system.h
index 03a2d0e900..0cd012004d 100644
--- a/include/system/system.h
+++ b/include/system/system.h
@@ -57,6 +57,8 @@ bool is_mlock_on_fault(MlockState);
extern MlockState mlock_state;
+extern const char *machine_path;
+
#define MAX_OPTION_ROMS 16
typedef struct QEMUOptionRom {
const char *name;
diff --git a/qemu-options.hx b/qemu-options.hx
index 6dd3e04e38..b7dd2a64f0 100644
--- a/qemu-options.hx
+++ b/qemu-options.hx
@@ -765,6 +765,16 @@ SRST
Preallocate memory when using -mem-path.
ERST
+DEF("machine-path", HAS_ARG, QEMU_OPTION_machine_path,
+ "-machine-path DIR A directory in which to create machine nodes\n",
+ QEMU_ARCH_ALL)
+SRST
+``-machine-path path``
+ Selects the machine path.
+ Multi-arch machine nodes will be created in <path>.
+ This option also sets -mem-shared-path to the given path.
+ERST
+
DEF("k", HAS_ARG, QEMU_OPTION_k,
"-k language use keyboard layout (for example 'fr' for French)\n",
QEMU_ARCH_ALL)
diff --git a/system/vl.c b/system/vl.c
index c890da586c..4750b1cf69 100644
--- a/system/vl.c
+++ b/system/vl.c
@@ -182,6 +182,7 @@ static QemuPluginList plugin_list = QTAILQ_HEAD_INITIALIZER(plugin_list);
static BlockdevOptionsQueue bdo_queue = QSIMPLEQ_HEAD_INITIALIZER(bdo_queue);
static bool nographic = false;
static int mem_prealloc; /* force preallocation of physical target memory */
+const char *machine_path;
static const char *vga_model = NULL;
static DisplayOptions dpy;
static int num_serial_hds;
@@ -3128,6 +3129,9 @@ void qemu_init(int argc, char **argv)
case QEMU_OPTION_mem_prealloc:
mem_prealloc = 1;
break;
+ case QEMU_OPTION_machine_path:
+ machine_path = optarg;
+ break;
case QEMU_OPTION_d:
log_mask = optarg;
break;
--
2.43.0
^ permalink raw reply related [flat|nested] 41+ messages in thread* Re: [PATCH 06/29] system/vl: Introduce -machine-path command line option
2026-02-05 19:58 ` [PATCH 06/29] system/vl: Introduce -machine-path command line option Ruslan Ruslichenko
@ 2026-02-05 20:17 ` Daniel P. Berrangé
0 siblings, 0 replies; 41+ messages in thread
From: Daniel P. Berrangé @ 2026-02-05 20:17 UTC (permalink / raw)
To: Ruslan Ruslichenko
Cc: qemu-devel, qemu-arm, peter.maydell, artem_mygaiev,
volodymyr_babchuk, takahiro.nakata.wr, Edgar E . Iglesias,
francisco.iglesias, Ruslan_Ruslichenko, Paolo Bonzini,
Edgar E . Iglesias
On Thu, Feb 05, 2026 at 08:58:01PM +0100, Ruslan Ruslichenko wrote:
> From: Ruslan Ruslichenko <Ruslan_Ruslichenko@epam.com>
>
> This patch adds a new command line option `-machine-path` to specify
> a directory path for shared machine resources.
We have a fairly general principal that we don't want to introduce
new top level command line arguments to QEMU anymore.
Could this perhaps be modelled as a machine type property, settable
via -machine instead ?
>
> This path is intended to serve as a base directory for devices or
> subsystems that need to automatically create coordination files,
> such as Unix domain sockets, particularly in multi-process simulation
> environments.
>
> 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>
> ---
> include/system/system.h | 2 ++
> qemu-options.hx | 10 ++++++++++
> system/vl.c | 4 ++++
> 3 files changed, 16 insertions(+)
>
> diff --git a/include/system/system.h b/include/system/system.h
> index 03a2d0e900..0cd012004d 100644
> --- a/include/system/system.h
> +++ b/include/system/system.h
> @@ -57,6 +57,8 @@ bool is_mlock_on_fault(MlockState);
>
> extern MlockState mlock_state;
>
> +extern const char *machine_path;
> +
> #define MAX_OPTION_ROMS 16
> typedef struct QEMUOptionRom {
> const char *name;
> diff --git a/qemu-options.hx b/qemu-options.hx
> index 6dd3e04e38..b7dd2a64f0 100644
> --- a/qemu-options.hx
> +++ b/qemu-options.hx
> @@ -765,6 +765,16 @@ SRST
> Preallocate memory when using -mem-path.
> ERST
>
> +DEF("machine-path", HAS_ARG, QEMU_OPTION_machine_path,
> + "-machine-path DIR A directory in which to create machine nodes\n",
> + QEMU_ARCH_ALL)
> +SRST
> +``-machine-path path``
> + Selects the machine path.
> + Multi-arch machine nodes will be created in <path>.
> + This option also sets -mem-shared-path to the given path.
> +ERST
> +
> DEF("k", HAS_ARG, QEMU_OPTION_k,
> "-k language use keyboard layout (for example 'fr' for French)\n",
> QEMU_ARCH_ALL)
> diff --git a/system/vl.c b/system/vl.c
> index c890da586c..4750b1cf69 100644
> --- a/system/vl.c
> +++ b/system/vl.c
> @@ -182,6 +182,7 @@ static QemuPluginList plugin_list = QTAILQ_HEAD_INITIALIZER(plugin_list);
> static BlockdevOptionsQueue bdo_queue = QSIMPLEQ_HEAD_INITIALIZER(bdo_queue);
> static bool nographic = false;
> static int mem_prealloc; /* force preallocation of physical target memory */
> +const char *machine_path;
> static const char *vga_model = NULL;
> static DisplayOptions dpy;
> static int num_serial_hds;
> @@ -3128,6 +3129,9 @@ void qemu_init(int argc, char **argv)
> case QEMU_OPTION_mem_prealloc:
> mem_prealloc = 1;
> break;
> + case QEMU_OPTION_machine_path:
> + machine_path = optarg;
> + break;
> case QEMU_OPTION_d:
> log_mask = optarg;
> break;
> --
> 2.43.0
>
>
With regards,
Daniel
--
|: https://berrange.com -o- https://www.flickr.com/photos/dberrange :|
|: https://libvirt.org -o- https://fstop138.berrange.com :|
|: https://entangle-photo.org -o- https://www.instagram.com/dberrange :|
^ permalink raw reply [flat|nested] 41+ messages in thread
* [PATCH 07/29] hw/core: Add Remote Port object skeleton
2026-02-05 19:57 [PATCH 00/29] hw/core: Introduce Remote Port Co-simulation Protocol Ruslan Ruslichenko
` (5 preceding siblings ...)
2026-02-05 19:58 ` [PATCH 06/29] system/vl: Introduce -machine-path command line option Ruslan Ruslichenko
@ 2026-02-05 19:58 ` Ruslan Ruslichenko
2026-02-05 19:58 ` [PATCH 08/29] hw/core: Setup Remote Port I/O channels Ruslan Ruslichenko
` (21 subsequent siblings)
28 siblings, 0 replies; 41+ messages in thread
From: Ruslan Ruslichenko @ 2026-02-05 19:58 UTC (permalink / raw)
To: qemu-devel
Cc: qemu-arm, peter.maydell, artem_mygaiev, volodymyr_babchuk,
takahiro.nakata.wr, Edgar E . Iglesias, francisco.iglesias,
Ruslan_Ruslichenko, Edgar E . Iglesias
From: Ruslan Ruslichenko <Ruslan_Ruslichenko@epam.com>
Introduce the QOM skeleton for the Remote Port subsystem.
This patch adds main types required for co-simulation:
- remote-port: An object that manages the lifecycle of the
co-simulation session and will handle the character device
backend in future patches.
- remote-port-device: An interface that allows other QEMU
devices to be connected to the Remote Port.
This infrastructure enables QEMU to be linked with external
simulators (e.g. SystemC).
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.c | 140 ++++++++++++++++++++++++++++++++++
include/hw/core/remote-port.h | 67 ++++++++++++++++
2 files changed, 207 insertions(+)
create mode 100644 hw/core/remote-port.c
create mode 100644 include/hw/core/remote-port.h
diff --git a/hw/core/remote-port.c b/hw/core/remote-port.c
new file mode 100644
index 0000000000..c909a825f3
--- /dev/null
+++ b/hw/core/remote-port.c
@@ -0,0 +1,140 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * QEMU remote attach
+ *
+ * Copyright (c) 2013 Xilinx Inc
+ * Written by Edgar E. Iglesias <edgar.iglesias@xilinx.com>
+ *
+ * This code is licensed under the GNU GPL.
+ */
+
+#include "qemu/osdep.h"
+#include "system/system.h"
+#include "chardev/char.h"
+#include "system/cpus.h"
+#include "system/cpu-timers.h"
+#include "system/reset.h"
+#include "hw/core/sysbus.h"
+#include "hw/core/hw-error.h"
+#include "qemu/sockets.h"
+#include "qemu/thread.h"
+#include "qemu/log.h"
+#include "qapi/error.h"
+#include "qemu/error-report.h"
+#include "migration/vmstate.h"
+#include "hw/core/qdev-properties.h"
+#include "hw/core/qdev-properties-system.h"
+#include "qemu/cutils.h"
+
+#ifndef _WIN32
+#include <sys/mman.h>
+#endif
+
+#include "hw/core/remote-port-proto.h"
+#include "hw/core/remote-port.h"
+
+#define D(x)
+#define SYNCD(x)
+
+#ifndef REMOTE_PORT_ERR_DEBUG
+#define REMOTE_PORT_DEBUG_LEVEL 0
+#else
+#define REMOTE_PORT_DEBUG_LEVEL 1
+#endif
+
+#define DB_PRINT_L(level, ...) do { \
+ if (REMOTE_PORT_DEBUG_LEVEL > level) { \
+ fprintf(stderr, ": %s: ", __func__); \
+ fprintf(stderr, ## __VA_ARGS__); \
+ } \
+} while (0)
+
+#define REMOTE_PORT_CLASS(klass) \
+ OBJECT_CLASS_CHECK(RemotePortClass, (klass), TYPE_REMOTE_PORT)
+
+
+static void rp_reset(DeviceState *dev)
+{
+ RemotePort *s = REMOTE_PORT(dev);
+
+ if (s->reset_done) {
+ return;
+ }
+
+ s->reset_done = true;
+}
+
+static void rp_realize(DeviceState *dev, Error **errp)
+{
+}
+
+static void rp_unrealize(DeviceState *dev)
+{
+ RemotePort *s = REMOTE_PORT(dev);
+
+ s->finalizing = true;
+}
+
+static const VMStateDescription vmstate_rp = {
+ .name = TYPE_REMOTE_PORT,
+ .version_id = 1,
+ .minimum_version_id = 1,
+ .fields = (VMStateField[]) {
+ VMSTATE_END_OF_LIST(),
+ }
+};
+
+static void rp_prop_allow_set_link(const Object *obj, const char *name,
+ Object *val, Error **errp)
+{
+}
+
+static void rp_init(Object *obj)
+{
+ RemotePort *s = REMOTE_PORT(obj);
+ int i;
+
+ for (i = 0; i < REMOTE_PORT_MAX_DEVS; ++i) {
+ char *name = g_strdup_printf("remote-port-dev%d", i);
+ object_property_add_link(obj, name, TYPE_REMOTE_PORT_DEVICE,
+ (Object **)&s->devs[i],
+ rp_prop_allow_set_link,
+ OBJ_PROP_LINK_STRONG);
+ g_free(name);
+ }
+}
+
+static void rp_class_init(ObjectClass *klass, const void *data)
+{
+ DeviceClass *dc = DEVICE_CLASS(klass);
+
+ dc->legacy_reset = rp_reset;
+ dc->realize = rp_realize;
+ dc->unrealize = rp_unrealize;
+ dc->vmsd = &vmstate_rp;
+}
+
+static const TypeInfo rp_info = {
+ .name = TYPE_REMOTE_PORT,
+ .parent = TYPE_DEVICE,
+ .instance_size = sizeof(RemotePort),
+ .instance_init = rp_init,
+ .class_init = rp_class_init,
+ .interfaces = (InterfaceInfo[]) {
+ { },
+ },
+};
+
+static const TypeInfo rp_device_info = {
+ .name = TYPE_REMOTE_PORT_DEVICE,
+ .parent = TYPE_INTERFACE,
+ .class_size = sizeof(RemotePortDeviceClass),
+};
+
+static void rp_register_types(void)
+{
+ type_register_static(&rp_info);
+ type_register_static(&rp_device_info);
+}
+
+type_init(rp_register_types)
diff --git a/include/hw/core/remote-port.h b/include/hw/core/remote-port.h
new file mode 100644
index 0000000000..db71071c8e
--- /dev/null
+++ b/include/hw/core/remote-port.h
@@ -0,0 +1,67 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * QEMU remote port.
+ *
+ * Copyright (c) 2013 Xilinx Inc
+ * Written by Edgar E. Iglesias <edgar.iglesias@xilinx.com>
+ *
+ * This code is licensed under the GNU GPL.
+ */
+#ifndef REMOTE_PORT_H__
+#define REMOTE_PORT_H__
+
+#include <stdbool.h>
+#include "hw/core/remote-port-proto.h"
+#include "chardev/char.h"
+#include "chardev/char-fe.h"
+#include "qobject/qdict.h"
+
+#define TYPE_REMOTE_PORT_DEVICE "remote-port-device"
+
+#define REMOTE_PORT_DEVICE_CLASS(klass) \
+ OBJECT_CLASS_CHECK(RemotePortDeviceClass, (klass), TYPE_REMOTE_PORT_DEVICE)
+#define REMOTE_PORT_DEVICE_GET_CLASS(obj) \
+ OBJECT_GET_CLASS(RemotePortDeviceClass, (obj), TYPE_REMOTE_PORT_DEVICE)
+#define REMOTE_PORT_DEVICE(obj) \
+ INTERFACE_CHECK(RemotePortDevice, (obj), TYPE_REMOTE_PORT_DEVICE)
+
+typedef struct RemotePort RemotePort;
+
+typedef struct RemotePortDevice {
+ /*< private >*/
+ Object parent_obj;
+} RemotePortDevice;
+
+typedef struct RemotePortDeviceClass {
+ /*< private >*/
+ InterfaceClass parent_class;
+
+ /*< public >*/
+ /**
+ * ops - operations to perform when remote port packets are recieved for
+ * this device. Function N will be called for a remote port packet with
+ * cmd == N in the header.
+ *
+ * @obj - Remote port device to recieve packet
+ * @pkt - remote port packets
+ */
+
+ void (*ops[RP_CMD_max + 1])(RemotePortDevice *obj, struct rp_pkt *pkt);
+
+} RemotePortDeviceClass;
+
+#define TYPE_REMOTE_PORT "remote-port"
+#define REMOTE_PORT(obj) OBJECT_CHECK(RemotePort, (obj), TYPE_REMOTE_PORT)
+
+struct RemotePort {
+ DeviceState parent;
+
+ bool finalizing;
+
+ bool reset_done;
+
+#define REMOTE_PORT_MAX_DEVS 1024
+ RemotePortDevice *devs[REMOTE_PORT_MAX_DEVS];
+};
+
+#endif
--
2.43.0
^ permalink raw reply related [flat|nested] 41+ messages in thread* [PATCH 08/29] hw/core: Setup Remote Port I/O channels
2026-02-05 19:57 [PATCH 00/29] hw/core: Introduce Remote Port Co-simulation Protocol Ruslan Ruslichenko
` (6 preceding siblings ...)
2026-02-05 19:58 ` [PATCH 07/29] hw/core: Add Remote Port object skeleton Ruslan Ruslichenko
@ 2026-02-05 19:58 ` Ruslan Ruslichenko
2026-02-05 20:28 ` Daniel P. Berrangé
2026-02-05 19:58 ` [PATCH 09/29] hw/core: Add Remote Port protocol thread and handshake Ruslan Ruslichenko
` (20 subsequent siblings)
28 siblings, 1 reply; 41+ messages in thread
From: Ruslan Ruslichenko @ 2026-02-05 19:58 UTC (permalink / raw)
To: qemu-devel
Cc: qemu-arm, peter.maydell, artem_mygaiev, volodymyr_babchuk,
takahiro.nakata.wr, Edgar E . Iglesias, francisco.iglesias,
Ruslan_Ruslichenko, Edgar E . Iglesias
From: Ruslan Ruslichenko <Ruslan_Ruslichenko@epam.com>
Add initialization of communication channels with
remote peer.
This includes character device backend, which can be
configured based on QOM properties or automatic socket
creation based on the machine path. The patch also
initializes the signaling event pipes.
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.c | 187 ++++++++++++++++++++++++++++++++++
include/hw/core/remote-port.h | 16 +++
2 files changed, 203 insertions(+)
diff --git a/hw/core/remote-port.c b/hw/core/remote-port.c
index c909a825f3..5154c1bc2a 100644
--- a/hw/core/remote-port.c
+++ b/hw/core/remote-port.c
@@ -52,6 +52,58 @@
#define REMOTE_PORT_CLASS(klass) \
OBJECT_CLASS_CHECK(RemotePortClass, (klass), TYPE_REMOTE_PORT)
+static char *rp_sanitize_prefix(RemotePort *s)
+{
+ char *sanitized_name;
+ char *c;
+
+ sanitized_name = g_strdup(s->prefix);
+ for (c = sanitized_name; *c != '\0'; c++) {
+ if (*c == '/') {
+ *c = '_';
+ }
+ }
+ return sanitized_name;
+}
+
+static char *rp_autocreate_chardesc(RemotePort *s, bool server)
+{
+ char *prefix;
+ char *chardesc;
+ int r;
+
+ prefix = rp_sanitize_prefix(s);
+ r = asprintf(&chardesc, "unix:%s/qemu-rport-%s%s",
+ machine_path, prefix, server ? ",wait,server" : "");
+ assert(r > 0);
+ free(prefix);
+ return chardesc;
+}
+
+static Chardev *rp_autocreate_chardev(RemotePort *s, char *name)
+{
+ Chardev *chr = NULL;
+ char *chardesc;
+ char *s_path;
+ int r;
+
+ r = asprintf(&s_path, "%s/qemu-rport-%s", machine_path,
+ rp_sanitize_prefix(s));
+ assert(r > 0);
+ if (g_file_test(s_path, G_FILE_TEST_EXISTS)) {
+ chardesc = rp_autocreate_chardesc(s, false);
+ chr = qemu_chr_new_noreplay(name, chardesc, false, NULL);
+ free(chardesc);
+ }
+ free(s_path);
+
+ if (!chr) {
+ chardesc = rp_autocreate_chardesc(s, true);
+ chr = qemu_chr_new_noreplay(name, chardesc, false, NULL);
+ free(chardesc);
+ }
+ return chr;
+}
static void rp_reset(DeviceState *dev)
{
@@ -66,6 +118,127 @@ static void rp_reset(DeviceState *dev)
static void rp_realize(DeviceState *dev, Error **errp)
{
+ RemotePort *s = REMOTE_PORT(dev);
+ int r;
+ Error *err = NULL;
+
+ s->prefix = object_get_canonical_path(OBJECT(dev));
+
+ if (!qemu_chr_fe_get_driver(&s->chr)) {
+ char *name;
+ Chardev *chr = NULL;
+ static int nr;
+
+ r = asprintf(&name, "rport%d", nr);
+ nr++;
+ assert(r > 0);
+
+ if (s->chrdev_id) {
+ chr = qemu_chr_find(s->chrdev_id);
+ }
+
+ if (chr) {
+ /* Found the chardev via commandline */
+ } else if (s->chardesc) {
+ chr = qemu_chr_new(name, s->chardesc, NULL);
+ } else {
+ if (!machine_path) {
+ error_report("%s: Missing chardesc prop."
+ " Forgot -machine-path?",
+ s->prefix);
+ exit(EXIT_FAILURE);
+ }
+ chr = rp_autocreate_chardev(s, name);
+ }
+
+ free(name);
+ if (!chr) {
+ error_report("%s: Unable to create remort-port channel %s",
+ s->prefix, s->chardesc);
+ exit(EXIT_FAILURE);
+ }
+
+ qdev_prop_set_chr(dev, "chardev", chr);
+ s->chrdev = chr;
+ }
+
+#ifdef _WIN32
+ /*
+ * Create a socket connection between two sockets. We auto-bind
+ * and read out the port selected by the kernel.
+ */
+ {
+ char *name;
+ SocketAddress *sock;
+ int port;
+ int listen_sk;
+
+ sock = socket_parse("127.0.0.1:0", &error_abort);
+ listen_sk = socket_listen(sock, 1, &error_abort);
+
+ if (s->event.pipe.read < 0) {
+ perror("socket read");
+ exit(EXIT_FAILURE);
+ }
+
+ {
+ struct sockaddr_in saddr;
+ socklen_t slen = sizeof saddr;
+ int r;
+
+ r = getsockname(listen_sk, (struct sockaddr *) &saddr, &slen);
+ if (r < 0) {
+ perror("getsockname");
+ exit(EXIT_FAILURE);
+ }
+ port = htons(saddr.sin_port);
+ }
+
+ name = g_strdup_printf("127.0.0.1:%d", port);
+ s->event.pipe.write = inet_connect(name, &error_abort);
+ g_free(name);
+ if (s->event.pipe.write < 0) {
+ perror("socket write");
+ exit(EXIT_FAILURE);
+ }
+
+ for (;;) {
+ struct sockaddr_in saddr;
+ socklen_t slen = sizeof saddr;
+ int fd;
+
+ slen = sizeof(saddr);
+ fd = qemu_accept(listen_sk, (struct sockaddr *)&saddr, &slen);
+ if (fd < 0 && errno != EINTR) {
+ close(listen_sk);
+ return;
+ } else if (fd >= 0) {
+ close(listen_sk);
+ s->event.pipe.read = fd;
+ break;
+ }
+ }
+
+ if (!qemu_set_blocking(s->event.pipe.read, false, &err)) {
+ error_report("%s: Unable to set non-block for internal pipes",
+ s->prefix);
+ exit(EXIT_FAILURE);
+ }
+ }
+#else
+ if (!g_unix_open_pipe(s->event.pipes, FD_CLOEXEC, NULL)) {
+ error_report("%s: Unable to create remort-port internal pipes",
+ s->prefix);
+ exit(EXIT_FAILURE);
+ }
+
+ if (!qemu_set_blocking(s->event.pipe.read, false, &err)) {
+ error_report("%s: Unable to set non-block for internal pipes",
+ s->prefix);
+ exit(EXIT_FAILURE);
+ }
+
+#endif
}
static void rp_unrealize(DeviceState *dev)
@@ -73,6 +246,13 @@ static void rp_unrealize(DeviceState *dev)
RemotePort *s = REMOTE_PORT(dev);
s->finalizing = true;
+
+ info_report("%s: Wait for remote-port to disconnect", s->prefix);
+ qemu_chr_fe_disconnect(&s->chr);
+
+ close(s->event.pipe.read);
+ close(s->event.pipe.write);
+ object_unparent(OBJECT(s->chrdev));
}
static const VMStateDescription vmstate_rp = {
@@ -84,6 +264,12 @@ static const VMStateDescription vmstate_rp = {
}
};
+static Property rp_properties[] = {
+ DEFINE_PROP_CHR("chardev", RemotePort, chr),
+ DEFINE_PROP_STRING("chardesc", RemotePort, chardesc),
+ DEFINE_PROP_STRING("chrdev-id", RemotePort, chrdev_id),
+};
+
static void rp_prop_allow_set_link(const Object *obj, const char *name,
Object *val, Error **errp)
{
@@ -112,6 +298,7 @@ static void rp_class_init(ObjectClass *klass, const void *data)
dc->realize = rp_realize;
dc->unrealize = rp_unrealize;
dc->vmsd = &vmstate_rp;
+ device_class_set_props_n(dc, rp_properties, ARRAY_SIZE(rp_properties));
}
static const TypeInfo rp_info = {
diff --git a/include/hw/core/remote-port.h b/include/hw/core/remote-port.h
index db71071c8e..0f40018cdb 100644
--- a/include/hw/core/remote-port.h
+++ b/include/hw/core/remote-port.h
@@ -56,8 +56,24 @@ typedef struct RemotePortDeviceClass {
struct RemotePort {
DeviceState parent;
+ union {
+ int pipes[2];
+ struct {
+ int read;
+ int write;
+ } pipe;
+ } event;
+ Chardev *chrdev;
+ CharFrontend chr;
bool finalizing;
+ char *chardesc;
+ char *chrdev_id;
+
+ const char *prefix;
+ const char *remote_prefix;
+
+ uint32_t current_id;
bool reset_done;
#define REMOTE_PORT_MAX_DEVS 1024
--
2.43.0
^ permalink raw reply related [flat|nested] 41+ messages in thread* Re: [PATCH 08/29] hw/core: Setup Remote Port I/O channels
2026-02-05 19:58 ` [PATCH 08/29] hw/core: Setup Remote Port I/O channels Ruslan Ruslichenko
@ 2026-02-05 20:28 ` Daniel P. Berrangé
2026-02-06 17:33 ` Ruslan Ruslichenko
0 siblings, 1 reply; 41+ messages in thread
From: Daniel P. Berrangé @ 2026-02-05 20:28 UTC (permalink / raw)
To: Ruslan Ruslichenko
Cc: qemu-devel, qemu-arm, peter.maydell, artem_mygaiev,
volodymyr_babchuk, takahiro.nakata.wr, Edgar E . Iglesias,
francisco.iglesias, Ruslan_Ruslichenko, Edgar E . Iglesias
On Thu, Feb 05, 2026 at 08:58:03PM +0100, Ruslan Ruslichenko wrote:
> From: Ruslan Ruslichenko <Ruslan_Ruslichenko@epam.com>
>
> Add initialization of communication channels with
> remote peer.
>
> This includes character device backend, which can be
> configured based on QOM properties or automatic socket
> creation based on the machine path. The patch also
> initializes the signaling event pipes.
>
> 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.c | 187 ++++++++++++++++++++++++++++++++++
> include/hw/core/remote-port.h | 16 +++
> 2 files changed, 203 insertions(+)
>
> diff --git a/hw/core/remote-port.c b/hw/core/remote-port.c
> index c909a825f3..5154c1bc2a 100644
> --- a/hw/core/remote-port.c
> +++ b/hw/core/remote-port.c
> @@ -52,6 +52,58 @@
> #define REMOTE_PORT_CLASS(klass) \
> OBJECT_CLASS_CHECK(RemotePortClass, (klass), TYPE_REMOTE_PORT)
>
> +static char *rp_sanitize_prefix(RemotePort *s)
> +{
> + char *sanitized_name;
> + char *c;
> +
> + sanitized_name = g_strdup(s->prefix);
> + for (c = sanitized_name; *c != '\0'; c++) {
> + if (*c == '/') {
> + *c = '_';
> + }
> + }
> + return sanitized_name;
> +}
> +
> +static char *rp_autocreate_chardesc(RemotePort *s, bool server)
> +{
> + char *prefix;
> + char *chardesc;
> + int r;
> +
> + prefix = rp_sanitize_prefix(s);
> + r = asprintf(&chardesc, "unix:%s/qemu-rport-%s%s",
> + machine_path, prefix, server ? ",wait,server" : "");
> + assert(r > 0);
> + free(prefix);
> + return chardesc;
> +}
> +
> +static Chardev *rp_autocreate_chardev(RemotePort *s, char *name)
> +{
> + Chardev *chr = NULL;
> + char *chardesc;
> + char *s_path;
> + int r;
> +
> + r = asprintf(&s_path, "%s/qemu-rport-%s", machine_path,
> + rp_sanitize_prefix(s));
> + assert(r > 0);
> + if (g_file_test(s_path, G_FILE_TEST_EXISTS)) {
> + chardesc = rp_autocreate_chardesc(s, false);
> + chr = qemu_chr_new_noreplay(name, chardesc, false, NULL);
> + free(chardesc);
> + }
> + free(s_path);
> +
> + if (!chr) {
> + chardesc = rp_autocreate_chardesc(s, true);
> + chr = qemu_chr_new_noreplay(name, chardesc, false, NULL);
> + free(chardesc);
> + }
> + return chr;
> +}
>
> static void rp_reset(DeviceState *dev)
> {
> @@ -66,6 +118,127 @@ static void rp_reset(DeviceState *dev)
>
> static void rp_realize(DeviceState *dev, Error **errp)
> {
> + RemotePort *s = REMOTE_PORT(dev);
> + int r;
> + Error *err = NULL;
> +
> + s->prefix = object_get_canonical_path(OBJECT(dev));
> +
> + if (!qemu_chr_fe_get_driver(&s->chr)) {
> + char *name;
> + Chardev *chr = NULL;
> + static int nr;
> +
> + r = asprintf(&name, "rport%d", nr);
> + nr++;
> + assert(r > 0);
> +
> + if (s->chrdev_id) {
> + chr = qemu_chr_find(s->chrdev_id);
> + }
> +
> + if (chr) {
> + /* Found the chardev via commandline */
> + } else if (s->chardesc) {
> + chr = qemu_chr_new(name, s->chardesc, NULL);
> + } else {
> + if (!machine_path) {
> + error_report("%s: Missing chardesc prop."
> + " Forgot -machine-path?",
> + s->prefix);
> + exit(EXIT_FAILURE);
> + }
> + chr = rp_autocreate_chardev(s, name);
> + }
Having three different ways to configure the chardev rather
feels like overkill. IMHO it should be sufficient to just
accept thue chardev ID as a mandatory argument, and not
attempt to create chardevs from this code. That would
in turn seem to avoid the need for the -machine-path arg
to exist ?
> +#ifdef _WIN32
> + /*
> + * Create a socket connection between two sockets. We auto-bind
> + * and read out the port selected by the kernel.
> + */
> + {
> + char *name;
> + SocketAddress *sock;
> + int port;
> + int listen_sk;
> +
> + sock = socket_parse("127.0.0.1:0", &error_abort);
> + listen_sk = socket_listen(sock, 1, &error_abort);
Please avoid introducing new usage of low level sockets APIs. The higher
level QIOChannelSocket APIs is preferred, that said.....
> +
> + if (s->event.pipe.read < 0) {
> + perror("socket read");
> + exit(EXIT_FAILURE);
> + }
> +
> + {
> + struct sockaddr_in saddr;
> + socklen_t slen = sizeof saddr;
> + int r;
> +
> + r = getsockname(listen_sk, (struct sockaddr *) &saddr, &slen);
> + if (r < 0) {
> + perror("getsockname");
> + exit(EXIT_FAILURE);
> + }
> + port = htons(saddr.sin_port);
> + }
> +
> + name = g_strdup_printf("127.0.0.1:%d", port);
> + s->event.pipe.write = inet_connect(name, &error_abort);
> + g_free(name);
> + if (s->event.pipe.write < 0) {
> + perror("socket write");
> + exit(EXIT_FAILURE);
> + }
> +
> + for (;;) {
> + struct sockaddr_in saddr;
> + socklen_t slen = sizeof saddr;
> + int fd;
> +
> + slen = sizeof(saddr);
> + fd = qemu_accept(listen_sk, (struct sockaddr *)&saddr, &slen);
> + if (fd < 0 && errno != EINTR) {
> + close(listen_sk);
> + return;
> + } else if (fd >= 0) {
> + close(listen_sk);
> + s->event.pipe.read = fd;
> + break;
> + }
> + }
> +
> + if (!qemu_set_blocking(s->event.pipe.read, false, &err)) {
> + error_report("%s: Unable to set non-block for internal pipes",
> + s->prefix);
> + exit(EXIT_FAILURE);
> + }
> + }
> +#else
> + if (!g_unix_open_pipe(s->event.pipes, FD_CLOEXEC, NULL)) {
> + error_report("%s: Unable to create remort-port internal pipes",
> + s->prefix);
> + exit(EXIT_FAILURE);
> + }
> +
> + if (!qemu_set_blocking(s->event.pipe.read, false, &err)) {
> + error_report("%s: Unable to set non-block for internal pipes",
> + s->prefix);
> + exit(EXIT_FAILURE);
> + }
> +
> +#endif
... I can't help thinking we have this code "self event pipe" design
somewhere else in QEMU. Ideally we would have a helper API for this
task and avoid OS conditional code in this device impl.
> }
>
> static void rp_unrealize(DeviceState *dev)
> @@ -73,6 +246,13 @@ static void rp_unrealize(DeviceState *dev)
> RemotePort *s = REMOTE_PORT(dev);
>
> s->finalizing = true;
> +
> + info_report("%s: Wait for remote-port to disconnect", s->prefix);
> + qemu_chr_fe_disconnect(&s->chr);
> +
> + close(s->event.pipe.read);
> + close(s->event.pipe.write);
> + object_unparent(OBJECT(s->chrdev));
> }
>
> static const VMStateDescription vmstate_rp = {
> @@ -84,6 +264,12 @@ static const VMStateDescription vmstate_rp = {
> }
> };
>
> +static Property rp_properties[] = {
> + DEFINE_PROP_CHR("chardev", RemotePort, chr),
> + DEFINE_PROP_STRING("chardesc", RemotePort, chardesc),
> + DEFINE_PROP_STRING("chrdev-id", RemotePort, chrdev_id),
> +};
This really feels lik
> +
> static void rp_prop_allow_set_link(const Object *obj, const char *name,
> Object *val, Error **errp)
> {
> @@ -112,6 +298,7 @@ static void rp_class_init(ObjectClass *klass, const void *data)
> dc->realize = rp_realize;
> dc->unrealize = rp_unrealize;
> dc->vmsd = &vmstate_rp;
> + device_class_set_props_n(dc, rp_properties, ARRAY_SIZE(rp_properties));
> }
>
> static const TypeInfo rp_info = {
> diff --git a/include/hw/core/remote-port.h b/include/hw/core/remote-port.h
> index db71071c8e..0f40018cdb 100644
> --- a/include/hw/core/remote-port.h
> +++ b/include/hw/core/remote-port.h
> @@ -56,8 +56,24 @@ typedef struct RemotePortDeviceClass {
> struct RemotePort {
> DeviceState parent;
>
> + union {
> + int pipes[2];
> + struct {
> + int read;
> + int write;
> + } pipe;
> + } event;
> + Chardev *chrdev;
> + CharFrontend chr;
> bool finalizing;
>
> + char *chardesc;
> + char *chrdev_id;
> +
> + const char *prefix;
> + const char *remote_prefix;
> +
> + uint32_t current_id;
> bool reset_done;
>
> #define REMOTE_PORT_MAX_DEVS 1024
> --
> 2.43.0
>
>
With regards,
Daniel
--
|: https://berrange.com -o- https://www.flickr.com/photos/dberrange :|
|: https://libvirt.org -o- https://fstop138.berrange.com :|
|: https://entangle-photo.org -o- https://www.instagram.com/dberrange :|
^ permalink raw reply [flat|nested] 41+ messages in thread* Re: [PATCH 08/29] hw/core: Setup Remote Port I/O channels
2026-02-05 20:28 ` Daniel P. Berrangé
@ 2026-02-06 17:33 ` Ruslan Ruslichenko
0 siblings, 0 replies; 41+ messages in thread
From: Ruslan Ruslichenko @ 2026-02-06 17:33 UTC (permalink / raw)
To: Daniel P. Berrangé
Cc: qemu-devel, qemu-arm, peter.maydell, artem_mygaiev,
volodymyr_babchuk, takahiro.nakata.wr, Edgar E . Iglesias,
francisco.iglesias, Ruslan_Ruslichenko, Edgar E . Iglesias
Hello Daniel,
Thank you for your review.
On Thu, Feb 5, 2026 at 9:28 PM Daniel P. Berrangé <berrange@redhat.com> wrote:
>
> On Thu, Feb 05, 2026 at 08:58:03PM +0100, Ruslan Ruslichenko wrote:
> > From: Ruslan Ruslichenko <Ruslan_Ruslichenko@epam.com>
> >
> > Add initialization of communication channels with
> > remote peer.
> >
> > This includes character device backend, which can be
> > configured based on QOM properties or automatic socket
> > creation based on the machine path. The patch also
> > initializes the signaling event pipes.
> >
> > 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.c | 187 ++++++++++++++++++++++++++++++++++
> > include/hw/core/remote-port.h | 16 +++
> > 2 files changed, 203 insertions(+)
> >
> > diff --git a/hw/core/remote-port.c b/hw/core/remote-port.c
> > index c909a825f3..5154c1bc2a 100644
> > --- a/hw/core/remote-port.c
> > +++ b/hw/core/remote-port.c
> > @@ -52,6 +52,58 @@
> > #define REMOTE_PORT_CLASS(klass) \
> > OBJECT_CLASS_CHECK(RemotePortClass, (klass), TYPE_REMOTE_PORT)
> >
> > +static char *rp_sanitize_prefix(RemotePort *s)
> > +{
> > + char *sanitized_name;
> > + char *c;
> > +
> > + sanitized_name = g_strdup(s->prefix);
> > + for (c = sanitized_name; *c != '\0'; c++) {
> > + if (*c == '/') {
> > + *c = '_';
> > + }
> > + }
> > + return sanitized_name;
> > +}
> > +
> > +static char *rp_autocreate_chardesc(RemotePort *s, bool server)
> > +{
> > + char *prefix;
> > + char *chardesc;
> > + int r;
> > +
> > + prefix = rp_sanitize_prefix(s);
> > + r = asprintf(&chardesc, "unix:%s/qemu-rport-%s%s",
> > + machine_path, prefix, server ? ",wait,server" : "");
> > + assert(r > 0);
> > + free(prefix);
> > + return chardesc;
> > +}
> > +
> > +static Chardev *rp_autocreate_chardev(RemotePort *s, char *name)
> > +{
> > + Chardev *chr = NULL;
> > + char *chardesc;
> > + char *s_path;
> > + int r;
> > +
> > + r = asprintf(&s_path, "%s/qemu-rport-%s", machine_path,
> > + rp_sanitize_prefix(s));
> > + assert(r > 0);
> > + if (g_file_test(s_path, G_FILE_TEST_EXISTS)) {
> > + chardesc = rp_autocreate_chardesc(s, false);
> > + chr = qemu_chr_new_noreplay(name, chardesc, false, NULL);
> > + free(chardesc);
> > + }
> > + free(s_path);
> > +
> > + if (!chr) {
> > + chardesc = rp_autocreate_chardesc(s, true);
> > + chr = qemu_chr_new_noreplay(name, chardesc, false, NULL);
> > + free(chardesc);
> > + }
> > + return chr;
> > +}
> >
> > static void rp_reset(DeviceState *dev)
> > {
> > @@ -66,6 +118,127 @@ static void rp_reset(DeviceState *dev)
> >
> > static void rp_realize(DeviceState *dev, Error **errp)
> > {
> > + RemotePort *s = REMOTE_PORT(dev);
> > + int r;
> > + Error *err = NULL;
> > +
> > + s->prefix = object_get_canonical_path(OBJECT(dev));
> > +
> > + if (!qemu_chr_fe_get_driver(&s->chr)) {
> > + char *name;
> > + Chardev *chr = NULL;
> > + static int nr;
> > +
> > + r = asprintf(&name, "rport%d", nr);
> > + nr++;
> > + assert(r > 0);
> > +
> > + if (s->chrdev_id) {
> > + chr = qemu_chr_find(s->chrdev_id);
> > + }
> > +
> > + if (chr) {
> > + /* Found the chardev via commandline */
> > + } else if (s->chardesc) {
> > + chr = qemu_chr_new(name, s->chardesc, NULL);
> > + } else {
> > + if (!machine_path) {
> > + error_report("%s: Missing chardesc prop."
> > + " Forgot -machine-path?",
> > + s->prefix);
> > + exit(EXIT_FAILURE);
> > + }
> > + chr = rp_autocreate_chardev(s, name);
> > + }
>
> Having three different ways to configure the chardev rather
> feels like overkill. IMHO it should be sufficient to just
> accept thue chardev ID as a mandatory argument, and not
> attempt to create chardevs from this code. That would
> in turn seem to avoid the need for the -machine-path arg
> to exist ?
>
I think we can consider using only chrdev_id, for now. This loses some
flexibility, but definitely simplifies things a lot.
>
> > +#ifdef _WIN32
> > + /*
> > + * Create a socket connection between two sockets. We auto-bind
> > + * and read out the port selected by the kernel.
> > + */
> > + {
> > + char *name;
> > + SocketAddress *sock;
> > + int port;
> > + int listen_sk;
> > +
> > + sock = socket_parse("127.0.0.1:0", &error_abort);
> > + listen_sk = socket_listen(sock, 1, &error_abort);
>
> Please avoid introducing new usage of low level sockets APIs. The higher
> level QIOChannelSocket APIs is preferred, that said.....
>
> > +
> > + if (s->event.pipe.read < 0) {
> > + perror("socket read");
> > + exit(EXIT_FAILURE);
> > + }
> > +
> > + {
> > + struct sockaddr_in saddr;
> > + socklen_t slen = sizeof saddr;
> > + int r;
> > +
> > + r = getsockname(listen_sk, (struct sockaddr *) &saddr, &slen);
> > + if (r < 0) {
> > + perror("getsockname");
> > + exit(EXIT_FAILURE);
> > + }
> > + port = htons(saddr.sin_port);
> > + }
> > +
> > + name = g_strdup_printf("127.0.0.1:%d", port);
> > + s->event.pipe.write = inet_connect(name, &error_abort);
> > + g_free(name);
> > + if (s->event.pipe.write < 0) {
> > + perror("socket write");
> > + exit(EXIT_FAILURE);
> > + }
> > +
> > + for (;;) {
> > + struct sockaddr_in saddr;
> > + socklen_t slen = sizeof saddr;
> > + int fd;
> > +
> > + slen = sizeof(saddr);
> > + fd = qemu_accept(listen_sk, (struct sockaddr *)&saddr, &slen);
> > + if (fd < 0 && errno != EINTR) {
> > + close(listen_sk);
> > + return;
> > + } else if (fd >= 0) {
> > + close(listen_sk);
> > + s->event.pipe.read = fd;
> > + break;
> > + }
> > + }
> > +
> > + if (!qemu_set_blocking(s->event.pipe.read, false, &err)) {
> > + error_report("%s: Unable to set non-block for internal pipes",
> > + s->prefix);
> > + exit(EXIT_FAILURE);
> > + }
> > + }
> > +#else
> > + if (!g_unix_open_pipe(s->event.pipes, FD_CLOEXEC, NULL)) {
> > + error_report("%s: Unable to create remort-port internal pipes",
> > + s->prefix);
> > + exit(EXIT_FAILURE);
> > + }
> > +
> > + if (!qemu_set_blocking(s->event.pipe.read, false, &err)) {
> > + error_report("%s: Unable to set non-block for internal pipes",
> > + s->prefix);
> > + exit(EXIT_FAILURE);
> > + }
> > +
> > +#endif
>
> ... I can't help thinking we have this code "self event pipe" design
> somewhere else in QEMU. Ideally we would have a helper API for this
> task and avoid OS conditional code in this device impl.
>
>
Thanks! Looks like EventNotifier may work here and it supports both platforms.
I'll try to use it instead then.
> > }
> >
> > static void rp_unrealize(DeviceState *dev)
> > @@ -73,6 +246,13 @@ static void rp_unrealize(DeviceState *dev)
> > RemotePort *s = REMOTE_PORT(dev);
> >
> > s->finalizing = true;
> > +
> > + info_report("%s: Wait for remote-port to disconnect", s->prefix);
> > + qemu_chr_fe_disconnect(&s->chr);
> > +
> > + close(s->event.pipe.read);
> > + close(s->event.pipe.write);
> > + object_unparent(OBJECT(s->chrdev));
> > }
> >
> > static const VMStateDescription vmstate_rp = {
> > @@ -84,6 +264,12 @@ static const VMStateDescription vmstate_rp = {
> > }
> > };
> >
> > +static Property rp_properties[] = {
> > + DEFINE_PROP_CHR("chardev", RemotePort, chr),
> > + DEFINE_PROP_STRING("chardesc", RemotePort, chardesc),
> > + DEFINE_PROP_STRING("chrdev-id", RemotePort, chrdev_id),
> > +};
>
> This really feels lik
>
> > +
> > static void rp_prop_allow_set_link(const Object *obj, const char *name,
> > Object *val, Error **errp)
> > {
> > @@ -112,6 +298,7 @@ static void rp_class_init(ObjectClass *klass, const void *data)
> > dc->realize = rp_realize;
> > dc->unrealize = rp_unrealize;
> > dc->vmsd = &vmstate_rp;
> > + device_class_set_props_n(dc, rp_properties, ARRAY_SIZE(rp_properties));
> > }
> >
> > static const TypeInfo rp_info = {
> > diff --git a/include/hw/core/remote-port.h b/include/hw/core/remote-port.h
> > index db71071c8e..0f40018cdb 100644
> > --- a/include/hw/core/remote-port.h
> > +++ b/include/hw/core/remote-port.h
> > @@ -56,8 +56,24 @@ typedef struct RemotePortDeviceClass {
> > struct RemotePort {
> > DeviceState parent;
> >
> > + union {
> > + int pipes[2];
> > + struct {
> > + int read;
> > + int write;
> > + } pipe;
> > + } event;
> > + Chardev *chrdev;
> > + CharFrontend chr;
> > bool finalizing;
> >
> > + char *chardesc;
> > + char *chrdev_id;
> > +
> > + const char *prefix;
> > + const char *remote_prefix;
> > +
> > + uint32_t current_id;
> > bool reset_done;
> >
> > #define REMOTE_PORT_MAX_DEVS 1024
> > --
> > 2.43.0
> >
> >
>
> With regards,
> Daniel
> --
> |: https://berrange.com -o- https://www.flickr.com/photos/dberrange :|
> |: https://libvirt.org -o- https://fstop138.berrange.com :|
> |: https://entangle-photo.org -o- https://www.instagram.com/dberrange :|
>
--
BR,
Ruslan
^ permalink raw reply [flat|nested] 41+ messages in thread
* [PATCH 09/29] hw/core: Add Remote Port protocol thread and handshake
2026-02-05 19:57 [PATCH 00/29] hw/core: Introduce Remote Port Co-simulation Protocol Ruslan Ruslichenko
` (7 preceding siblings ...)
2026-02-05 19:58 ` [PATCH 08/29] hw/core: Setup Remote Port I/O channels Ruslan Ruslichenko
@ 2026-02-05 19:58 ` Ruslan Ruslichenko
2026-02-05 20:29 ` Daniel P. Berrangé
2026-02-05 19:58 ` [PATCH 10/29] hw/core: Implement Remote Port packet dispatch logic Ruslan Ruslichenko
` (19 subsequent siblings)
28 siblings, 1 reply; 41+ messages in thread
From: Ruslan Ruslichenko @ 2026-02-05 19:58 UTC (permalink / raw)
To: qemu-devel
Cc: qemu-arm, peter.maydell, artem_mygaiev, volodymyr_babchuk,
takahiro.nakata.wr, Edgar E . Iglesias, francisco.iglesias,
Ruslan_Ruslichenko, Edgar E . Iglesias
From: Ruslan Ruslichenko <Ruslan_Ruslichenko@epam.com>
Introduce main execution loop for the Remote Port protocol.
Creates a dedicated thread to manage the communication lifecycle.
This includes logic to read packets and manage RX queue.
Patch also implements handshake logic to verify protocol
versions and negotiate capabilities with remote peer.
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.c | 190 ++++++++++++++++++++++++++++++++++
include/hw/core/remote-port.h | 22 ++++
2 files changed, 212 insertions(+)
diff --git a/hw/core/remote-port.c b/hw/core/remote-port.c
index 5154c1bc2a..91b0682884 100644
--- a/hw/core/remote-port.c
+++ b/hw/core/remote-port.c
@@ -52,6 +52,88 @@
#define REMOTE_PORT_CLASS(klass) \
OBJECT_CLASS_CHECK(RemotePortClass, (klass), TYPE_REMOTE_PORT)
+static void rp_pkt_dump(const char *prefix, const char *buf, size_t len)
+{
+ qemu_hexdump(stdout, prefix, buf, len);
+}
+
+static void rp_fatal_error(RemotePort *s, const char *reason)
+{
+ error_report("%s: %s", s->prefix, reason);
+ exit(EXIT_FAILURE);
+}
+
+static ssize_t rp_recv(RemotePort *s, void *buf, size_t count)
+{
+ ssize_t r;
+
+ r = qemu_chr_fe_read_all(&s->chr, buf, count);
+ if (r <= 0) {
+ return r;
+ }
+ if (r != count) {
+ error_report("%s: Bad read, expected %zd but got %zd",
+ s->prefix, count, r);
+ rp_fatal_error(s, "Bad read");
+ }
+
+ return r;
+}
+
+ssize_t rp_write(RemotePort *s, const void *buf, size_t count)
+{
+ ssize_t r;
+
+ qemu_mutex_lock(&s->write_mutex);
+ r = qemu_chr_fe_write_all(&s->chr, buf, count);
+ qemu_mutex_unlock(&s->write_mutex);
+ assert(r == count);
+ if (r <= 0) {
+ error_report("%s: Disconnected r=%zd buf=%p count=%zd",
+ s->prefix, r, buf, count);
+ rp_fatal_error(s, "Bad write");
+ }
+ return r;
+}
+
+static void rp_cmd_hello(RemotePort *s, struct rp_pkt *pkt)
+{
+ s->peer.version = pkt->hello.version;
+ if (pkt->hello.version.major != RP_VERSION_MAJOR) {
+ error_report("remote-port version missmatch remote=%d.%d local=%d.%d",
+ pkt->hello.version.major, pkt->hello.version.minor,
+ RP_VERSION_MAJOR, RP_VERSION_MINOR);
+ rp_fatal_error(s, "Bad version");
+ }
+
+ if (pkt->hello.caps.len) {
+ void *caps = (char *) pkt + pkt->hello.caps.offset;
+
+ rp_process_caps(&s->peer, caps, pkt->hello.caps.len);
+ }
+}
+
+static void rp_say_hello(RemotePort *s)
+{
+ struct rp_pkt_hello pkt;
+ uint32_t caps[] = {
+ CAP_BUSACCESS_EXT_BASE,
+ CAP_BUSACCESS_EXT_BYTE_EN,
+ CAP_WIRE_POSTED_UPDATES,
+ CAP_ATS,
+ };
+ size_t len;
+
+ len = rp_encode_hello_caps(s->current_id++, 0, &pkt, RP_VERSION_MAJOR,
+ RP_VERSION_MINOR,
+ caps, caps, sizeof caps / sizeof caps[0]);
+ rp_write(s, (void *) &pkt, len);
+
+ if (sizeof caps) {
+ rp_write(s, caps, sizeof caps);
+ }
+}
+
static char *rp_sanitize_prefix(RemotePort *s)
{
char *sanitized_name;
@@ -105,6 +187,108 @@ static Chardev *rp_autocreate_chardev(RemotePort *s, char *name)
return chr;
}
+static bool rp_pt_process_pkt(RemotePort *s, RemotePortDynPkt *dpkt)
+{
+ struct rp_pkt *pkt = dpkt->pkt;
+
+ D(qemu_log("%s: cmd=%x id=%d dev=%d\n", __func__, pkt->hdr.cmd,
+ pkt->hdr.id, pkt->hdr.dev));
+
+ if (pkt->hdr.dev >= ARRAY_SIZE(s->devs)) {
+ /* FIXME: Respond with an error. */
+ return true;
+ }
+
+ switch (pkt->hdr.cmd) {
+ case RP_CMD_hello:
+ rp_cmd_hello(s, pkt);
+ break;
+ case RP_CMD_read:
+ case RP_CMD_write:
+ case RP_CMD_interrupt:
+ case RP_CMD_ats_req:
+ case RP_CMD_ats_inv:
+ /* TBD */;
+ break;
+ default:
+ g_assert_not_reached();
+ break;
+ }
+ return false;
+}
+
+static int rp_read_pkt(RemotePort *s, RemotePortDynPkt *dpkt)
+{
+ struct rp_pkt *pkt = dpkt->pkt;
+ int used;
+ int r;
+
+ r = rp_recv(s, pkt, sizeof pkt->hdr);
+ if (r <= 0) {
+ return r;
+ }
+ used = rp_decode_hdr((void *) &pkt->hdr);
+ assert(used == sizeof pkt->hdr);
+
+ if (pkt->hdr.len) {
+ rp_dpkt_alloc(dpkt, sizeof pkt->hdr + pkt->hdr.len);
+ /* pkt may move due to realloc. */
+ pkt = dpkt->pkt;
+ r = rp_recv(s, &pkt->hdr + 1, pkt->hdr.len);
+ if (r <= 0) {
+ return r;
+ }
+ rp_decode_payload(pkt);
+ }
+
+ return used + r;
+}
+
+static void *rp_protocol_thread(void *arg)
+{
+ RemotePort *s = REMOTE_PORT(arg);
+ unsigned int i;
+ int r;
+
+ /* Make sure we have a decent bufsize to start with. */
+ rp_dpkt_alloc(&s->rsp, sizeof s->rsp.pkt->busaccess + 1024);
+ for (i = 0; i < ARRAY_SIZE(s->rx_queue.pkt); i++) {
+ rp_dpkt_alloc(&s->rx_queue.pkt[i],
+ sizeof s->rx_queue.pkt[i].pkt->busaccess + 1024);
+ s->rx_queue.inuse[i] = false;
+ }
+
+ rp_say_hello(s);
+
+ while (1) {
+ RemotePortDynPkt *dpkt;
+ unsigned int wpos = s->rx_queue.wpos;
+ bool handled;
+
+ dpkt = &s->rx_queue.pkt[wpos];
+ s->rx_queue.inuse[wpos] = true;
+
+ r = rp_read_pkt(s, dpkt);
+ if (r <= 0) {
+ /* Disconnected. */
+ break;
+ }
+ if (0) {
+ rp_pkt_dump("rport-pkt", (void *) dpkt->pkt,
+ sizeof dpkt->pkt->hdr + dpkt->pkt->hdr.len);
+ }
+ handled = rp_pt_process_pkt(s, dpkt);
+ if (handled) {
+ s->rx_queue.inuse[wpos] = false;
+ }
+ }
+
+ if (!s->finalizing) {
+ rp_fatal_error(s, "Disconnected");
+ }
+ return NULL;
+}
+
static void rp_reset(DeviceState *dev)
{
RemotePort *s = REMOTE_PORT(dev);
@@ -113,6 +297,9 @@ static void rp_reset(DeviceState *dev)
return;
}
+ qemu_thread_create(&s->thread, "remote-port", rp_protocol_thread, s,
+ QEMU_THREAD_JOINABLE);
+
s->reset_done = true;
}
@@ -124,6 +311,8 @@ static void rp_realize(DeviceState *dev, Error **errp)
s->prefix = object_get_canonical_path(OBJECT(dev));
+ qemu_mutex_init(&s->write_mutex);
+
if (!qemu_chr_fe_get_driver(&s->chr)) {
char *name;
Chardev *chr = NULL;
@@ -249,6 +438,7 @@ static void rp_unrealize(DeviceState *dev)
info_report("%s: Wait for remote-port to disconnect", s->prefix);
qemu_chr_fe_disconnect(&s->chr);
+ qemu_thread_join(&s->thread);
close(s->event.pipe.read);
close(s->event.pipe.write);
diff --git a/include/hw/core/remote-port.h b/include/hw/core/remote-port.h
index 0f40018cdb..b88e523894 100644
--- a/include/hw/core/remote-port.h
+++ b/include/hw/core/remote-port.h
@@ -56,6 +56,7 @@ typedef struct RemotePortDeviceClass {
struct RemotePort {
DeviceState parent;
+ QemuThread thread;
union {
int pipes[2];
struct {
@@ -66,9 +67,28 @@ struct RemotePort {
Chardev *chrdev;
CharFrontend chr;
bool finalizing;
+ /* To serialize writes to fd. */
+ QemuMutex write_mutex;
char *chardesc;
char *chrdev_id;
+ struct rp_peer_state peer;
+
+#define RX_QUEUE_SIZE 1024
+ struct {
+ /* This array must be sized minimum 2 and always a power of 2. */
+ RemotePortDynPkt pkt[RX_QUEUE_SIZE];
+ bool inuse[RX_QUEUE_SIZE];
+ QemuSemaphore sem;
+ unsigned int wpos;
+ unsigned int rpos;
+ } rx_queue;
+
+ /*
+ * rsp holds responses for the remote side.
+ * Used by the slave.
+ */
+ RemotePortDynPkt rsp;
const char *prefix;
const char *remote_prefix;
@@ -80,4 +100,6 @@ struct RemotePort {
RemotePortDevice *devs[REMOTE_PORT_MAX_DEVS];
};
+ssize_t rp_write(RemotePort *s, const void *buf, size_t count);
+
#endif
--
2.43.0
^ permalink raw reply related [flat|nested] 41+ messages in thread* Re: [PATCH 09/29] hw/core: Add Remote Port protocol thread and handshake
2026-02-05 19:58 ` [PATCH 09/29] hw/core: Add Remote Port protocol thread and handshake Ruslan Ruslichenko
@ 2026-02-05 20:29 ` Daniel P. Berrangé
2026-02-06 17:38 ` Ruslan Ruslichenko
0 siblings, 1 reply; 41+ messages in thread
From: Daniel P. Berrangé @ 2026-02-05 20:29 UTC (permalink / raw)
To: Ruslan Ruslichenko
Cc: qemu-devel, qemu-arm, peter.maydell, artem_mygaiev,
volodymyr_babchuk, takahiro.nakata.wr, Edgar E . Iglesias,
francisco.iglesias, Ruslan_Ruslichenko, Edgar E . Iglesias
On Thu, Feb 05, 2026 at 08:58:04PM +0100, Ruslan Ruslichenko wrote:
> From: Ruslan Ruslichenko <Ruslan_Ruslichenko@epam.com>
>
> Introduce main execution loop for the Remote Port protocol.
> Creates a dedicated thread to manage the communication lifecycle.
> This includes logic to read packets and manage RX queue.
>
> Patch also implements handshake logic to verify protocol
> versions and negotiate capabilities with remote peer.
>
> 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.c | 190 ++++++++++++++++++++++++++++++++++
> include/hw/core/remote-port.h | 22 ++++
> 2 files changed, 212 insertions(+)
>
> diff --git a/hw/core/remote-port.c b/hw/core/remote-port.c
> index 5154c1bc2a..91b0682884 100644
> --- a/hw/core/remote-port.c
> +++ b/hw/core/remote-port.c
> @@ -52,6 +52,88 @@
> #define REMOTE_PORT_CLASS(klass) \
> OBJECT_CLASS_CHECK(RemotePortClass, (klass), TYPE_REMOTE_PORT)
>
> +static void rp_pkt_dump(const char *prefix, const char *buf, size_t len)
> +{
> + qemu_hexdump(stdout, prefix, buf, len);
> +}
> +
> +static void rp_fatal_error(RemotePort *s, const char *reason)
> +{
> + error_report("%s: %s", s->prefix, reason);
> + exit(EXIT_FAILURE);
> +}
> +
> +static ssize_t rp_recv(RemotePort *s, void *buf, size_t count)
> +{
> + ssize_t r;
> +
> + r = qemu_chr_fe_read_all(&s->chr, buf, count);
> + if (r <= 0) {
> + return r;
> + }
> + if (r != count) {
> + error_report("%s: Bad read, expected %zd but got %zd",
> + s->prefix, count, r);
> + rp_fatal_error(s, "Bad read");
> + }
> +
> + return r;
> +}
> +
> +ssize_t rp_write(RemotePort *s, const void *buf, size_t count)
> +{
> + ssize_t r;
> +
> + qemu_mutex_lock(&s->write_mutex);
> + r = qemu_chr_fe_write_all(&s->chr, buf, count);
> + qemu_mutex_unlock(&s->write_mutex);
> + assert(r == count);
> + if (r <= 0) {
> + error_report("%s: Disconnected r=%zd buf=%p count=%zd",
> + s->prefix, r, buf, count);
> + rp_fatal_error(s, "Bad write");
> + }
> + return r;
> +}
> +
> +static void rp_cmd_hello(RemotePort *s, struct rp_pkt *pkt)
> +{
> + s->peer.version = pkt->hello.version;
> + if (pkt->hello.version.major != RP_VERSION_MAJOR) {
> + error_report("remote-port version missmatch remote=%d.%d local=%d.%d",
> + pkt->hello.version.major, pkt->hello.version.minor,
> + RP_VERSION_MAJOR, RP_VERSION_MINOR);
> + rp_fatal_error(s, "Bad version");
> + }
> +
> + if (pkt->hello.caps.len) {
> + void *caps = (char *) pkt + pkt->hello.caps.offset;
> +
> + rp_process_caps(&s->peer, caps, pkt->hello.caps.len);
> + }
> +}
> +
> +static void rp_say_hello(RemotePort *s)
> +{
> + struct rp_pkt_hello pkt;
> + uint32_t caps[] = {
> + CAP_BUSACCESS_EXT_BASE,
> + CAP_BUSACCESS_EXT_BYTE_EN,
> + CAP_WIRE_POSTED_UPDATES,
> + CAP_ATS,
> + };
> + size_t len;
> +
> + len = rp_encode_hello_caps(s->current_id++, 0, &pkt, RP_VERSION_MAJOR,
> + RP_VERSION_MINOR,
> + caps, caps, sizeof caps / sizeof caps[0]);
> + rp_write(s, (void *) &pkt, len);
> +
> + if (sizeof caps) {
> + rp_write(s, caps, sizeof caps);
> + }
> +}
> +
> static char *rp_sanitize_prefix(RemotePort *s)
> {
> char *sanitized_name;
> @@ -105,6 +187,108 @@ static Chardev *rp_autocreate_chardev(RemotePort *s, char *name)
> return chr;
> }
>
> +static bool rp_pt_process_pkt(RemotePort *s, RemotePortDynPkt *dpkt)
> +{
> + struct rp_pkt *pkt = dpkt->pkt;
> +
> + D(qemu_log("%s: cmd=%x id=%d dev=%d\n", __func__, pkt->hdr.cmd,
> + pkt->hdr.id, pkt->hdr.dev));
This kind of thing feels better suited to being implement with
QEMU tracepoints.
> +
> + if (pkt->hdr.dev >= ARRAY_SIZE(s->devs)) {
> + /* FIXME: Respond with an error. */
> + return true;
> + }
> +
> + switch (pkt->hdr.cmd) {
> + case RP_CMD_hello:
> + rp_cmd_hello(s, pkt);
> + break;
> + case RP_CMD_read:
> + case RP_CMD_write:
> + case RP_CMD_interrupt:
> + case RP_CMD_ats_req:
> + case RP_CMD_ats_inv:
> + /* TBD */;
> + break;
> + default:
> + g_assert_not_reached();
> + break;
> + }
> + return false;
> +}
With regards,
Daniel
--
|: https://berrange.com -o- https://www.flickr.com/photos/dberrange :|
|: https://libvirt.org -o- https://fstop138.berrange.com :|
|: https://entangle-photo.org -o- https://www.instagram.com/dberrange :|
^ permalink raw reply [flat|nested] 41+ messages in thread* Re: [PATCH 09/29] hw/core: Add Remote Port protocol thread and handshake
2026-02-05 20:29 ` Daniel P. Berrangé
@ 2026-02-06 17:38 ` Ruslan Ruslichenko
0 siblings, 0 replies; 41+ messages in thread
From: Ruslan Ruslichenko @ 2026-02-06 17:38 UTC (permalink / raw)
To: Daniel P. Berrangé
Cc: qemu-devel, qemu-arm, peter.maydell, artem_mygaiev,
volodymyr_babchuk, takahiro.nakata.wr, Edgar E . Iglesias,
francisco.iglesias, Ruslan_Ruslichenko, Edgar E . Iglesias
On Thu, Feb 5, 2026 at 9:30 PM Daniel P. Berrangé <berrange@redhat.com> wrote:
>
> On Thu, Feb 05, 2026 at 08:58:04PM +0100, Ruslan Ruslichenko wrote:
> > From: Ruslan Ruslichenko <Ruslan_Ruslichenko@epam.com>
> >
> > Introduce main execution loop for the Remote Port protocol.
> > Creates a dedicated thread to manage the communication lifecycle.
> > This includes logic to read packets and manage RX queue.
> >
> > Patch also implements handshake logic to verify protocol
> > versions and negotiate capabilities with remote peer.
> >
> > 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.c | 190 ++++++++++++++++++++++++++++++++++
> > include/hw/core/remote-port.h | 22 ++++
> > 2 files changed, 212 insertions(+)
> >
> > diff --git a/hw/core/remote-port.c b/hw/core/remote-port.c
> > index 5154c1bc2a..91b0682884 100644
> > --- a/hw/core/remote-port.c
> > +++ b/hw/core/remote-port.c
> > @@ -52,6 +52,88 @@
> > #define REMOTE_PORT_CLASS(klass) \
> > OBJECT_CLASS_CHECK(RemotePortClass, (klass), TYPE_REMOTE_PORT)
> >
> > +static void rp_pkt_dump(const char *prefix, const char *buf, size_t len)
> > +{
> > + qemu_hexdump(stdout, prefix, buf, len);
> > +}
> > +
> > +static void rp_fatal_error(RemotePort *s, const char *reason)
> > +{
> > + error_report("%s: %s", s->prefix, reason);
> > + exit(EXIT_FAILURE);
> > +}
> > +
> > +static ssize_t rp_recv(RemotePort *s, void *buf, size_t count)
> > +{
> > + ssize_t r;
> > +
> > + r = qemu_chr_fe_read_all(&s->chr, buf, count);
> > + if (r <= 0) {
> > + return r;
> > + }
> > + if (r != count) {
> > + error_report("%s: Bad read, expected %zd but got %zd",
> > + s->prefix, count, r);
> > + rp_fatal_error(s, "Bad read");
> > + }
> > +
> > + return r;
> > +}
> > +
> > +ssize_t rp_write(RemotePort *s, const void *buf, size_t count)
> > +{
> > + ssize_t r;
> > +
> > + qemu_mutex_lock(&s->write_mutex);
> > + r = qemu_chr_fe_write_all(&s->chr, buf, count);
> > + qemu_mutex_unlock(&s->write_mutex);
> > + assert(r == count);
> > + if (r <= 0) {
> > + error_report("%s: Disconnected r=%zd buf=%p count=%zd",
> > + s->prefix, r, buf, count);
> > + rp_fatal_error(s, "Bad write");
> > + }
> > + return r;
> > +}
> > +
> > +static void rp_cmd_hello(RemotePort *s, struct rp_pkt *pkt)
> > +{
> > + s->peer.version = pkt->hello.version;
> > + if (pkt->hello.version.major != RP_VERSION_MAJOR) {
> > + error_report("remote-port version missmatch remote=%d.%d local=%d.%d",
> > + pkt->hello.version.major, pkt->hello.version.minor,
> > + RP_VERSION_MAJOR, RP_VERSION_MINOR);
> > + rp_fatal_error(s, "Bad version");
> > + }
> > +
> > + if (pkt->hello.caps.len) {
> > + void *caps = (char *) pkt + pkt->hello.caps.offset;
> > +
> > + rp_process_caps(&s->peer, caps, pkt->hello.caps.len);
> > + }
> > +}
> > +
> > +static void rp_say_hello(RemotePort *s)
> > +{
> > + struct rp_pkt_hello pkt;
> > + uint32_t caps[] = {
> > + CAP_BUSACCESS_EXT_BASE,
> > + CAP_BUSACCESS_EXT_BYTE_EN,
> > + CAP_WIRE_POSTED_UPDATES,
> > + CAP_ATS,
> > + };
> > + size_t len;
> > +
> > + len = rp_encode_hello_caps(s->current_id++, 0, &pkt, RP_VERSION_MAJOR,
> > + RP_VERSION_MINOR,
> > + caps, caps, sizeof caps / sizeof caps[0]);
> > + rp_write(s, (void *) &pkt, len);
> > +
> > + if (sizeof caps) {
> > + rp_write(s, caps, sizeof caps);
> > + }
> > +}
> > +
> > static char *rp_sanitize_prefix(RemotePort *s)
> > {
> > char *sanitized_name;
> > @@ -105,6 +187,108 @@ static Chardev *rp_autocreate_chardev(RemotePort *s, char *name)
> > return chr;
> > }
> >
> > +static bool rp_pt_process_pkt(RemotePort *s, RemotePortDynPkt *dpkt)
> > +{
> > + struct rp_pkt *pkt = dpkt->pkt;
> > +
> > + D(qemu_log("%s: cmd=%x id=%d dev=%d\n", __func__, pkt->hdr.cmd,
> > + pkt->hdr.id, pkt->hdr.dev));
>
> This kind of thing feels better suited to being implement with
> QEMU tracepoints.
>
Agreed. I will replace the manual debug logging with standard QEMU
tracepoints in v2.
> > +
> > + if (pkt->hdr.dev >= ARRAY_SIZE(s->devs)) {
> > + /* FIXME: Respond with an error. */
> > + return true;
> > + }
> > +
> > + switch (pkt->hdr.cmd) {
> > + case RP_CMD_hello:
> > + rp_cmd_hello(s, pkt);
> > + break;
> > + case RP_CMD_read:
> > + case RP_CMD_write:
> > + case RP_CMD_interrupt:
> > + case RP_CMD_ats_req:
> > + case RP_CMD_ats_inv:
> > + /* TBD */;
> > + break;
> > + default:
> > + g_assert_not_reached();
> > + break;
> > + }
> > + return false;
> > +}
>
> With regards,
> Daniel
> --
> |: https://berrange.com -o- https://www.flickr.com/photos/dberrange :|
> |: https://libvirt.org -o- https://fstop138.berrange.com :|
> |: https://entangle-photo.org -o- https://www.instagram.com/dberrange :|
>
--
BR,
Ruslan
^ permalink raw reply [flat|nested] 41+ messages in thread
* [PATCH 10/29] hw/core: Implement Remote Port packet dispatch logic
2026-02-05 19:57 [PATCH 00/29] hw/core: Introduce Remote Port Co-simulation Protocol Ruslan Ruslichenko
` (8 preceding siblings ...)
2026-02-05 19:58 ` [PATCH 09/29] hw/core: Add Remote Port protocol thread and handshake Ruslan Ruslichenko
@ 2026-02-05 19:58 ` Ruslan Ruslichenko
2026-02-05 20:32 ` Daniel P. Berrangé
2026-02-05 19:58 ` [PATCH 11/29] hw/core: Implement Remote Port response handling Ruslan Ruslichenko
` (18 subsequent siblings)
28 siblings, 1 reply; 41+ messages in thread
From: Ruslan Ruslichenko @ 2026-02-05 19:58 UTC (permalink / raw)
To: qemu-devel
Cc: qemu-arm, peter.maydell, artem_mygaiev, volodymyr_babchuk,
takahiro.nakata.wr, Edgar E . Iglesias, francisco.iglesias,
Ruslan_Ruslichenko, Edgar E . Iglesias
From: Ruslan Ruslichenko <Ruslan_Ruslichenko@epam.com>
Implement the mechanism to transfer packets from the dedicated
protocol thread to the main QEMU execution loop for processing.
The patch adds the following features:
- signaling logic using internal pipe to wake up the main loop
- the rp_process handler, which retrieves packets from queue and
dispatches them to the target Remote Port device.
This enables QEMU device models to handle remote events.
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.c | 148 +++++++++++++++++++++++++++++++++-
include/hw/core/remote-port.h | 5 ++
2 files changed, 152 insertions(+), 1 deletion(-)
diff --git a/hw/core/remote-port.c b/hw/core/remote-port.c
index 91b0682884..e44d9249c3 100644
--- a/hw/core/remote-port.c
+++ b/hw/core/remote-port.c
@@ -52,6 +52,8 @@
#define REMOTE_PORT_CLASS(klass) \
OBJECT_CLASS_CHECK(RemotePortClass, (klass), TYPE_REMOTE_PORT)
+static void rp_event_read(void *opaque);
+
static void rp_pkt_dump(const char *prefix, const char *buf, size_t len)
{
qemu_hexdump(stdout, prefix, buf, len);
@@ -96,6 +98,12 @@ ssize_t rp_write(RemotePort *s, const void *buf, size_t count)
return r;
}
+static unsigned int rp_has_work(RemotePort *s)
+{
+ unsigned int work = s->rx_queue.wpos - s->rx_queue.rpos;
+ return work;
+}
+
static void rp_cmd_hello(RemotePort *s, struct rp_pkt *pkt)
{
s->peer.version = pkt->hello.version;
@@ -187,6 +195,135 @@ static Chardev *rp_autocreate_chardev(RemotePort *s, char *name)
return chr;
}
+void rp_process(RemotePort *s)
+{
+ while (true) {
+ struct rp_pkt *pkt;
+ unsigned int rpos;
+ bool actioned = false;
+ RemotePortDevice *dev;
+ RemotePortDeviceClass *rpdc;
+
+ qemu_mutex_lock(&s->rsp_mutex);
+ if (!rp_has_work(s)) {
+ qemu_mutex_unlock(&s->rsp_mutex);
+ break;
+ }
+ rpos = s->rx_queue.rpos;
+
+ pkt = s->rx_queue.pkt[rpos].pkt;
+ D(qemu_log("%s: io-thread rpos=%d wpos=%d cmd=%d dev=%d\n",
+ s->prefix, s->rx_queue.rpos, s->rx_queue.wpos,
+ pkt->hdr.cmd, pkt->hdr.dev));
+
+ /*
+ * To handle recursiveness, we need to advance the index
+ * index before processing the packet.
+ */
+ s->rx_queue.rpos++;
+ s->rx_queue.rpos %= ARRAY_SIZE(s->rx_queue.pkt);
+ qemu_mutex_unlock(&s->rsp_mutex);
+
+ dev = s->devs[pkt->hdr.dev];
+ if (dev) {
+ rpdc = REMOTE_PORT_DEVICE_GET_CLASS(dev);
+ if (rpdc->ops[pkt->hdr.cmd]) {
+ rpdc->ops[pkt->hdr.cmd](dev, pkt);
+ actioned = true;
+ }
+ }
+
+ switch (pkt->hdr.cmd) {
+ /* TBD */
+ default:
+ assert(actioned);
+ }
+
+ s->rx_queue.inuse[rpos] = false;
+ qemu_sem_post(&s->rx_queue.sem);
+ }
+}
+
+static void rp_event_read(void *opaque)
+{
+ RemotePort *s = REMOTE_PORT(opaque);
+ unsigned char buf[32];
+ ssize_t r;
+
+ /* We don't care about the data. Just read it out to clear the event. */
+ do {
+#ifdef _WIN32
+ r = qemu_recv_wrap(s->event.pipe.read, buf, sizeof buf, 0);
+#else
+ r = read(s->event.pipe.read, buf, sizeof buf);
+#endif
+ if (r == 0) {
+ return;
+ }
+ } while (r == sizeof buf || (r < 0 && errno == EINTR));
+
+ rp_process(s);
+}
+
+static void rp_event_notify(RemotePort *s)
+{
+ unsigned char d = 0;
+ ssize_t r;
+
+#ifdef _WIN32
+ /* Mingw is sensitive about doing write's to socket descriptors. */
+ r = qemu_send_wrap(s->event.pipe.write, &d, sizeof d, 0);
+#else
+ r = qemu_write_full(s->event.pipe.write, &d, sizeof d);
+#endif
+ if (r == 0) {
+ hw_error("%s: pipe closed\n", s->prefix);
+ }
+}
+
+/* Handover a pkt to CPU or IO-thread context. */
+static void rp_pt_handover_pkt(RemotePort *s, RemotePortDynPkt *dpkt)
+{
+ bool full;
+
+ /*
+ * Take the rsp lock around the wpos update, otherwise
+ * rp_wait_resp will race with us.
+ */
+ qemu_mutex_lock(&s->rsp_mutex);
+ s->rx_queue.wpos++;
+ s->rx_queue.wpos %= ARRAY_SIZE(s->rx_queue.pkt);
+ /*
+ * Ensure rx_queue index update is visible to consumer
+ * before signaling event, to prevent lost wakeup
+ */
+ smp_mb();
+ rp_event_notify(s);
+ qemu_cond_signal(&s->progress_cond);
+ qemu_mutex_unlock(&s->rsp_mutex);
+
+ do {
+ full = s->rx_queue.inuse[s->rx_queue.wpos];
+ if (full) {
+ qemu_log("%s: FULL rx queue %d\n", __func__, s->rx_queue.wpos);
+ if (qemu_sem_timedwait(&s->rx_queue.sem, 2 * 1000) != 0) {
+#ifndef _WIN32
+ int sval;
+
+#ifndef CONFIG_SEM_TIMEDWAIT
+ sval = s->rx_queue.sem.count;
+#else
+ sem_getvalue(&s->rx_queue.sem.sem, &sval);
+#endif
+ qemu_log("semwait: %d rpos=%u wpos=%u\n", sval,
+ s->rx_queue.rpos, s->rx_queue.wpos);
+#endif
+ qemu_log("Deadlock?\n");
+ }
+ }
+ } while (full);
+}
+
static bool rp_pt_process_pkt(RemotePort *s, RemotePortDynPkt *dpkt)
{
struct rp_pkt *pkt = dpkt->pkt;
@@ -208,7 +345,7 @@ static bool rp_pt_process_pkt(RemotePort *s, RemotePortDynPkt *dpkt)
case RP_CMD_interrupt:
case RP_CMD_ats_req:
case RP_CMD_ats_inv:
- /* TBD */;
+ rp_pt_handover_pkt(s, dpkt);
break;
default:
g_assert_not_reached();
@@ -312,6 +449,8 @@ static void rp_realize(DeviceState *dev, Error **errp)
s->prefix = object_get_canonical_path(OBJECT(dev));
qemu_mutex_init(&s->write_mutex);
+ qemu_mutex_init(&s->rsp_mutex);
+ qemu_cond_init(&s->progress_cond);
if (!qemu_chr_fe_get_driver(&s->chr)) {
char *name;
@@ -413,6 +552,7 @@ static void rp_realize(DeviceState *dev, Error **errp)
s->prefix);
exit(EXIT_FAILURE);
}
+ qemu_set_fd_handler(s->event.pipe.read, rp_event_read, NULL, s);
}
#else
if (!g_unix_open_pipe(s->event.pipes, FD_CLOEXEC, NULL)) {
@@ -427,7 +567,10 @@ static void rp_realize(DeviceState *dev, Error **errp)
exit(EXIT_FAILURE);
}
+ qemu_set_fd_handler(s->event.pipe.read, rp_event_read, NULL, s);
#endif
+
+ qemu_sem_init(&s->rx_queue.sem, ARRAY_SIZE(s->rx_queue.pkt) - 1);
}
static void rp_unrealize(DeviceState *dev)
@@ -436,6 +579,9 @@ static void rp_unrealize(DeviceState *dev)
s->finalizing = true;
+ /* Unregister handler. */
+ qemu_set_fd_handler(s->event.pipe.read, NULL, NULL, s);
+
info_report("%s: Wait for remote-port to disconnect", s->prefix);
qemu_chr_fe_disconnect(&s->chr);
qemu_thread_join(&s->thread);
diff --git a/include/hw/core/remote-port.h b/include/hw/core/remote-port.h
index b88e523894..21dfbe89cd 100644
--- a/include/hw/core/remote-port.h
+++ b/include/hw/core/remote-port.h
@@ -74,6 +74,9 @@ struct RemotePort {
char *chrdev_id;
struct rp_peer_state peer;
+ QemuMutex rsp_mutex;
+ QemuCond progress_cond;
+
#define RX_QUEUE_SIZE 1024
struct {
/* This array must be sized minimum 2 and always a power of 2. */
@@ -100,6 +103,8 @@ struct RemotePort {
RemotePortDevice *devs[REMOTE_PORT_MAX_DEVS];
};
+void rp_process(RemotePort *s);
+
ssize_t rp_write(RemotePort *s, const void *buf, size_t count);
#endif
--
2.43.0
^ permalink raw reply related [flat|nested] 41+ messages in thread* Re: [PATCH 10/29] hw/core: Implement Remote Port packet dispatch logic
2026-02-05 19:58 ` [PATCH 10/29] hw/core: Implement Remote Port packet dispatch logic Ruslan Ruslichenko
@ 2026-02-05 20:32 ` Daniel P. Berrangé
0 siblings, 0 replies; 41+ messages in thread
From: Daniel P. Berrangé @ 2026-02-05 20:32 UTC (permalink / raw)
To: Ruslan Ruslichenko
Cc: qemu-devel, qemu-arm, peter.maydell, artem_mygaiev,
volodymyr_babchuk, takahiro.nakata.wr, Edgar E . Iglesias,
francisco.iglesias, Ruslan_Ruslichenko, Edgar E . Iglesias
On Thu, Feb 05, 2026 at 08:58:05PM +0100, Ruslan Ruslichenko wrote:
> From: Ruslan Ruslichenko <Ruslan_Ruslichenko@epam.com>
>
> Implement the mechanism to transfer packets from the dedicated
> protocol thread to the main QEMU execution loop for processing.
>
> The patch adds the following features:
> - signaling logic using internal pipe to wake up the main loop
> - the rp_process handler, which retrieves packets from queue and
> dispatches them to the target Remote Port device.
>
> This enables QEMU device models to handle remote events.
>
> 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.c | 148 +++++++++++++++++++++++++++++++++-
> include/hw/core/remote-port.h | 5 ++
> 2 files changed, 152 insertions(+), 1 deletion(-)
>
> diff --git a/hw/core/remote-port.c b/hw/core/remote-port.c
> index 91b0682884..e44d9249c3 100644
> --- a/hw/core/remote-port.c
> +++ b/hw/core/remote-port.c
> @@ -52,6 +52,8 @@
> #define REMOTE_PORT_CLASS(klass) \
> OBJECT_CLASS_CHECK(RemotePortClass, (klass), TYPE_REMOTE_PORT)
>
> +static void rp_event_read(void *opaque);
> +
> static void rp_pkt_dump(const char *prefix, const char *buf, size_t len)
> {
> qemu_hexdump(stdout, prefix, buf, len);
> @@ -96,6 +98,12 @@ ssize_t rp_write(RemotePort *s, const void *buf, size_t count)
> return r;
> }
>
> +static unsigned int rp_has_work(RemotePort *s)
> +{
> + unsigned int work = s->rx_queue.wpos - s->rx_queue.rpos;
> + return work;
> +}
> +
> static void rp_cmd_hello(RemotePort *s, struct rp_pkt *pkt)
> {
> s->peer.version = pkt->hello.version;
> @@ -187,6 +195,135 @@ static Chardev *rp_autocreate_chardev(RemotePort *s, char *name)
> return chr;
> }
>
> +void rp_process(RemotePort *s)
> +{
> + while (true) {
> + struct rp_pkt *pkt;
> + unsigned int rpos;
> + bool actioned = false;
> + RemotePortDevice *dev;
> + RemotePortDeviceClass *rpdc;
> +
> + qemu_mutex_lock(&s->rsp_mutex);
> + if (!rp_has_work(s)) {
> + qemu_mutex_unlock(&s->rsp_mutex);
> + break;
> + }
> + rpos = s->rx_queue.rpos;
> +
> + pkt = s->rx_queue.pkt[rpos].pkt;
> + D(qemu_log("%s: io-thread rpos=%d wpos=%d cmd=%d dev=%d\n",
> + s->prefix, s->rx_queue.rpos, s->rx_queue.wpos,
> + pkt->hdr.cmd, pkt->hdr.dev));
Same point as last patch, that emitted structured data in
tracepoints is preferrable to merely logging. Consider this
to apply to any other qemu_log call, so I won't repeat it
every time.
> +
> + /*
> + * To handle recursiveness, we need to advance the index
> + * index before processing the packet.
> + */
> + s->rx_queue.rpos++;
> + s->rx_queue.rpos %= ARRAY_SIZE(s->rx_queue.pkt);
> + qemu_mutex_unlock(&s->rsp_mutex);
> +
> + dev = s->devs[pkt->hdr.dev];
> + if (dev) {
> + rpdc = REMOTE_PORT_DEVICE_GET_CLASS(dev);
> + if (rpdc->ops[pkt->hdr.cmd]) {
> + rpdc->ops[pkt->hdr.cmd](dev, pkt);
> + actioned = true;
> + }
> + }
> +
> + switch (pkt->hdr.cmd) {
> + /* TBD */
> + default:
> + assert(actioned);
> + }
> +
> + s->rx_queue.inuse[rpos] = false;
> + qemu_sem_post(&s->rx_queue.sem);
> + }
> +}
> +
> +static void rp_event_read(void *opaque)
> +{
> + RemotePort *s = REMOTE_PORT(opaque);
> + unsigned char buf[32];
> + ssize_t r;
> +
> + /* We don't care about the data. Just read it out to clear the event. */
> + do {
> +#ifdef _WIN32
> + r = qemu_recv_wrap(s->event.pipe.read, buf, sizeof buf, 0);
> +#else
> + r = read(s->event.pipe.read, buf, sizeof buf);
> +#endif
> + if (r == 0) {
> + return;
> + }
> + } while (r == sizeof buf || (r < 0 && errno == EINTR));
> +
> + rp_process(s);
> +}
> +
> +static void rp_event_notify(RemotePort *s)
> +{
> + unsigned char d = 0;
> + ssize_t r;
> +
> +#ifdef _WIN32
> + /* Mingw is sensitive about doing write's to socket descriptors. */
> + r = qemu_send_wrap(s->event.pipe.write, &d, sizeof d, 0);
> +#else
> + r = qemu_write_full(s->event.pipe.write, &d, sizeof d);
> +#endif
> + if (r == 0) {
> + hw_error("%s: pipe closed\n", s->prefix);
> + }
> +}
> +
> +/* Handover a pkt to CPU or IO-thread context. */
> +static void rp_pt_handover_pkt(RemotePort *s, RemotePortDynPkt *dpkt)
> +{
> + bool full;
> +
> + /*
> + * Take the rsp lock around the wpos update, otherwise
> + * rp_wait_resp will race with us.
> + */
> + qemu_mutex_lock(&s->rsp_mutex);
> + s->rx_queue.wpos++;
> + s->rx_queue.wpos %= ARRAY_SIZE(s->rx_queue.pkt);
> + /*
> + * Ensure rx_queue index update is visible to consumer
> + * before signaling event, to prevent lost wakeup
> + */
> + smp_mb();
> + rp_event_notify(s);
> + qemu_cond_signal(&s->progress_cond);
> + qemu_mutex_unlock(&s->rsp_mutex);
> +
> + do {
> + full = s->rx_queue.inuse[s->rx_queue.wpos];
> + if (full) {
> + qemu_log("%s: FULL rx queue %d\n", __func__, s->rx_queue.wpos);
> + if (qemu_sem_timedwait(&s->rx_queue.sem, 2 * 1000) != 0) {
> +#ifndef _WIN32
> + int sval;
> +
> +#ifndef CONFIG_SEM_TIMEDWAIT
> + sval = s->rx_queue.sem.count;
> +#else
> + sem_getvalue(&s->rx_queue.sem.sem, &sval);
> +#endif
> + qemu_log("semwait: %d rpos=%u wpos=%u\n", sval,
> + s->rx_queue.rpos, s->rx_queue.wpos);
> +#endif
> + qemu_log("Deadlock?\n");
> + }
> + }
> + } while (full);
> +}
With regards,
Daniel
--
|: https://berrange.com -o- https://www.flickr.com/photos/dberrange :|
|: https://libvirt.org -o- https://fstop138.berrange.com :|
|: https://entangle-photo.org -o- https://www.instagram.com/dberrange :|
^ permalink raw reply [flat|nested] 41+ messages in thread
* [PATCH 11/29] hw/core: Implement Remote Port response handling
2026-02-05 19:57 [PATCH 00/29] hw/core: Introduce Remote Port Co-simulation Protocol Ruslan Ruslichenko
` (9 preceding siblings ...)
2026-02-05 19:58 ` [PATCH 10/29] hw/core: Implement Remote Port packet dispatch logic Ruslan Ruslichenko
@ 2026-02-05 19:58 ` Ruslan Ruslichenko
2026-02-05 19:58 ` [PATCH 12/29] hw/core: Implement Remote Port time synchronization Ruslan Ruslichenko
` (17 subsequent siblings)
28 siblings, 0 replies; 41+ messages in thread
From: Ruslan Ruslichenko @ 2026-02-05 19:58 UTC (permalink / raw)
To: qemu-devel
Cc: qemu-arm, peter.maydell, artem_mygaiev, volodymyr_babchuk,
takahiro.nakata.wr, Edgar E . Iglesias, francisco.iglesias,
Ruslan_Ruslichenko, Edgar E . Iglesias
From: Ruslan Ruslichenko <Ruslan_Ruslichenko@epam.com>
Add infrastructure to handle transaction responses from
remote peers.
This patch enables QEMU to wait for replies when it initiates
a memory access (Read/Write). It implements a mechanism to track
outstanding transaction IDs and block the execution thread until
the patching response is received.
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.c | 136 +++++++++++++++++++++++++++++++++-
include/hw/core/remote-port.h | 29 ++++++++
2 files changed, 163 insertions(+), 2 deletions(-)
diff --git a/hw/core/remote-port.c b/hw/core/remote-port.c
index e44d9249c3..00c9529348 100644
--- a/hw/core/remote-port.c
+++ b/hw/core/remote-port.c
@@ -59,6 +59,16 @@ static void rp_pkt_dump(const char *prefix, const char *buf, size_t len)
qemu_hexdump(stdout, prefix, buf, len);
}
+void rp_rsp_mutex_lock(RemotePort *s)
+{
+ qemu_mutex_lock(&s->rsp_mutex);
+}
+
+void rp_rsp_mutex_unlock(RemotePort *s)
+{
+ qemu_mutex_unlock(&s->rsp_mutex);
+}
+
static void rp_fatal_error(RemotePort *s, const char *reason)
{
error_report("%s: %s", s->prefix, reason);
@@ -104,6 +114,80 @@ static unsigned int rp_has_work(RemotePort *s)
return work;
}
+/* Response handling. */
+RemotePortRespSlot *rp_dev_timed_wait_resp(RemotePort *s, uint32_t dev,
+ uint32_t id, int timems)
+{
+ int i;
+
+ assert(s->devs[dev]);
+
+ /* Find a free slot. */
+ for (i = 0; i < ARRAY_SIZE(s->dev_state[dev].rsp_queue); i++) {
+ if (s->dev_state[dev].rsp_queue[i].used == false) {
+ break;
+ }
+ }
+
+ if (i == ARRAY_SIZE(s->dev_state[dev].rsp_queue) ||
+ s->dev_state[dev].rsp_queue[i].used == true) {
+ error_report("Number of outstanding transactions exceeded! %d",
+ RP_MAX_OUTSTANDING_TRANSACTIONS);
+ rp_fatal_error(s, "Internal error");
+ }
+
+ /* Got a slot, fill it in. */
+ s->dev_state[dev].rsp_queue[i].id = id;
+ s->dev_state[dev].rsp_queue[i].valid = false;
+ s->dev_state[dev].rsp_queue[i].used = true;
+
+ while (!s->dev_state[dev].rsp_queue[i].valid) {
+ rp_rsp_mutex_unlock(s);
+ rp_event_read(s);
+ rp_rsp_mutex_lock(s);
+ if (s->dev_state[dev].rsp_queue[i].valid) {
+ break;
+ }
+ if (!rp_has_work(s)) {
+ if (timems) {
+ if (!qemu_cond_timedwait(&s->progress_cond, &s->rsp_mutex,
+ timems)) {
+ /*
+ * TimeOut!
+ */
+ break;
+ }
+ } else {
+ qemu_cond_wait(&s->progress_cond, &s->rsp_mutex);
+ }
+ }
+ }
+ return &s->dev_state[dev].rsp_queue[i];
+}
+
+RemotePortRespSlot *rp_dev_wait_resp(RemotePort *s, uint32_t dev, uint32_t id)
+{
+ return rp_dev_timed_wait_resp(s, dev, id, 0);
+}
+
+RemotePortDynPkt rp_wait_resp(RemotePort *s)
+{
+ while (!rp_dpkt_is_valid(&s->rspqueue)) {
+ rp_rsp_mutex_unlock(s);
+ rp_event_read(s);
+ rp_rsp_mutex_lock(s);
+ /* Need to recheck the condition with the rsp lock taken. */
+ if (rp_dpkt_is_valid(&s->rspqueue)) {
+ break;
+ }
+ D(qemu_log("%s: wait for progress\n", __func__));
+ if (!rp_has_work(s)) {
+ qemu_cond_wait(&s->progress_cond, &s->rsp_mutex);
+ }
+ }
+ return s->rspqueue;
+}
+
static void rp_cmd_hello(RemotePort *s, struct rp_pkt *pkt)
{
s->peer.version = pkt->hello.version;
@@ -328,14 +412,52 @@ static bool rp_pt_process_pkt(RemotePort *s, RemotePortDynPkt *dpkt)
{
struct rp_pkt *pkt = dpkt->pkt;
- D(qemu_log("%s: cmd=%x id=%d dev=%d\n", __func__, pkt->hdr.cmd,
- pkt->hdr.id, pkt->hdr.dev));
+ D(qemu_log("%s: cmd=%x id=%d dev=%d rsp=%d\n", __func__, pkt->hdr.cmd,
+ pkt->hdr.id, pkt->hdr.dev,
+ pkt->hdr.flags & RP_PKT_FLAGS_response));
if (pkt->hdr.dev >= ARRAY_SIZE(s->devs)) {
/* FIXME: Respond with an error. */
return true;
}
+ if (pkt->hdr.flags & RP_PKT_FLAGS_response) {
+ uint32_t dev = pkt->hdr.dev;
+ uint32_t id = pkt->hdr.id;
+ int i;
+
+ if (pkt->hdr.flags & RP_PKT_FLAGS_posted) {
+ printf("Drop response for posted packets\n");
+ return true;
+ }
+
+ qemu_mutex_lock(&s->rsp_mutex);
+
+ /* Try to find a per-device slot first. */
+ for (i = 0; i < ARRAY_SIZE(s->dev_state[dev].rsp_queue); i++) {
+ if (s->devs[dev] && s->dev_state[dev].rsp_queue[i].used == true
+ && s->dev_state[dev].rsp_queue[i].id == id) {
+ break;
+ }
+ }
+
+ if (i < ARRAY_SIZE(s->dev_state[dev].rsp_queue)) {
+ /* Found a per device one. */
+ assert(s->dev_state[dev].rsp_queue[i].valid == false);
+
+ rp_dpkt_swap(&s->dev_state[dev].rsp_queue[i].rsp, dpkt);
+ s->dev_state[dev].rsp_queue[i].valid = true;
+
+ qemu_cond_signal(&s->progress_cond);
+ } else {
+ rp_dpkt_swap(&s->rspqueue, dpkt);
+ qemu_cond_signal(&s->progress_cond);
+ }
+
+ qemu_mutex_unlock(&s->rsp_mutex);
+ return true;
+ }
+
switch (pkt->hdr.cmd) {
case RP_CMD_hello:
rp_cmd_hello(s, pkt);
@@ -389,6 +511,7 @@ static void *rp_protocol_thread(void *arg)
/* Make sure we have a decent bufsize to start with. */
rp_dpkt_alloc(&s->rsp, sizeof s->rsp.pkt->busaccess + 1024);
+ rp_dpkt_alloc(&s->rspqueue, sizeof s->rspqueue.pkt->busaccess + 1024);
for (i = 0; i < ARRAY_SIZE(s->rx_queue.pkt); i++) {
rp_dpkt_alloc(&s->rx_queue.pkt[i],
sizeof s->rx_queue.pkt[i].pkt->busaccess + 1024);
@@ -614,6 +737,7 @@ static void rp_prop_allow_set_link(const Object *obj, const char *name,
static void rp_init(Object *obj)
{
RemotePort *s = REMOTE_PORT(obj);
+ int t;
int i;
for (i = 0; i < REMOTE_PORT_MAX_DEVS; ++i) {
@@ -623,6 +747,14 @@ static void rp_init(Object *obj)
rp_prop_allow_set_link,
OBJ_PROP_LINK_STRONG);
g_free(name);
+
+
+ for (t = 0; t < RP_MAX_OUTSTANDING_TRANSACTIONS; t++) {
+ s->dev_state[i].rsp_queue[t].used = false;
+ s->dev_state[i].rsp_queue[t].valid = false;
+ rp_dpkt_alloc(&s->dev_state[i].rsp_queue[t].rsp,
+ sizeof s->dev_state[i].rsp_queue[t].rsp.pkt->busaccess + 1024);
+ }
}
}
diff --git a/include/hw/core/remote-port.h b/include/hw/core/remote-port.h
index 21dfbe89cd..c1b21eb573 100644
--- a/include/hw/core/remote-port.h
+++ b/include/hw/core/remote-port.h
@@ -53,6 +53,13 @@ typedef struct RemotePortDeviceClass {
#define TYPE_REMOTE_PORT "remote-port"
#define REMOTE_PORT(obj) OBJECT_CHECK(RemotePort, (obj), TYPE_REMOTE_PORT)
+typedef struct RemotePortRespSlot {
+ RemotePortDynPkt rsp;
+ uint32_t id;
+ bool used;
+ bool valid;
+} RemotePortRespSlot;
+
struct RemotePort {
DeviceState parent;
@@ -93,6 +100,13 @@ struct RemotePort {
*/
RemotePortDynPkt rsp;
+ /*
+ * rspqueue holds received responses from the remote side.
+ * Only one for the moment but it might grow.
+ * Used by the master.
+ */
+ RemotePortDynPkt rspqueue;
+
const char *prefix;
const char *remote_prefix;
@@ -100,9 +114,24 @@ struct RemotePort {
bool reset_done;
#define REMOTE_PORT_MAX_DEVS 1024
+#define RP_MAX_OUTSTANDING_TRANSACTIONS 32
+ struct {
+ RemotePortRespSlot rsp_queue[RP_MAX_OUTSTANDING_TRANSACTIONS];
+ } dev_state[REMOTE_PORT_MAX_DEVS];
+
RemotePortDevice *devs[REMOTE_PORT_MAX_DEVS];
};
+
+void rp_rsp_mutex_lock(RemotePort *s);
+void rp_rsp_mutex_unlock(RemotePort *s);
+
+RemotePortDynPkt rp_wait_resp(RemotePort *s);
+
+RemotePortRespSlot *rp_dev_wait_resp(RemotePort *s, uint32_t dev, uint32_t id);
+RemotePortRespSlot *rp_dev_timed_wait_resp(RemotePort *s, uint32_t dev,
+ uint32_t id, int timems);
+
void rp_process(RemotePort *s);
ssize_t rp_write(RemotePort *s, const void *buf, size_t count);
--
2.43.0
^ permalink raw reply related [flat|nested] 41+ messages in thread* [PATCH 12/29] hw/core: Implement Remote Port time synchronization
2026-02-05 19:57 [PATCH 00/29] hw/core: Introduce Remote Port Co-simulation Protocol Ruslan Ruslichenko
` (10 preceding siblings ...)
2026-02-05 19:58 ` [PATCH 11/29] hw/core: Implement Remote Port response handling Ruslan Ruslichenko
@ 2026-02-05 19:58 ` Ruslan Ruslichenko
2026-02-05 19:58 ` [PATCH 13/29] system/memory: Introduce unified MemoryTransaction and .access callback Ruslan Ruslichenko
` (16 subsequent siblings)
28 siblings, 0 replies; 41+ messages in thread
From: Ruslan Ruslichenko @ 2026-02-05 19:58 UTC (permalink / raw)
To: qemu-devel
Cc: qemu-arm, peter.maydell, artem_mygaiev, volodymyr_babchuk,
takahiro.nakata.wr, Edgar E . Iglesias, francisco.iglesias,
Ruslan_Ruslichenko, Edgar E . Iglesias
From: Ruslan Ruslichenko <Ruslan_Ruslichenko@epam.com>
Add logic to synchronize QEMU's virtual time with the remote peer.
The patch uses timers to periodically send sync requests to the peer
and handle incoming sync requests to defer the response if the peer
is ahead of QEMU's current time.
This also adds the 'sync' and 'sync-quantum' QOM properties to
configure this behavior.
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.c | 184 +++++++++++++++++++++++++++++++++-
include/hw/core/remote-port.h | 15 +++
2 files changed, 197 insertions(+), 2 deletions(-)
diff --git a/hw/core/remote-port.c b/hw/core/remote-port.c
index 00c9529348..6a1933a5a6 100644
--- a/hw/core/remote-port.c
+++ b/hw/core/remote-port.c
@@ -13,6 +13,7 @@
#include "chardev/char.h"
#include "system/cpus.h"
#include "system/cpu-timers.h"
+#include "exec/icount.h"
#include "system/reset.h"
#include "hw/core/sysbus.h"
#include "hw/core/hw-error.h"
@@ -53,6 +54,8 @@
OBJECT_CLASS_CHECK(RemotePortClass, (klass), TYPE_REMOTE_PORT)
static void rp_event_read(void *opaque);
+static void sync_timer_hit(void *opaque);
+static void syncresp_timer_hit(void *opaque);
static void rp_pkt_dump(const char *prefix, const char *buf, size_t len)
{
@@ -69,9 +72,42 @@ void rp_rsp_mutex_unlock(RemotePort *s)
qemu_mutex_unlock(&s->rsp_mutex);
}
+int64_t rp_normalized_vmclk(RemotePort *s)
+{
+ int64_t clk;
+
+ clk = qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL);
+ clk -= s->peer.clk_base;
+ return clk;
+}
+
+static void rp_restart_sync_timer_bare(RemotePort *s)
+{
+ if (!s->do_sync) {
+ return;
+ }
+
+ if (s->sync.quantum) {
+ ptimer_stop(s->sync.ptimer);
+ ptimer_set_limit(s->sync.ptimer, s->sync.quantum, 1);
+ ptimer_run(s->sync.ptimer, 1);
+ }
+}
+
+void rp_restart_sync_timer(RemotePort *s)
+{
+ if (s->doing_sync) {
+ return;
+ }
+ ptimer_transaction_begin(s->sync.ptimer);
+ rp_restart_sync_timer_bare(s);
+ ptimer_transaction_commit(s->sync.ptimer);
+}
+
static void rp_fatal_error(RemotePort *s, const char *reason)
{
- error_report("%s: %s", s->prefix, reason);
+ int64_t clk = rp_normalized_vmclk(s);
+ error_report("%s: %s clk=%" PRIu64 " ns", s->prefix, reason, clk);
exit(EXIT_FAILURE);
}
@@ -205,6 +241,39 @@ static void rp_cmd_hello(RemotePort *s, struct rp_pkt *pkt)
}
}
+static void rp_cmd_sync(RemotePort *s, struct rp_pkt *pkt)
+{
+ size_t enclen;
+ int64_t clk;
+ int64_t diff;
+
+ assert(!(pkt->hdr.flags & RP_PKT_FLAGS_response));
+
+ clk = rp_normalized_vmclk(s);
+ diff = pkt->sync.timestamp - clk;
+
+ enclen = rp_encode_sync_resp(pkt->hdr.id, pkt->hdr.dev, &s->sync.rsp.sync,
+ pkt->sync.timestamp);
+ assert(enclen == sizeof s->sync.rsp.sync);
+
+ /* We have temporarily disabled blocking syncs into QEMU. */
+ if (diff <= 0LL || true) {
+ /* We are already a head of time. Respond and issue a sync. */
+ SYNCD(printf("%s: sync resp %lu\n", s->prefix, pkt->sync.timestamp));
+ rp_write(s, (void *) &s->sync.rsp, enclen);
+ return;
+ }
+
+ SYNCD(printf("%s: delayed sync resp - start diff=%ld (ts=%lu clk=%lu)\n",
+ s->prefix, pkt->sync.timestamp - clk, pkt->sync.timestamp, clk));
+
+ ptimer_transaction_begin(s->sync.ptimer_resp);
+ ptimer_set_limit(s->sync.ptimer_resp, diff, 1);
+ ptimer_run(s->sync.ptimer_resp, 1);
+ s->sync.resp_timer_enabled = true;
+ ptimer_transaction_commit(s->sync.ptimer_resp);
+}
+
static void rp_say_hello(RemotePort *s)
{
struct rp_pkt_hello pkt;
@@ -226,6 +295,56 @@ static void rp_say_hello(RemotePort *s)
}
}
+static void rp_say_sync(RemotePort *s, int64_t clk)
+{
+ struct rp_pkt_sync pkt;
+ size_t len;
+
+ len = rp_encode_sync(s->current_id++, 0, &pkt, clk);
+ rp_write(s, (void *) &pkt, len);
+}
+
+static void syncresp_timer_hit(void *opaque)
+{
+ RemotePort *s = REMOTE_PORT(opaque);
+
+ s->sync.resp_timer_enabled = false;
+ SYNCD(printf("%s: delayed sync response - send\n", s->prefix));
+ rp_write(s, (void *) &s->sync.rsp, sizeof s->sync.rsp.sync);
+ memset(&s->sync.rsp, 0, sizeof s->sync.rsp);
+}
+
+static void sync_timer_hit(void *opaque)
+{
+ RemotePort *s = REMOTE_PORT(opaque);
+ int64_t clk;
+ RemotePortDynPkt rsp;
+
+ clk = rp_normalized_vmclk(s);
+ if (s->sync.resp_timer_enabled) {
+ SYNCD(printf("%s: sync while delaying a resp! clk=%lu\n",
+ s->prefix, clk));
+ s->sync.need_sync = true;
+ rp_restart_sync_timer_bare(s);
+ return;
+ }
+
+ /* Sync. */
+ s->doing_sync = true;
+ s->sync.need_sync = false;
+ qemu_mutex_lock(&s->rsp_mutex);
+ /* Send the sync. */
+ rp_say_sync(s, clk);
+
+ SYNCD(printf("%s: syncing wait for resp %lu\n", s->prefix, clk));
+ rsp = rp_wait_resp(s);
+ rp_dpkt_invalidate(&rsp);
+ qemu_mutex_unlock(&s->rsp_mutex);
+ s->doing_sync = false;
+
+ rp_restart_sync_timer_bare(s);
+}
+
static char *rp_sanitize_prefix(RemotePort *s)
{
char *sanitized_name;
@@ -318,7 +437,9 @@ void rp_process(RemotePort *s)
}
switch (pkt->hdr.cmd) {
- /* TBD */
+ case RP_CMD_sync:
+ rp_cmd_sync(s, pkt);
+ break;
default:
assert(actioned);
}
@@ -408,6 +529,33 @@ static void rp_pt_handover_pkt(RemotePort *s, RemotePortDynPkt *dpkt)
} while (full);
}
+static bool rp_pt_cmd_sync(RemotePort *s, struct rp_pkt *pkt)
+{
+ size_t enclen;
+ int64_t clk;
+ int64_t diff = 0;
+ struct rp_pkt rsp;
+
+ assert(!(pkt->hdr.flags & RP_PKT_FLAGS_response));
+
+ if (use_icount) {
+ clk = rp_normalized_vmclk(s);
+ diff = pkt->sync.timestamp - clk;
+ }
+ enclen = rp_encode_sync_resp(pkt->hdr.id, pkt->hdr.dev, &rsp.sync,
+ pkt->sync.timestamp);
+ assert(enclen == sizeof rsp.sync);
+
+ if (!use_icount || diff < s->sync.quantum) {
+ /* We are still OK. */
+ rp_write(s, (void *) &rsp, enclen);
+ return true;
+ }
+
+ /* We need IO or CPU thread sync. */
+ return false;
+}
+
static bool rp_pt_process_pkt(RemotePort *s, RemotePortDynPkt *dpkt)
{
struct rp_pkt *pkt = dpkt->pkt;
@@ -462,6 +610,11 @@ static bool rp_pt_process_pkt(RemotePort *s, RemotePortDynPkt *dpkt)
case RP_CMD_hello:
rp_cmd_hello(s, pkt);
break;
+ case RP_CMD_sync:
+ if (rp_pt_cmd_sync(s, pkt)) {
+ return true;
+ }
+ /* Fall-through. */
case RP_CMD_read:
case RP_CMD_write:
case RP_CMD_interrupt:
@@ -560,6 +713,7 @@ static void rp_reset(DeviceState *dev)
qemu_thread_create(&s->thread, "remote-port", rp_protocol_thread, s,
QEMU_THREAD_JOINABLE);
+ rp_restart_sync_timer(s);
s->reset_done = true;
}
@@ -571,6 +725,8 @@ static void rp_realize(DeviceState *dev, Error **errp)
s->prefix = object_get_canonical_path(OBJECT(dev));
+ s->peer.clk_base = qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL);
+
qemu_mutex_init(&s->write_mutex);
qemu_mutex_init(&s->rsp_mutex);
qemu_cond_init(&s->progress_cond);
@@ -693,6 +849,27 @@ static void rp_realize(DeviceState *dev, Error **errp)
qemu_set_fd_handler(s->event.pipe.read, rp_event_read, NULL, s);
#endif
+
+ /*
+ * Pick up the quantum from the local property setup.
+ * After config negotiation with the peer, sync.quantum value might
+ * change.
+ */
+ s->sync.quantum = s->peer.local_cfg.quantum;
+
+ s->sync.ptimer = ptimer_init(sync_timer_hit, s, PTIMER_POLICY_LEGACY);
+ s->sync.ptimer_resp = ptimer_init(syncresp_timer_hit, s,
+ PTIMER_POLICY_LEGACY);
+
+ /* The Sync-quantum is expressed in nano-seconds. */
+ ptimer_transaction_begin(s->sync.ptimer);
+ ptimer_set_freq(s->sync.ptimer, 1000 * 1000 * 1000);
+ ptimer_transaction_commit(s->sync.ptimer);
+
+ ptimer_transaction_begin(s->sync.ptimer_resp);
+ ptimer_set_freq(s->sync.ptimer_resp, 1000 * 1000 * 1000);
+ ptimer_transaction_commit(s->sync.ptimer_resp);
+
qemu_sem_init(&s->rx_queue.sem, ARRAY_SIZE(s->rx_queue.pkt) - 1);
}
@@ -727,6 +904,9 @@ static Property rp_properties[] = {
DEFINE_PROP_CHR("chardev", RemotePort, chr),
DEFINE_PROP_STRING("chardesc", RemotePort, chardesc),
DEFINE_PROP_STRING("chrdev-id", RemotePort, chrdev_id),
+ DEFINE_PROP_BOOL("sync", RemotePort, do_sync, false),
+ DEFINE_PROP_UINT64("sync-quantum", RemotePort, peer.local_cfg.quantum,
+ 1000000),
};
static void rp_prop_allow_set_link(const Object *obj, const char *name,
diff --git a/include/hw/core/remote-port.h b/include/hw/core/remote-port.h
index c1b21eb573..1d8b64925a 100644
--- a/include/hw/core/remote-port.h
+++ b/include/hw/core/remote-port.h
@@ -15,6 +15,7 @@
#include "chardev/char.h"
#include "chardev/char-fe.h"
#include "qobject/qdict.h"
+#include "hw/core/ptimer.h"
#define TYPE_REMOTE_PORT_DEVICE "remote-port-device"
@@ -73,6 +74,8 @@ struct RemotePort {
} event;
Chardev *chrdev;
CharFrontend chr;
+ bool do_sync;
+ bool doing_sync;
bool finalizing;
/* To serialize writes to fd. */
QemuMutex write_mutex;
@@ -81,6 +84,15 @@ struct RemotePort {
char *chrdev_id;
struct rp_peer_state peer;
+ struct {
+ ptimer_state *ptimer;
+ ptimer_state *ptimer_resp;
+ bool resp_timer_enabled;
+ bool need_sync;
+ struct rp_pkt rsp;
+ uint64_t quantum;
+ } sync;
+
QemuMutex rsp_mutex;
QemuCond progress_cond;
@@ -131,6 +143,9 @@ RemotePortDynPkt rp_wait_resp(RemotePort *s);
RemotePortRespSlot *rp_dev_wait_resp(RemotePort *s, uint32_t dev, uint32_t id);
RemotePortRespSlot *rp_dev_timed_wait_resp(RemotePort *s, uint32_t dev,
uint32_t id, int timems);
+void rp_restart_sync_timer(RemotePort *s);
+
+int64_t rp_normalized_vmclk(RemotePort *s);
void rp_process(RemotePort *s);
--
2.43.0
^ permalink raw reply related [flat|nested] 41+ messages in thread* [PATCH 13/29] system/memory: Introduce unified MemoryTransaction and .access callback
2026-02-05 19:57 [PATCH 00/29] hw/core: Introduce Remote Port Co-simulation Protocol Ruslan Ruslichenko
` (11 preceding siblings ...)
2026-02-05 19:58 ` [PATCH 12/29] hw/core: Implement Remote Port time synchronization Ruslan Ruslichenko
@ 2026-02-05 19:58 ` Ruslan Ruslichenko
2026-02-05 19:58 ` [PATCH 14/29] hw/core: Add Remote Port Memory Master object skeleton Ruslan Ruslichenko
` (15 subsequent siblings)
28 siblings, 0 replies; 41+ messages in thread
From: Ruslan Ruslichenko @ 2026-02-05 19:58 UTC (permalink / raw)
To: qemu-devel
Cc: qemu-arm, peter.maydell, artem_mygaiev, volodymyr_babchuk,
takahiro.nakata.wr, Edgar E . Iglesias, francisco.iglesias,
Ruslan_Ruslichenko, Paolo Bonzini, Peter Xu,
Philippe Mathieu-Daudé, Edgar E . Iglesias
From: Ruslan Ruslichenko <Ruslan_Ruslichenko@epam.com>
Introduce a unified 'MemoryTransaction' structure to encapsulate all
parameters of a memory access, including address, data, size, and
attributes.
The logic in memory_region_dispatch_read/write is updated to prioritize
the new 'access' callback when available.
This change simplifies the integration of transaction-based subsystems,
such as the Remote Port, by providing a standard container for memory
access metadata.
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>
---
include/system/memory.h | 23 ++++++++++++++
system/memory.c | 66 +++++++++++++++++++++++++++++++++++++++--
2 files changed, 87 insertions(+), 2 deletions(-)
diff --git a/include/system/memory.h b/include/system/memory.h
index 0562af3136..e1fb82608d 100644
--- a/include/system/memory.h
+++ b/include/system/memory.h
@@ -84,6 +84,28 @@ extern unsigned int global_dirty_tracking;
typedef struct MemoryRegionOps MemoryRegionOps;
+typedef struct MemoryTransaction {
+ union {
+ /*
+ * Data is passed by values up to 64bit sizes. Beyond
+ * that, a pointer is passed in p8.
+ *
+ * Note that p8 has no alignment restrictions.
+ */
+ uint8_t *p8;
+ uint64_t u64;
+ uint32_t u32;
+ uint16_t u16;
+ uint8_t u8;
+ } data;
+ bool rw;
+ hwaddr addr;
+ unsigned int size;
+ MemTxAttrs attr;
+ void *opaque;
+} MemoryTransaction;
+
+
struct ReservedRegion {
Range range;
unsigned type;
@@ -298,6 +320,7 @@ static inline void iommu_notifier_init(IOMMUNotifier *n, IOMMUNotify fn,
* Memory region callbacks
*/
struct MemoryRegionOps {
+ MemTxResult (*access)(MemoryTransaction *tr);
/* Read from the memory region. @addr is relative to @mr; @size is
* in bytes. */
uint64_t (*read)(void *opaque,
diff --git a/system/memory.c b/system/memory.c
index 0b1462f028..4f62796cd3 100644
--- a/system/memory.c
+++ b/system/memory.c
@@ -431,6 +431,31 @@ static int get_cpu_index(void)
return -1;
}
+static MemTxResult memory_region_read_accessor_attr(MemoryRegion *mr,
+ hwaddr addr,
+ uint64_t *value,
+ unsigned size,
+ signed shift,
+ uint64_t mask,
+ MemTxAttrs attrs)
+{
+ MemoryTransaction tr = { { 0 } };
+ MemTxResult ret;
+
+ if (mr->flush_coalesced_mmio) {
+ qemu_flush_coalesced_mmio_buffer();
+ }
+
+ tr.opaque = mr->opaque;
+ tr.addr = addr;
+ tr.size = size;
+ tr.attr = attrs;
+ ret = mr->ops->access(&tr);
+ *value |= (tr.data.u64 & mask) << shift;
+
+ return ret;
+}
+
static MemTxResult memory_region_read_accessor(MemoryRegion *mr,
hwaddr addr,
uint64_t *value,
@@ -476,6 +501,31 @@ static MemTxResult memory_region_read_with_attrs_accessor(MemoryRegion *mr,
return r;
}
+static MemTxResult memory_region_write_accessor_attr(MemoryRegion *mr,
+ hwaddr addr,
+ uint64_t *value,
+ unsigned size,
+ signed shift,
+ uint64_t mask,
+ MemTxAttrs attrs)
+{
+ MemoryTransaction tr = { { 0 } };
+
+ if (mr->flush_coalesced_mmio) {
+ qemu_flush_coalesced_mmio_buffer();
+ }
+
+ tr.opaque = mr->opaque;
+ tr.rw = true;
+ tr.addr = addr;
+ tr.size = size;
+ tr.attr = attrs;
+ tr.data.u64 = (*value >> shift) & mask;
+ trace_memory_region_ops_write(get_cpu_index(), mr, tr.addr, tr.data.u64,
+ tr.size, memory_region_name(mr));
+ return mr->ops->access(&tr);
+}
+
static MemTxResult memory_region_write_accessor(MemoryRegion *mr,
hwaddr addr,
uint64_t *value,
@@ -1603,7 +1653,13 @@ static MemTxResult memory_region_dispatch_read1(MemoryRegion *mr,
{
*pval = 0;
- if (mr->ops->read) {
+ if (mr->ops->access) {
+ return access_with_adjusted_size(addr, pval, size,
+ mr->ops->impl.min_access_size,
+ mr->ops->impl.max_access_size,
+ memory_region_read_accessor_attr,
+ mr, attrs);
+ } else if (mr->ops->read) {
return access_with_adjusted_size(addr, pval, size,
mr->ops->impl.min_access_size,
mr->ops->impl.max_access_size,
@@ -1698,7 +1754,13 @@ MemTxResult memory_region_dispatch_write(MemoryRegion *mr,
return MEMTX_OK;
}
- if (mr->ops->write) {
+ if (mr->ops->access) {
+ return access_with_adjusted_size(addr, &data, size,
+ mr->ops->impl.min_access_size,
+ mr->ops->impl.max_access_size,
+ memory_region_write_accessor_attr,
+ mr, attrs);
+ } else if (mr->ops->write) {
return access_with_adjusted_size(addr, &data, size,
mr->ops->impl.min_access_size,
mr->ops->impl.max_access_size,
--
2.43.0
^ permalink raw reply related [flat|nested] 41+ messages in thread* [PATCH 14/29] hw/core: Add Remote Port Memory Master object skeleton
2026-02-05 19:57 [PATCH 00/29] hw/core: Introduce Remote Port Co-simulation Protocol Ruslan Ruslichenko
` (12 preceding siblings ...)
2026-02-05 19:58 ` [PATCH 13/29] system/memory: Introduce unified MemoryTransaction and .access callback Ruslan Ruslichenko
@ 2026-02-05 19:58 ` Ruslan Ruslichenko
2026-02-05 19:58 ` [PATCH 15/29] hw/core: Implement Remote Port Memory Master bus transactions Ruslan Ruslichenko
` (14 subsequent siblings)
28 siblings, 0 replies; 41+ messages in thread
From: Ruslan Ruslichenko @ 2026-02-05 19:58 UTC (permalink / raw)
To: qemu-devel
Cc: qemu-arm, peter.maydell, artem_mygaiev, volodymyr_babchuk,
takahiro.nakata.wr, Edgar E . Iglesias, francisco.iglesias,
Ruslan_Ruslichenko, Edgar E . Iglesias
From: Ruslan Ruslichenko <Ruslan_Ruslichenko@epam.com>
Introduce 'remote-port-memory-master' device skeleton.
This device exposes a MemoryRegion within QEMU that represents
resources located in the external simulator. When implemented
fully, read/write accesses to the region will be encapsulated
into Remote Port packets and transmitted to the peer.
This enables QEMU to map remote peripherals or memory into
its own address space.
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-memory-master.c | 151 ++++++++++++++++++++
hw/core/remote-port.c | 5 +
include/hw/core/remote-port-memory-master.h | 59 ++++++++
include/hw/core/remote-port.h | 2 +
4 files changed, 217 insertions(+)
create mode 100644 hw/core/remote-port-memory-master.c
create mode 100644 include/hw/core/remote-port-memory-master.h
diff --git a/hw/core/remote-port-memory-master.c b/hw/core/remote-port-memory-master.c
new file mode 100644
index 0000000000..d714f66ddf
--- /dev/null
+++ b/hw/core/remote-port-memory-master.c
@@ -0,0 +1,151 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * QEMU remote port memory master.
+ *
+ * Copyright (c) 2014 Xilinx Inc
+ * Written by Edgar E. Iglesias <edgar.iglesias@xilinx.com>
+ *
+ * This code is licensed under the GNU GPL.
+ */
+
+#include "qemu/osdep.h"
+#include "system/system.h"
+#include "qemu/log.h"
+#include "qapi/qmp/qerror.h"
+#include "qapi/error.h"
+#include "hw/core/sysbus.h"
+#include "migration/vmstate.h"
+#include "hw/core/qdev-properties.h"
+#include "trace.h"
+
+#include "hw/core/remote-port-proto.h"
+#include "hw/core/remote-port.h"
+#include "hw/core/remote-port-memory-master.h"
+
+#ifndef REMOTE_PORT_ERR_DEBUG
+#define REMOTE_PORT_DEBUG_LEVEL 0
+#else
+#define REMOTE_PORT_DEBUG_LEVEL 1
+#endif
+
+#define DB_PRINT_L(level, ...) do { \
+ if (REMOTE_PORT_DEBUG_LEVEL > level) { \
+ fprintf(stderr, ": %s: ", __func__); \
+ fprintf(stderr, ## __VA_ARGS__); \
+ } \
+} while (0)
+
+#define REMOTE_PORT_MEMORY_MASTER_PARENT_CLASS \
+ object_class_get_parent( \
+ object_class_by_name(TYPE_REMOTE_PORT_MEMORY_MASTER))
+
+#define RP_MAX_ACCESS_SIZE 4096
+
+static MemTxResult rp_access(MemoryTransaction *tr)
+{
+ /* TBD */
+ return MEMTX_OK;
+}
+
+static const MemoryRegionOps rp_ops_template = {
+ .access = rp_access,
+ .valid.max_access_size = RP_MAX_ACCESS_SIZE,
+ .valid.unaligned = true,
+ .impl.max_access_size = RP_MAX_ACCESS_SIZE,
+ .impl.unaligned = false,
+ .endianness = DEVICE_LITTLE_ENDIAN,
+};
+
+static void rp_memory_master_realize(DeviceState *dev, Error **errp)
+{
+ RemotePortMemoryMaster *s = REMOTE_PORT_MEMORY_MASTER(dev);
+ int i;
+
+ /* Sanity check max access size. */
+ if (s->max_access_size > RP_MAX_ACCESS_SIZE) {
+ error_setg(errp, "%s: max-access-size %d too large! MAX is %d",
+ TYPE_REMOTE_PORT_MEMORY_MASTER, s->max_access_size,
+ RP_MAX_ACCESS_SIZE);
+ return;
+ }
+
+ if (s->max_access_size < 4) {
+ error_setg(errp, "%s: max-access-size %d too small! MIN is 4",
+ TYPE_REMOTE_PORT_MEMORY_MASTER, s->max_access_size);
+ return;
+ }
+
+ assert(s->rp);
+ s->peer = rp_get_peer(s->rp);
+
+ /* Create a single static region if configuration says so. */
+ if (s->map_num) {
+ /* Initialize rp_ops from template. */
+ s->rp_ops = g_malloc(sizeof *s->rp_ops);
+ memcpy(s->rp_ops, &rp_ops_template, sizeof *s->rp_ops);
+ s->rp_ops->valid.max_access_size = s->max_access_size;
+ s->rp_ops->impl.max_access_size = s->max_access_size;
+
+ s->mmaps = g_new0(typeof(*s->mmaps), s->map_num);
+ for (i = 0; i < s->map_num; ++i) {
+ char *name = g_strdup_printf("rp-%d", i);
+
+ s->mmaps[i].offset = s->map_offset;
+ memory_region_init_io(&s->mmaps[i].iomem, OBJECT(dev), s->rp_ops,
+ &s->mmaps[i], name, s->map_size);
+ sysbus_init_mmio(SYS_BUS_DEVICE(dev), &s->mmaps[i].iomem);
+ s->mmaps[i].parent = s;
+ g_free(name);
+ }
+ }
+}
+
+static void rp_prop_allow_set_link(const Object *obj, const char *name,
+ Object *val, Error **errp)
+{
+}
+
+static void rp_memory_master_init(Object *obj)
+{
+ RemotePortMemoryMaster *rpms = REMOTE_PORT_MEMORY_MASTER(obj);
+ object_property_add_link(obj, "rp-adaptor0", "remote-port",
+ (Object **)&rpms->rp,
+ rp_prop_allow_set_link,
+ OBJ_PROP_LINK_STRONG);
+}
+
+static Property rp_properties[] = {
+ DEFINE_PROP_UINT32("map-num", RemotePortMemoryMaster, map_num, 0),
+ DEFINE_PROP_UINT64("map-offset", RemotePortMemoryMaster, map_offset, 0),
+ DEFINE_PROP_UINT64("map-size", RemotePortMemoryMaster, map_size, 0),
+ DEFINE_PROP_UINT32("rp-chan0", RemotePortMemoryMaster, rp_dev, 0),
+ DEFINE_PROP_BOOL("relative", RemotePortMemoryMaster, relative, false),
+ DEFINE_PROP_UINT32("max-access-size", RemotePortMemoryMaster,
+ max_access_size, RP_MAX_ACCESS_SIZE),
+};
+
+static void rp_memory_master_class_init(ObjectClass *oc, const void *data)
+{
+ DeviceClass *dc = DEVICE_CLASS(oc);
+ device_class_set_props_n(dc, rp_properties, ARRAY_SIZE(rp_properties));
+ dc->realize = rp_memory_master_realize;
+}
+
+static const TypeInfo rp_info = {
+ .name = TYPE_REMOTE_PORT_MEMORY_MASTER,
+ .parent = TYPE_SYS_BUS_DEVICE,
+ .instance_size = sizeof(RemotePortMemoryMaster),
+ .instance_init = rp_memory_master_init,
+ .class_init = rp_memory_master_class_init,
+ .interfaces = (InterfaceInfo[]) {
+ { TYPE_REMOTE_PORT_DEVICE },
+ { },
+ },
+};
+
+static void rp_register_types(void)
+{
+ type_register_static(&rp_info);
+}
+
+type_init(rp_register_types)
diff --git a/hw/core/remote-port.c b/hw/core/remote-port.c
index 6a1933a5a6..c783a12153 100644
--- a/hw/core/remote-port.c
+++ b/hw/core/remote-port.c
@@ -938,6 +938,11 @@ static void rp_init(Object *obj)
}
}
+struct rp_peer_state *rp_get_peer(RemotePort *s)
+{
+ return &s->peer;
+}
+
static void rp_class_init(ObjectClass *klass, const void *data)
{
DeviceClass *dc = DEVICE_CLASS(klass);
diff --git a/include/hw/core/remote-port-memory-master.h b/include/hw/core/remote-port-memory-master.h
new file mode 100644
index 0000000000..70f7c38c35
--- /dev/null
+++ b/include/hw/core/remote-port-memory-master.h
@@ -0,0 +1,59 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * QEMU remote port memory master. Read and write transactions
+ * recieved from QEMU are transmitted over remote-port.
+ *
+ * Copyright (c) 2020 Xilinx Inc
+ * Written by Edgar E. Iglesias <edgar.iglesias@xilinx.com>
+ *
+ * This code is licensed under the GNU GPL.
+ */
+#ifndef REMOTE_PORT_MEMORY_MASTER_H
+#define REMOTE_PORT_MEMORY_MASTER_H
+
+#include "hw/core/remote-port.h"
+
+#define TYPE_REMOTE_PORT_MEMORY_MASTER "remote-port-memory-master"
+#define REMOTE_PORT_MEMORY_MASTER(obj) \
+ OBJECT_CHECK(RemotePortMemoryMaster, (obj), \
+ TYPE_REMOTE_PORT_MEMORY_MASTER)
+
+typedef struct RemotePortMemoryMaster RemotePortMemoryMaster;
+
+typedef struct RemotePortMap {
+ void *parent;
+ MemoryRegion iomem;
+ uint32_t rp_dev;
+ uint64_t offset;
+} RemotePortMap;
+
+struct RemotePortMemoryMaster {
+ /* private */
+ SysBusDevice parent;
+
+ MemoryRegionOps *rp_ops;
+ RemotePortMap *mmaps;
+
+ /* public */
+ uint32_t map_num;
+ uint64_t map_offset;
+ uint64_t map_size;
+ uint32_t rp_dev;
+ bool relative;
+ uint32_t max_access_size;
+ struct RemotePort *rp;
+ struct rp_peer_state *peer;
+ int rp_timeout;
+};
+
+MemTxResult rp_mm_access(RemotePort *rp, uint32_t rp_dev,
+ struct rp_peer_state *peer,
+ MemoryTransaction *tr,
+ bool relative, uint64_t offset);
+
+MemTxResult rp_mm_access_with_def_attr(RemotePort *rp, uint32_t rp_dev,
+ struct rp_peer_state *peer,
+ MemoryTransaction *tr,
+ bool relative, uint64_t offset,
+ uint32_t def_attr);
+#endif
diff --git a/include/hw/core/remote-port.h b/include/hw/core/remote-port.h
index 1d8b64925a..e5dfaf7c3b 100644
--- a/include/hw/core/remote-port.h
+++ b/include/hw/core/remote-port.h
@@ -151,4 +151,6 @@ void rp_process(RemotePort *s);
ssize_t rp_write(RemotePort *s, const void *buf, size_t count);
+struct rp_peer_state *rp_get_peer(RemotePort *s);
+
#endif
--
2.43.0
^ permalink raw reply related [flat|nested] 41+ messages in thread* [PATCH 15/29] hw/core: Implement Remote Port Memory Master bus transactions
2026-02-05 19:57 [PATCH 00/29] hw/core: Introduce Remote Port Co-simulation Protocol Ruslan Ruslichenko
` (13 preceding siblings ...)
2026-02-05 19:58 ` [PATCH 14/29] hw/core: Add Remote Port Memory Master object skeleton Ruslan Ruslichenko
@ 2026-02-05 19:58 ` Ruslan Ruslichenko
2026-02-05 19:58 ` [PATCH 16/29] system/physmem: Add ats_do_translate helper Ruslan Ruslichenko
` (13 subsequent siblings)
28 siblings, 0 replies; 41+ messages in thread
From: Ruslan Ruslichenko @ 2026-02-05 19:58 UTC (permalink / raw)
To: qemu-devel
Cc: qemu-arm, peter.maydell, artem_mygaiev, volodymyr_babchuk,
takahiro.nakata.wr, Edgar E . Iglesias, francisco.iglesias,
Ruslan_Ruslichenko, Edgar E . Iglesias
From: Ruslan Ruslichenko <Ruslan_Ruslichenko@epam.com>
Implement the core access handlers for the Remote Port Memory
Master device, which enables QEMU to initiate memory-mapped
transaction to remote peers.
The patch adds support for:
- Translation of QEMU MemoryTransactions into Remote Port
read/write packets
- Support for propagating of transaction attributes
(Secure, MasterID)
- Synchronous response waiting with optional timeout
- Add trace events for monitoring TX/RX bus transactions
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-memory-master.c | 167 +++++++++++++++++++++++++++-
hw/core/remote-port.c | 5 +
hw/core/trace-events | 4 +
include/hw/core/remote-port.h | 11 ++
4 files changed, 185 insertions(+), 2 deletions(-)
diff --git a/hw/core/remote-port-memory-master.c b/hw/core/remote-port-memory-master.c
index d714f66ddf..4a6c43423d 100644
--- a/hw/core/remote-port-memory-master.c
+++ b/hw/core/remote-port-memory-master.c
@@ -41,10 +41,173 @@
#define RP_MAX_ACCESS_SIZE 4096
+static int rp_mm_get_timeout(MemoryTransaction *tr)
+{
+ RemotePortMap *map = tr->opaque;
+ RemotePortMemoryMaster *s;
+
+ if (!map || !map->parent ||
+ !object_dynamic_cast(OBJECT(map->parent),
+ TYPE_REMOTE_PORT_MEMORY_MASTER)) {
+ return 0;
+ }
+ s = REMOTE_PORT_MEMORY_MASTER(map->parent);
+ return s->rp_timeout;
+}
+
+MemTxResult rp_mm_access_with_def_attr(RemotePort *rp, uint32_t rp_dev,
+ struct rp_peer_state *peer,
+ MemoryTransaction *tr,
+ bool relative, uint64_t offset,
+ uint32_t def_attr)
+{
+ uint64_t addr = tr->addr;
+ RemotePortRespSlot *rsp_slot;
+ RemotePortDynPkt *rsp;
+ struct {
+ struct rp_pkt_busaccess_ext_base pkt;
+ uint8_t reserved[RP_MAX_ACCESS_SIZE];
+ } pay;
+ uint8_t *data = rp_busaccess_tx_dataptr(peer, &pay.pkt);
+ struct rp_encode_busaccess_in in = {0};
+ int i;
+ int len;
+ int rp_timeout = rp_mm_get_timeout(tr);
+ MemTxResult ret;
+
+ if (rp_timeout) {
+ return MEMTX_ERROR;
+ }
+ DB_PRINT_L(0, "addr: %" HWADDR_PRIx " data: %" PRIx64 "\n",
+ addr, tr->data.u64);
+
+ if (tr->rw) {
+ /* Data up to 8 bytes is passed as values. */
+ if (tr->size <= 8) {
+ for (i = 0; i < tr->size; i++) {
+ data[i] = tr->data.u64 >> (i * 8);
+ }
+ } else {
+ memcpy(data, tr->data.p8, tr->size);
+ }
+ }
+
+ addr += relative ? 0 : offset;
+
+ in.cmd = tr->rw ? RP_CMD_write : RP_CMD_read;
+ in.id = rp_new_id(rp);
+ in.dev = rp_dev;
+ in.clk = rp_normalized_vmclk(rp);
+ in.master_id = tr->attr.requester_id;
+ in.addr = addr;
+ in.attr = def_attr;
+ in.attr |= tr->attr.secure ? RP_BUS_ATTR_SECURE : 0;
+ in.size = tr->size;
+ in.stream_width = tr->size;
+ len = rp_encode_busaccess(peer, &pay.pkt, &in);
+ len += tr->rw ? tr->size : 0;
+
+ trace_remote_port_memory_master_tx_busaccess(rp_cmd_to_string(in.cmd),
+ in.id, in.flags, in.dev, in.addr, in.size, in.attr);
+
+ rp_rsp_mutex_lock(rp);
+ rp_write(rp, (void *) &pay, len);
+
+ if (!rp_timeout) {
+ rsp_slot = rp_dev_wait_resp(rp, in.dev, in.id);
+ } else {
+ rsp_slot = rp_dev_timed_wait_resp(rp, in.dev, in.id, rp_timeout);
+ if (rsp_slot->valid == false) {
+ /*
+ * Timeout error
+ */
+ rp_rsp_mutex_unlock(rp);
+ return MEMTX_ERROR;
+ }
+ }
+ rsp = &rsp_slot->rsp;
+
+ /* We dont support out of order answers yet. */
+ assert(rsp->pkt->hdr.id == in.id);
+
+ switch (rp_get_busaccess_response(rsp->pkt)) {
+ case RP_RESP_OK:
+ ret = MEMTX_OK;
+ break;
+ case RP_RESP_ADDR_ERROR:
+ ret = MEMTX_DECODE_ERROR;
+ break;
+ default:
+ ret = MEMTX_ERROR;
+ break;
+ }
+
+ if (ret == MEMTX_OK && !tr->rw) {
+ data = rp_busaccess_rx_dataptr(peer, &rsp->pkt->busaccess_ext_base);
+ /* Data up to 8 bytes is return as values. */
+ if (tr->size <= 8) {
+ for (i = 0; i < tr->size; i++) {
+ tr->data.u64 |= ((uint64_t) data[i]) << (i * 8);
+ }
+ } else {
+ memcpy(tr->data.p8, data, tr->size);
+ }
+ }
+
+ trace_remote_port_memory_master_rx_busaccess(
+ rp_cmd_to_string(rsp->pkt->hdr.cmd), rsp->pkt->hdr.id,
+ rsp->pkt->hdr.flags, rsp->pkt->hdr.dev, rsp->pkt->busaccess.addr,
+ rsp->pkt->busaccess.len, rsp->pkt->busaccess.attributes);
+
+ if (rp_timeout) {
+ for (i = 0; i < ARRAY_SIZE(rp->dev_state[rp_dev].rsp_queue); i++) {
+ if (rp->dev_state[rp_dev].rsp_queue[i].used &&
+ rp->dev_state[rp_dev].rsp_queue[i].valid) {
+ rp_resp_slot_done(rp, &rp->dev_state[rp_dev].rsp_queue[i]);
+ }
+ }
+ } else {
+ rp_resp_slot_done(rp, rsp_slot);
+ }
+ rp_rsp_mutex_unlock(rp);
+
+ /*
+ * For strongly ordered or transactions that don't allow Early Acking,
+ * we need to drain the pending RP processing queue here. This is
+ * because RP handles responses in parallel with normal requests so
+ * they may get reordered. This becomes visible for example with reads
+ * to read-to-clear registers that clear interrupts. Even though the
+ * lowering of the interrupt-wires arrives to us before the read-resp,
+ * we may handle the response before the wire update, resulting in
+ * spurious interrupts.
+ *
+ * This has some room for optimization but for now we use the big hammer
+ * and drain the entire qeueue.
+ */
+ rp_process(rp);
+
+ /* Reads are sync-points, roll the sync timer. */
+ rp_restart_sync_timer(rp);
+ DB_PRINT_L(1, "\n");
+ return ret;
+}
+
+MemTxResult rp_mm_access(RemotePort *rp, uint32_t rp_dev,
+ struct rp_peer_state *peer,
+ MemoryTransaction *tr,
+ bool relative, uint64_t offset)
+{
+ return rp_mm_access_with_def_attr(rp, rp_dev, peer, tr, relative, offset,
+ 0);
+}
+
static MemTxResult rp_access(MemoryTransaction *tr)
{
- /* TBD */
- return MEMTX_OK;
+ RemotePortMap *map = tr->opaque;
+ RemotePortMemoryMaster *s = map->parent;
+
+ return rp_mm_access(s->rp, s->rp_dev, s->peer, tr, s->relative,
+ map->offset);
}
static const MemoryRegionOps rp_ops_template = {
diff --git a/hw/core/remote-port.c b/hw/core/remote-port.c
index c783a12153..41ac1b46dc 100644
--- a/hw/core/remote-port.c
+++ b/hw/core/remote-port.c
@@ -62,6 +62,11 @@ static void rp_pkt_dump(const char *prefix, const char *buf, size_t len)
qemu_hexdump(stdout, prefix, buf, len);
}
+uint32_t rp_new_id(RemotePort *s)
+{
+ return qatomic_fetch_inc(&s->current_id);
+}
+
void rp_rsp_mutex_lock(RemotePort *s)
{
qemu_mutex_lock(&s->rsp_mutex);
diff --git a/hw/core/trace-events b/hw/core/trace-events
index 2cf085ac66..6332002d65 100644
--- a/hw/core/trace-events
+++ b/hw/core/trace-events
@@ -32,3 +32,7 @@ clock_set_mul_div(const char *clk, uint32_t oldmul, uint32_t mul, uint32_t olddi
# cpu-common.c
cpu_reset(int cpu_index) "%d"
+
+# remote-port-memory-master.c
+remote_port_memory_master_tx_busaccess(const char *cmd, uint32_t id, uint32_t flags, uint32_t dev, uint64_t addr, uint32_t len, uint64_t attr) "cmd=%s, id=0x%"PRIx32", flags=0x%"PRIx32", dev=0x%"PRIx32", addr=0x%"PRIx64", len=0x%"PRIx32", attr=0x%"PRIx64
+remote_port_memory_master_rx_busaccess(const char *cmd, uint32_t id, uint32_t flags, uint32_t dev, uint64_t addr, uint32_t len, uint64_t attr) "cmd=%s, id=0x%"PRIx32", flags=0x%"PRIx32", dev=0x%"PRIx32", addr=0x%"PRIx64", len=0x%"PRIx32", attr=0x%"PRIx64
diff --git a/include/hw/core/remote-port.h b/include/hw/core/remote-port.h
index e5dfaf7c3b..172c4b6204 100644
--- a/include/hw/core/remote-port.h
+++ b/include/hw/core/remote-port.h
@@ -153,4 +153,15 @@ ssize_t rp_write(RemotePort *s, const void *buf, size_t count);
struct rp_peer_state *rp_get_peer(RemotePort *s);
+uint32_t rp_new_id(RemotePort *s);
+
+static inline void rp_resp_slot_done(RemotePort *s,
+ RemotePortRespSlot *rsp_slot)
+{
+ rp_dpkt_invalidate(&rsp_slot->rsp);
+ rsp_slot->id = ~0;
+ rsp_slot->used = false;
+ rsp_slot->valid = false;
+}
+
#endif
--
2.43.0
^ permalink raw reply related [flat|nested] 41+ messages in thread* [PATCH 16/29] system/physmem: Add ats_do_translate helper
2026-02-05 19:57 [PATCH 00/29] hw/core: Introduce Remote Port Co-simulation Protocol Ruslan Ruslichenko
` (14 preceding siblings ...)
2026-02-05 19:58 ` [PATCH 15/29] hw/core: Implement Remote Port Memory Master bus transactions Ruslan Ruslichenko
@ 2026-02-05 19:58 ` Ruslan Ruslichenko
2026-02-05 19:58 ` [PATCH 17/29] hw/core: Add Remote Port ATS device skeleton Ruslan Ruslichenko
` (12 subsequent siblings)
28 siblings, 0 replies; 41+ messages in thread
From: Ruslan Ruslichenko @ 2026-02-05 19:58 UTC (permalink / raw)
To: qemu-devel
Cc: qemu-arm, peter.maydell, artem_mygaiev, volodymyr_babchuk,
takahiro.nakata.wr, Edgar E . Iglesias, francisco.iglesias,
Ruslan_Ruslichenko, Paolo Bonzini, Peter Xu,
Philippe Mathieu-Daudé, Edgar E . Iglesias
From: Ruslan Ruslichenko <Ruslan_Ruslichenko@epam.com>
Add 'ats_do_translate' method to enable manual IOMMU address
translation.
This allows the Remote Port subsystem to resolve guest virtual
addresses to physical addresses by querying the QEMU IOMMU
translation pipeline. This is required for the Remote Port
subsystem to support translation requests from external masters
in a co-simulation environment.
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>
---
include/system/memory.h | 8 +++++++
system/physmem.c | 53 +++++++++++++++++++++++++++++++++++++++++
2 files changed, 61 insertions(+)
diff --git a/include/system/memory.h b/include/system/memory.h
index e1fb82608d..c787c544ce 100644
--- a/include/system/memory.h
+++ b/include/system/memory.h
@@ -2895,6 +2895,14 @@ static inline MemoryRegion *address_space_translate(AddressSpace *as,
addr, xlat, len, is_write, attrs);
}
+MemoryRegion *ats_do_translate(AddressSpace *as,
+ hwaddr addr,
+ hwaddr *xlat,
+ hwaddr *plen_out,
+ AddressSpace **target_as,
+ int *prot,
+ MemTxAttrs attrs);
+
/* address_space_access_valid: check for validity of accessing an address
* space range
*
diff --git a/system/physmem.c b/system/physmem.c
index 2fb0c25c93..b4c18f3a01 100644
--- a/system/physmem.c
+++ b/system/physmem.c
@@ -527,6 +527,59 @@ static MemoryRegionSection flatview_do_translate(FlatView *fv,
return *section;
}
+/* This function is called from RCU critical section */
+MemoryRegion *ats_do_translate(AddressSpace *as,
+ hwaddr addr,
+ hwaddr *xlat,
+ hwaddr *plen_out,
+ AddressSpace **target_as,
+ int *prot,
+ MemTxAttrs attrs)
+{
+ MemoryRegionSection *section;
+ IOMMUMemoryRegion *iommu_mr;
+ hwaddr plen = (hwaddr)(-1);
+ FlatView *fv = address_space_to_flatview(as);
+ IOMMUMemoryRegionClass *imrc;
+ IOMMUTLBEntry iotlb;
+ int iommu_idx = 0;
+
+ if (!plen_out) {
+ plen_out = &plen;
+ }
+
+ /* This can be MMIO, so setup MMIO bit. */
+ section = address_space_translate_internal(
+ flatview_to_dispatch(fv), addr, xlat,
+ plen_out, true);
+
+ /* Illegal translation */
+ if (section->mr == &io_mem_unassigned) {
+ return NULL;
+ }
+
+ iommu_mr = memory_region_get_iommu(section->mr);
+ if (!iommu_mr) {
+ return NULL;
+ }
+
+ imrc = memory_region_get_iommu_class_nocheck(iommu_mr);
+
+ if (imrc->attrs_to_index) {
+ iommu_idx = imrc->attrs_to_index(iommu_mr, attrs);
+ }
+
+ iotlb = imrc->translate(iommu_mr, addr, IOMMU_RO, iommu_idx);
+
+ *xlat = ((iotlb.translated_addr & ~iotlb.addr_mask)
+ | (addr & iotlb.addr_mask));
+ *plen_out = MIN(*plen_out, (addr | iotlb.addr_mask) - addr + 1);
+ *prot = iotlb.perm;
+ *target_as = iotlb.target_as;
+
+ return section->mr;
+}
+
/* Called from RCU critical section */
IOMMUTLBEntry address_space_get_iotlb_entry(AddressSpace *as, hwaddr addr,
bool is_write, MemTxAttrs attrs)
--
2.43.0
^ permalink raw reply related [flat|nested] 41+ messages in thread* [PATCH 17/29] hw/core: Add Remote Port ATS device skeleton
2026-02-05 19:57 [PATCH 00/29] hw/core: Introduce Remote Port Co-simulation Protocol Ruslan Ruslichenko
` (15 preceding siblings ...)
2026-02-05 19:58 ` [PATCH 16/29] system/physmem: Add ats_do_translate helper Ruslan Ruslichenko
@ 2026-02-05 19:58 ` Ruslan Ruslichenko
2026-02-05 19:58 ` [PATCH 18/29] hw/core: Implement Remote Port ATS logic and cache management Ruslan Ruslichenko
` (11 subsequent siblings)
28 siblings, 0 replies; 41+ messages in thread
From: Ruslan Ruslichenko @ 2026-02-05 19:58 UTC (permalink / raw)
To: qemu-devel
Cc: qemu-arm, peter.maydell, artem_mygaiev, volodymyr_babchuk,
takahiro.nakata.wr, Edgar E . Iglesias, francisco.iglesias,
Ruslan_Ruslichenko, Edgar E . Iglesias
From: Ruslan Ruslichenko <Ruslan_Ruslichenko@epam.com>
Inroduce the remote-port-ats device and remote-port-ats-cache
interface.
This patch provides the QOM definition and basic initialization
logic required for Address Translation Services in Remote Port
system.
Signed-off-by: Francisco Iglesias <francisco.iglesias@amd.com>
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-ats.c | 205 ++++++++++++++++++++++++++++++
include/hw/core/remote-port-ats.h | 75 +++++++++++
2 files changed, 280 insertions(+)
create mode 100644 hw/core/remote-port-ats.c
create mode 100644 include/hw/core/remote-port-ats.h
diff --git a/hw/core/remote-port-ats.c b/hw/core/remote-port-ats.c
new file mode 100644
index 0000000000..c0e8150452
--- /dev/null
+++ b/hw/core/remote-port-ats.c
@@ -0,0 +1,205 @@
+// SPDX-License-Identifier: MIT
+/*
+ * QEMU remote port ATS
+ *
+ * Copyright (c) 2021 Xilinx Inc
+ * Written by Francisco Iglesias <francisco.iglesias@xilinx.com>
+ *
+ * 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.
+ */
+#include "qemu/osdep.h"
+#include "system/system.h"
+#include "system/dma.h"
+#include "qemu/log.h"
+#include "qapi/qmp/qerror.h"
+#include "qapi/error.h"
+#include "hw/core/sysbus.h"
+#include "migration/vmstate.h"
+#include "hw/core/qdev-properties.h"
+#include "trace.h"
+
+#include "hw/core/remote-port-proto.h"
+#include "hw/core/remote-port-ats.h"
+
+typedef struct ATSIOMMUNotifier {
+ IOMMUNotifier n;
+ MemoryRegion *mr;
+ RemotePortATS *rp_ats;
+ int iommu_idx;
+} ATSIOMMUNotifier;
+
+IOMMUTLBEntry *rp_ats_cache_lookup_translation(RemotePortATSCache *cache,
+ hwaddr translated_addr,
+ hwaddr len)
+{
+ RemotePortATSCacheClass *c = REMOTE_PORT_ATS_CACHE_GET_CLASS(cache);
+
+ return c->lookup_translation(cache, translated_addr, len);
+}
+
+static IOMMUTLBEntry *rp_ats_lookup_translation(RemotePortATSCache *cache,
+ hwaddr translated_addr,
+ hwaddr len)
+{
+ /* TBD */
+ return NULL;
+}
+
+static bool ats_translate_address(RemotePortATS *s, struct rp_pkt *pkt,
+ hwaddr *phys_addr, hwaddr *phys_len)
+{
+ /* TBD */
+ return true;
+}
+
+static void rp_ats_req(RemotePortDevice *dev, struct rp_pkt *pkt)
+{
+ RemotePortATS *s = REMOTE_PORT_ATS(dev);
+ size_t pktlen = sizeof(struct rp_pkt_ats);
+ hwaddr phys_addr = 0;
+ hwaddr phys_len = (hwaddr)(-1);
+ uint64_t result;
+ size_t enclen;
+ int64_t delay;
+ int64_t clk;
+
+ assert(!(pkt->hdr.flags & RP_PKT_FLAGS_response));
+
+ rp_dpkt_alloc(&s->rsp, pktlen);
+
+ result = ats_translate_address(s, pkt, &phys_addr, &phys_len) ?
+ RP_ATS_RESULT_ok : RP_ATS_RESULT_error;
+
+ /*
+ * delay here could be set to the annotated cost of doing issuing
+ * these accesses. QEMU doesn't support this kind of annotations
+ * at the moment. So we just clear the delay.
+ */
+ delay = 0;
+ clk = pkt->ats.timestamp + delay;
+
+ enclen = rp_encode_ats_req(pkt->hdr.id, pkt->hdr.dev,
+ &s->rsp.pkt->ats,
+ clk,
+ pkt->ats.attributes,
+ phys_addr,
+ phys_len,
+ result,
+ pkt->hdr.flags | RP_PKT_FLAGS_response);
+ assert(enclen == pktlen);
+
+ rp_write(s->rp, (void *)s->rsp.pkt, enclen);
+}
+
+static void rp_ats_realize(DeviceState *dev, Error **errp)
+{
+ RemotePortATS *s = REMOTE_PORT_ATS(dev);
+
+ s->peer = rp_get_peer(s->rp);
+ address_space_init(&s->as, s->mr ? s->mr : get_system_memory(), "ats-as");
+
+ s->iommu_notifiers = g_array_new(false, true, sizeof(ATSIOMMUNotifier *));
+ s->cache = g_array_new(false, true, sizeof(IOMMUTLBEntry *));
+}
+
+static void rp_prop_allow_set_link(const Object *obj, const char *name,
+ Object *val, Error **errp)
+{
+}
+
+static void rp_ats_init(Object *obj)
+{
+ RemotePortATS *s = REMOTE_PORT_ATS(obj);
+
+ object_property_add_link(obj, "rp-adaptor0", "remote-port",
+ (Object **)&s->rp,
+ rp_prop_allow_set_link,
+ OBJ_PROP_LINK_STRONG);
+ object_property_add_link(obj, "mr", TYPE_MEMORY_REGION,
+ (Object **)&s->mr,
+ qdev_prop_allow_set_link_before_realize,
+ OBJ_PROP_LINK_STRONG);
+}
+
+static void rp_ats_unrealize(DeviceState *dev)
+{
+ RemotePortATS *s = REMOTE_PORT_ATS(dev);
+ ATSIOMMUNotifier *notifier;
+ int i;
+
+ for (i = 0; i < s->iommu_notifiers->len; i++) {
+ notifier = g_array_index(s->iommu_notifiers, ATSIOMMUNotifier *, i);
+ memory_region_unregister_iommu_notifier(notifier->mr, ¬ifier->n);
+ g_free(notifier);
+ }
+ g_array_free(s->iommu_notifiers, true);
+
+ address_space_destroy(&s->as);
+
+ for (i = 0; i < s->cache->len; i++) {
+ IOMMUTLBEntry *tmp = g_array_index(s->cache, IOMMUTLBEntry *, i);
+ g_free(tmp);
+ }
+ g_array_free(s->cache, true);
+}
+
+static Property rp_properties[] = {
+ DEFINE_PROP_UINT32("rp-chan0", RemotePortATS, rp_dev, 0),
+};
+
+static void rp_ats_class_init(ObjectClass *oc, const void *data)
+{
+ RemotePortDeviceClass *rpdc = REMOTE_PORT_DEVICE_CLASS(oc);
+ RemotePortATSCacheClass *atscc = REMOTE_PORT_ATS_CACHE_CLASS(oc);
+ DeviceClass *dc = DEVICE_CLASS(oc);
+
+ device_class_set_props_n(dc, rp_properties, ARRAY_SIZE(rp_properties));
+
+ rpdc->ops[RP_CMD_ats_req] = rp_ats_req;
+ dc->realize = rp_ats_realize;
+ dc->unrealize = rp_ats_unrealize;
+ atscc->lookup_translation = rp_ats_lookup_translation;
+}
+
+static const TypeInfo rp_ats_info = {
+ .name = TYPE_REMOTE_PORT_ATS,
+ .parent = TYPE_DEVICE,
+ .instance_size = sizeof(RemotePortATS),
+ .instance_init = rp_ats_init,
+ .class_init = rp_ats_class_init,
+ .interfaces = (InterfaceInfo[]) {
+ { TYPE_REMOTE_PORT_DEVICE },
+ { TYPE_REMOTE_PORT_ATS_CACHE },
+ { },
+ },
+};
+
+static const TypeInfo rp_ats_cache_info = {
+ .name = TYPE_REMOTE_PORT_ATS_CACHE,
+ .parent = TYPE_INTERFACE,
+ .class_size = sizeof(RemotePortATSCacheClass),
+};
+
+static void rp_register_types(void)
+{
+ type_register_static(&rp_ats_cache_info);
+ type_register_static(&rp_ats_info);
+}
+
+type_init(rp_register_types)
diff --git a/include/hw/core/remote-port-ats.h b/include/hw/core/remote-port-ats.h
new file mode 100644
index 0000000000..8bdae1bce9
--- /dev/null
+++ b/include/hw/core/remote-port-ats.h
@@ -0,0 +1,75 @@
+// SPDX-License-Identifier: MIT
+/*
+ * QEMU remote port ATS
+ *
+ * Copyright (c) 2021 Xilinx Inc
+ * Written by Francisco Iglesias <francisco.iglesias@xilinx.com>
+ *
+ * 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_ATS_H
+#define REMOTE_PORT_ATS_H
+
+#include "hw/core/remote-port.h"
+
+#define TYPE_REMOTE_PORT_ATS "remote-port-ats"
+#define REMOTE_PORT_ATS(obj) \
+ OBJECT_CHECK(RemotePortATS, (obj), TYPE_REMOTE_PORT_ATS)
+
+typedef struct {
+ /* private */
+ SysBusDevice parent;
+
+ /* public */
+ struct RemotePort *rp;
+ struct rp_peer_state *peer;
+ MemoryRegion *mr;
+ AddressSpace as;
+ RemotePortDynPkt rsp;
+ GArray *iommu_notifiers;
+ uint32_t rp_dev;
+ GArray *cache; /* Translation cache */
+} RemotePortATS;
+
+#define TYPE_REMOTE_PORT_ATS_CACHE "remote-port-ats-cache"
+
+#define REMOTE_PORT_ATS_CACHE_CLASS(klass) \
+ OBJECT_CLASS_CHECK(RemotePortATSCacheClass, (klass), \
+ TYPE_REMOTE_PORT_ATS_CACHE)
+#define REMOTE_PORT_ATS_CACHE_GET_CLASS(obj) \
+ OBJECT_GET_CLASS(RemotePortATSCacheClass, (obj), TYPE_REMOTE_PORT_ATS_CACHE)
+#define REMOTE_PORT_ATS_CACHE(obj) \
+ INTERFACE_CHECK(RemotePortATSCache, (obj), TYPE_REMOTE_PORT_ATS_CACHE)
+
+typedef struct RemotePortATSCache {
+ Object Parent;
+} RemotePortATSCache;
+
+typedef struct RemotePortATSCacheClass {
+ InterfaceClass parent;
+
+ IOMMUTLBEntry *(*lookup_translation)(RemotePortATSCache *cache,
+ hwaddr translated_addr,
+ hwaddr len);
+} RemotePortATSCacheClass;
+
+IOMMUTLBEntry *rp_ats_cache_lookup_translation(RemotePortATSCache *cache,
+ hwaddr translated_addr,
+ hwaddr len);
+#endif
--
2.43.0
^ permalink raw reply related [flat|nested] 41+ messages in thread* [PATCH 18/29] hw/core: Implement Remote Port ATS logic and cache management
2026-02-05 19:57 [PATCH 00/29] hw/core: Introduce Remote Port Co-simulation Protocol Ruslan Ruslichenko
` (16 preceding siblings ...)
2026-02-05 19:58 ` [PATCH 17/29] hw/core: Add Remote Port ATS device skeleton Ruslan Ruslichenko
@ 2026-02-05 19:58 ` Ruslan Ruslichenko
2026-02-05 19:58 ` [PATCH 19/29] hw/core: Add Remote Port memory slave device Ruslan Ruslichenko
` (10 subsequent siblings)
28 siblings, 0 replies; 41+ messages in thread
From: Ruslan Ruslichenko @ 2026-02-05 19:58 UTC (permalink / raw)
To: qemu-devel
Cc: qemu-arm, peter.maydell, artem_mygaiev, volodymyr_babchuk,
takahiro.nakata.wr, Edgar E . Iglesias, francisco.iglesias,
Ruslan_Ruslichenko, Edgar E . Iglesias
From: Ruslan Ruslichenko <Ruslan_Ruslichenko@epam.com>
Complete the Remote Port ATS implementation by adding the translation
and cache management logic.
This patch implements:
- The logic for ats_translate_address to resolve addresses via the
QEMU IOMMU translation mechanism.
- A local cache management system to store IOMMU entries for remote
masters.
- Integration with IOMMU notifiers to handle invalidation requests
and maintain coherency with the peer.
Signed-off-by: Francisco Iglesias <francisco.iglesias@amd.com>
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-ats.c | 207 +++++++++++++++++++++++++++++++++++++-
1 file changed, 205 insertions(+), 2 deletions(-)
diff --git a/hw/core/remote-port-ats.c b/hw/core/remote-port-ats.c
index c0e8150452..1f6c8d7410 100644
--- a/hw/core/remote-port-ats.c
+++ b/hw/core/remote-port-ats.c
@@ -57,14 +57,217 @@ static IOMMUTLBEntry *rp_ats_lookup_translation(RemotePortATSCache *cache,
hwaddr translated_addr,
hwaddr len)
{
- /* TBD */
+ RemotePortATS *s = REMOTE_PORT_ATS(cache);
+
+ for (int i = 0; i < s->cache->len; i++) {
+ IOMMUTLBEntry *iotlb = g_array_index(s->cache, IOMMUTLBEntry *, i);
+ hwaddr masked_start = (translated_addr & ~iotlb->addr_mask);
+ hwaddr masked_end = ((translated_addr + len - 1) & ~iotlb->addr_mask);
+
+ if (masked_start == iotlb->translated_addr &&
+ masked_end == iotlb->translated_addr) {
+ return iotlb;
+ }
+ }
+
return NULL;
}
+static void rp_ats_cache_remove(RemotePortATS *s, IOMMUTLBEntry *iotlb)
+{
+ for (int i = 0; i < s->cache->len; i++) {
+ IOMMUTLBEntry *tmp = g_array_index(s->cache, IOMMUTLBEntry *, i);
+ hwaddr masked_start = (tmp->iova & ~iotlb->addr_mask);
+ hwaddr masked_end = ((tmp->iova | tmp->addr_mask) & ~iotlb->addr_mask);
+
+ if (masked_start == iotlb->iova || masked_end == iotlb->iova) {
+ g_array_remove_index_fast(s->cache, i);
+ }
+ }
+}
+
+static void rp_ats_invalidate(RemotePortATS *s, IOMMUTLBEntry *iotlb)
+{
+ size_t pktlen = sizeof(struct rp_pkt_ats);
+ struct rp_pkt_ats pkt;
+ RemotePortRespSlot *rsp_slot;
+ RemotePortDynPkt *rsp;
+ size_t enclen;
+ int64_t clk;
+ uint32_t id;
+ hwaddr len = iotlb->addr_mask + 1;
+
+ id = rp_new_id(s->rp);
+ clk = rp_normalized_vmclk(s->rp);
+
+ enclen = rp_encode_ats_inv(id, s->rp_dev,
+ &pkt,
+ clk,
+ 0,
+ iotlb->iova,
+ len,
+ 0,
+ 0);
+ assert(enclen == pktlen);
+
+ rp_rsp_mutex_lock(s->rp);
+ rp_write(s->rp, (void *) &pkt, enclen);
+
+ rsp_slot = rp_dev_wait_resp(s->rp, s->rp_dev, id);
+ rsp = &rsp_slot->rsp;
+
+ /* We dont support out of order answers yet. */
+ assert(rsp->pkt->hdr.id == id);
+
+ rp_resp_slot_done(s->rp, rsp_slot);
+ rp_rsp_mutex_unlock(s->rp);
+}
+
+static void rp_ats_cache_insert(RemotePortATS *s,
+ hwaddr iova,
+ hwaddr translated_addr,
+ hwaddr mask,
+ AddressSpace *target_as)
+{
+ IOMMUTLBEntry *iotlb;
+
+ /*
+ * Invalidate all current translations that collide with the new one and
+ * does not have the same target_as. This means that translated_addresses
+ * towards the same addresses but in different target address spaces are
+ * not allowed.
+ */
+ for (int i = 0; i < s->cache->len; i++) {
+ iotlb = g_array_index(s->cache, IOMMUTLBEntry *, i);
+ hwaddr masked_start = (translated_addr & ~iotlb->addr_mask);
+ hwaddr masked_end = ((translated_addr | mask) & ~iotlb->addr_mask);
+ bool spans_region = masked_start < iotlb->translated_addr &&
+ masked_end > iotlb->translated_addr;
+
+ if (masked_start == iotlb->translated_addr ||
+ masked_end == iotlb->translated_addr || spans_region) {
+ hwaddr masked_iova_start;
+ hwaddr masked_iova_end;
+
+ /*
+ * Invalidated & remove the mapping if the address range hit in the
+ * cache but the target_as is different.
+ */
+ if (iotlb->target_as != target_as) {
+ rp_ats_invalidate(s, iotlb);
+ g_array_remove_index_fast(s->cache, i);
+ continue;
+ }
+
+ /*
+ * Remove duplicates with a smaller range length since the new
+ * mapping will span over it.
+ */
+ masked_iova_start = (iova & ~iotlb->addr_mask);
+ masked_iova_end = ((iova | mask) & ~iotlb->addr_mask);
+ spans_region = masked_iova_start < iotlb->iova &&
+ masked_iova_end > iotlb->iova;
+
+ if (masked_iova_start == iotlb->iova ||
+ masked_iova_end == iotlb->iova || spans_region) {
+
+ if ((iotlb->addr_mask + 1) < (mask + 1)) {
+ g_array_remove_index_fast(s->cache, i);
+ } else {
+ /*
+ * The new mapping is smaller or equal in size and is thus
+ * already cached.
+ */
+ return;
+ }
+ }
+ }
+ }
+
+ iotlb = g_new0(IOMMUTLBEntry, 1);
+ iotlb->iova = iova;
+ iotlb->translated_addr = translated_addr;
+ iotlb->addr_mask = mask;
+ iotlb->target_as = target_as;
+ g_array_append_val(s->cache, iotlb);
+}
+
+static void rp_ats_iommu_unmap_notify(IOMMUNotifier *n, IOMMUTLBEntry *iotlb)
+{
+ ATSIOMMUNotifier *notifier = container_of(n, ATSIOMMUNotifier, n);
+ RemotePortATS *s = notifier->rp_ats;
+
+ rp_ats_invalidate(s, iotlb);
+ rp_ats_cache_remove(s, iotlb);
+}
+
static bool ats_translate_address(RemotePortATS *s, struct rp_pkt *pkt,
hwaddr *phys_addr, hwaddr *phys_len)
{
- /* TBD */
+ MemTxAttrs attrs = MEMTXATTRS_UNSPECIFIED;
+ IOMMUMemoryRegion *iommu_mr;
+ AddressSpace *target_as;
+ MemoryRegion *mr;
+ int prot = 0;
+
+ RCU_READ_LOCK_GUARD();
+
+ mr = ats_do_translate(&s->as, pkt->ats.addr, phys_addr, phys_len,
+ &target_as, &prot, attrs);
+ if (!mr) {
+ return false;
+ }
+
+ iommu_mr = memory_region_get_iommu(mr);
+ if (iommu_mr) {
+ int iommu_idx = memory_region_iommu_attrs_to_index(iommu_mr, attrs);
+ ATSIOMMUNotifier *notifier;
+ int i;
+
+ for (i = 0; i < s->iommu_notifiers->len; i++) {
+ notifier = g_array_index(s->iommu_notifiers, ATSIOMMUNotifier *, i);
+ if (notifier->mr == mr && notifier->iommu_idx == iommu_idx) {
+ break;
+ }
+ }
+
+ /* Register a notifier if not found. */
+ if (i == s->iommu_notifiers->len) {
+ Error *err = NULL;
+ bool ret;
+
+ s->iommu_notifiers = g_array_set_size(s->iommu_notifiers, i + 1);
+ notifier = g_new0(ATSIOMMUNotifier, 1);
+ g_array_index(s->iommu_notifiers, ATSIOMMUNotifier *, i) = notifier;
+
+ notifier->mr = mr;
+ notifier->iommu_idx = iommu_idx;
+ notifier->rp_ats = s;
+
+ iommu_notifier_init(¬ifier->n,
+ rp_ats_iommu_unmap_notify,
+ IOMMU_NOTIFIER_UNMAP,
+ 0,
+ HWADDR_MAX,
+ iommu_idx);
+
+ ret = memory_region_register_iommu_notifier(mr, ¬ifier->n, &err);
+ if (ret) {
+ error_report_err(err);
+ exit(1);
+ }
+ }
+ }
+
+ if (!(prot & IOMMU_RO)) {
+ pkt->ats.attributes &= ~(RP_ATS_ATTR_exec | RP_ATS_ATTR_read);
+ }
+ if (!(prot & IOMMU_WO)) {
+ pkt->ats.attributes &= ~(RP_ATS_ATTR_write);
+ }
+
+ rp_ats_cache_insert(s, pkt->ats.addr, *phys_addr, *phys_len - 1, target_as);
+
return true;
}
--
2.43.0
^ permalink raw reply related [flat|nested] 41+ messages in thread* [PATCH 19/29] hw/core: Add Remote Port memory slave device
2026-02-05 19:57 [PATCH 00/29] hw/core: Introduce Remote Port Co-simulation Protocol Ruslan Ruslichenko
` (17 preceding siblings ...)
2026-02-05 19:58 ` [PATCH 18/29] hw/core: Implement Remote Port ATS logic and cache management Ruslan Ruslichenko
@ 2026-02-05 19:58 ` Ruslan Ruslichenko
2026-02-05 19:58 ` [PATCH 20/29] hw/core: Add Remote Port GPIO/Interrupt bridge Ruslan Ruslichenko
` (9 subsequent siblings)
28 siblings, 0 replies; 41+ messages in thread
From: Ruslan Ruslichenko @ 2026-02-05 19:58 UTC (permalink / raw)
To: qemu-devel
Cc: qemu-arm, peter.maydell, artem_mygaiev, volodymyr_babchuk,
takahiro.nakata.wr, Edgar E . Iglesias, francisco.iglesias,
Ruslan_Ruslichenko, Edgar E . Iglesias
From: Ruslan Ruslichenko <Ruslan_Ruslichenko@epam.com>
Introduce the 'remote-port-memory-slave' device to allow remote
peers to initiate memory transactions within QEMU.
The patch implements the logic to receive Remote Port bus access
commands, execute them within the QEMU Address Space, and return
the appropriate response.
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-memory-slave.c | 263 +++++++++++++++++++++
hw/core/trace-events | 4 +
include/hw/core/remote-port-memory-slave.h | 34 +++
3 files changed, 301 insertions(+)
create mode 100644 hw/core/remote-port-memory-slave.c
create mode 100644 include/hw/core/remote-port-memory-slave.h
diff --git a/hw/core/remote-port-memory-slave.c b/hw/core/remote-port-memory-slave.c
new file mode 100644
index 0000000000..bb23325469
--- /dev/null
+++ b/hw/core/remote-port-memory-slave.c
@@ -0,0 +1,263 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * QEMU remote port memory slave. Read and write transactions
+ * recieved from the remote port are translated into an address space.
+ *
+ * Copyright (c) 2013 Xilinx Inc
+ * Written by Edgar E. Iglesias <edgar.iglesias@xilinx.com>
+ * Written by Peter Crosthwaite <peter.crosthwaite@xilinx.com>
+ *
+ * This code is licensed under the GNU GPL.
+ */
+
+#include "qemu/osdep.h"
+#include "system/system.h"
+#include "system/dma.h"
+#include "qemu/log.h"
+#include "qapi/qmp/qerror.h"
+#include "qapi/error.h"
+#include "hw/core/sysbus.h"
+#include "migration/vmstate.h"
+#include "hw/core/qdev-properties.h"
+#include "qemu/error-report.h"
+#include "trace.h"
+#include "qemu/cutils.h"
+
+#include "hw/core/remote-port-proto.h"
+#include "hw/core/remote-port-memory-slave.h"
+
+#ifndef REMOTE_PORT_ERR_DEBUG
+#define REMOTE_PORT_DEBUG_LEVEL 0
+#else
+#define REMOTE_PORT_DEBUG_LEVEL 1
+#endif
+
+#define DB_PRINT_L(level, ...) do { \
+ if (REMOTE_PORT_DEBUG_LEVEL > level) { \
+ fprintf(stderr, ": %s: ", __func__); \
+ fprintf(stderr, ## __VA_ARGS__); \
+ } \
+} while (0)
+
+/* Slow path dealing with odd stuff like byte-enables. */
+static MemTxResult process_data_slow(RemotePortMemorySlave *s,
+ AddressSpace *as,
+ struct rp_pkt *pkt,
+ DMADirection dir,
+ uint8_t *data, uint8_t *byte_en)
+{
+ unsigned int i;
+ unsigned int byte_en_len = pkt->busaccess_ext_base.byte_enable_len;
+ unsigned int sw = pkt->busaccess.stream_width;
+ MemTxResult ret = MEMTX_OK;
+
+ assert(sw);
+
+ for (i = 0; i < pkt->busaccess.len; i++) {
+ if (byte_en && !byte_en[i % byte_en_len]) {
+ continue;
+ }
+
+ ret = dma_memory_rw(as, pkt->busaccess.addr + i % sw,
+ data + i, 1, dir, s->attr);
+
+ if (ret != MEMTX_OK) {
+ break;
+ }
+ }
+
+ return ret;
+}
+
+static AddressSpace *get_as_for_phys_busaccess(RemotePortMemorySlave *s,
+ struct rp_pkt *pkt)
+{
+ if (s->ats_cache) {
+ IOMMUTLBEntry *iotlb =
+ rp_ats_cache_lookup_translation(s->ats_cache,
+ pkt->busaccess.addr,
+ pkt->busaccess.len);
+ if (iotlb) {
+ /* Return the matching address space found. */
+ return iotlb->target_as;
+ }
+ }
+ /* Emit a warning on errors since this really should not happen. */
+ warn_report("Physical address error detected (range address: 0x%"
+ HWADDR_PRIx ", len: 0x%" PRIx32 " contains untranslated "
+ "addresses)", pkt->busaccess.addr, pkt->busaccess.len);
+ return NULL;
+}
+
+static void rp_cmd_rw(RemotePortMemorySlave *s, struct rp_pkt *pkt,
+ DMADirection dir)
+{
+ size_t pktlen = sizeof(struct rp_pkt_busaccess_ext_base);
+ struct rp_encode_busaccess_in in = {0};
+ size_t enclen;
+ int64_t delay;
+ uint8_t *data = NULL;
+ uint8_t *byte_en;
+ MemTxResult ret;
+ AddressSpace *as;
+
+ byte_en = rp_busaccess_byte_en_ptr(s->peer, &pkt->busaccess_ext_base);
+
+ if (dir == DMA_DIRECTION_TO_DEVICE) {
+ pktlen += pkt->busaccess.len;
+ } else {
+ data = rp_busaccess_rx_dataptr(s->peer, &pkt->busaccess_ext_base);
+ }
+
+ assert(pkt->busaccess.width == 0);
+ assert(!(pkt->hdr.flags & RP_PKT_FLAGS_response));
+
+ rp_dpkt_alloc(&s->rsp, pktlen);
+ if (dir == DMA_DIRECTION_TO_DEVICE) {
+ data = rp_busaccess_tx_dataptr(s->peer,
+ &s->rsp.pkt->busaccess_ext_base);
+ }
+ if (dir == DMA_DIRECTION_FROM_DEVICE && REMOTE_PORT_DEBUG_LEVEL > 0) {
+ DB_PRINT_L(0, "address: %" PRIx64 "\n", pkt->busaccess.addr);
+ qemu_hexdump(stderr, ": write: ",
+ (const char *) data, pkt->busaccess.len);
+ }
+ trace_remote_port_memory_slave_rx_busaccess(rp_cmd_to_string(pkt->hdr.cmd),
+ pkt->hdr.id, pkt->hdr.flags, pkt->hdr.dev, pkt->busaccess.addr,
+ pkt->busaccess.len, pkt->busaccess.attributes);
+
+ s->attr.secure = !!(pkt->busaccess.attributes & RP_BUS_ATTR_SECURE);
+ s->attr.requester_id = pkt->busaccess.master_id;
+
+ if (pkt->busaccess.attributes & RP_BUS_ATTR_PHYS_ADDR) {
+ as = get_as_for_phys_busaccess(s, pkt);
+ } else {
+ as = &s->as;
+ }
+ if (as) {
+ if (byte_en || pkt->busaccess.stream_width != pkt->busaccess.len) {
+ ret = process_data_slow(s, as, pkt, dir, data, byte_en);
+ } else {
+ ret = dma_memory_rw(as, pkt->busaccess.addr, data,
+ pkt->busaccess.len, dir, s->attr);
+ }
+ } else {
+ ret = MEMTX_ERROR;
+ }
+
+ if (dir == DMA_DIRECTION_TO_DEVICE && REMOTE_PORT_DEBUG_LEVEL > 0) {
+ DB_PRINT_L(0, "address: %" PRIx64 "\n", pkt->busaccess.addr);
+ qemu_hexdump(stderr, ": read: ",
+ (const char *) data, pkt->busaccess.len);
+ }
+ /*
+ * delay here could be set to the annotated cost of doing issuing
+ * these accesses. QEMU doesn't support this kind of annotations
+ * at the moment. So we just clear the delay.
+ */
+ delay = 0;
+
+ rp_encode_busaccess_in_rsp_init(&in, pkt);
+ in.clk = pkt->busaccess.timestamp + delay;
+
+ switch (ret) {
+ case MEMTX_ERROR:
+ in.attr |= RP_RESP_BUS_GENERIC_ERROR << RP_BUS_RESP_SHIFT;
+ break;
+ case MEMTX_DECODE_ERROR:
+ in.attr |= RP_RESP_ADDR_ERROR << RP_BUS_RESP_SHIFT;
+ break;
+ default:
+ /* MEMTX_OK maps to RP_RESP_OK. */
+ break;
+ }
+
+ enclen = rp_encode_busaccess(s->peer, &s->rsp.pkt->busaccess_ext_base,
+ &in);
+ assert(enclen <= pktlen);
+
+ trace_remote_port_memory_slave_tx_busaccess(rp_cmd_to_string(in.cmd),
+ in.id, in.flags, in.dev, in.addr, in.size, in.attr);
+
+ rp_write(s->rp, (void *)s->rsp.pkt, enclen);
+}
+
+static void rp_memory_slave_realize(DeviceState *dev, Error **errp)
+{
+ RemotePortMemorySlave *s = REMOTE_PORT_MEMORY_SLAVE(dev);
+
+ s->peer = rp_get_peer(s->rp);
+ address_space_init(&s->as, s->mr ? s->mr : get_system_memory(), "dma");
+}
+
+static void rp_memory_slave_write(RemotePortDevice *s, struct rp_pkt *pkt)
+{
+ return rp_cmd_rw(REMOTE_PORT_MEMORY_SLAVE(s), pkt,
+ DMA_DIRECTION_FROM_DEVICE);
+}
+
+static void rp_memory_slave_read(RemotePortDevice *s, struct rp_pkt *pkt)
+{
+ return rp_cmd_rw(REMOTE_PORT_MEMORY_SLAVE(s), pkt,
+ DMA_DIRECTION_TO_DEVICE);
+}
+
+static void rp_prop_allow_set_link(const Object *obj, const char *name,
+ Object *val, Error **errp)
+{
+}
+
+static void rp_memory_slave_init(Object *obj)
+{
+ RemotePortMemorySlave *rpms = REMOTE_PORT_MEMORY_SLAVE(obj);
+
+ object_property_add_link(obj, "rp-adaptor0", "remote-port",
+ (Object **)&rpms->rp,
+ rp_prop_allow_set_link,
+ OBJ_PROP_LINK_STRONG);
+ object_property_add_link(obj, "mr", TYPE_MEMORY_REGION,
+ (Object **)&rpms->mr,
+ qdev_prop_allow_set_link_before_realize,
+ OBJ_PROP_LINK_STRONG);
+ object_property_add_link(obj, "rp-ats-cache", TYPE_REMOTE_PORT_ATS_CACHE,
+ (Object **)&rpms->ats_cache,
+ qdev_prop_allow_set_link_before_realize,
+ OBJ_PROP_LINK_STRONG);
+}
+
+static void rp_memory_slave_unrealize(DeviceState *dev)
+{
+ RemotePortMemorySlave *s = REMOTE_PORT_MEMORY_SLAVE(dev);
+
+ address_space_destroy(&s->as);
+}
+
+static void rp_memory_slave_class_init(ObjectClass *oc, const void *data)
+{
+ RemotePortDeviceClass *rpdc = REMOTE_PORT_DEVICE_CLASS(oc);
+ DeviceClass *dc = DEVICE_CLASS(oc);
+
+ rpdc->ops[RP_CMD_write] = rp_memory_slave_write;
+ rpdc->ops[RP_CMD_read] = rp_memory_slave_read;
+ dc->realize = rp_memory_slave_realize;
+ dc->unrealize = rp_memory_slave_unrealize;
+}
+
+static const TypeInfo rp_info = {
+ .name = TYPE_REMOTE_PORT_MEMORY_SLAVE,
+ .parent = TYPE_DEVICE,
+ .instance_size = sizeof(RemotePortMemorySlave),
+ .instance_init = rp_memory_slave_init,
+ .class_init = rp_memory_slave_class_init,
+ .interfaces = (InterfaceInfo[]) {
+ { TYPE_REMOTE_PORT_DEVICE },
+ { },
+ },
+};
+
+static void rp_register_types(void)
+{
+ type_register_static(&rp_info);
+}
+
+type_init(rp_register_types)
diff --git a/hw/core/trace-events b/hw/core/trace-events
index 6332002d65..ee77916010 100644
--- a/hw/core/trace-events
+++ b/hw/core/trace-events
@@ -36,3 +36,7 @@ cpu_reset(int cpu_index) "%d"
# remote-port-memory-master.c
remote_port_memory_master_tx_busaccess(const char *cmd, uint32_t id, uint32_t flags, uint32_t dev, uint64_t addr, uint32_t len, uint64_t attr) "cmd=%s, id=0x%"PRIx32", flags=0x%"PRIx32", dev=0x%"PRIx32", addr=0x%"PRIx64", len=0x%"PRIx32", attr=0x%"PRIx64
remote_port_memory_master_rx_busaccess(const char *cmd, uint32_t id, uint32_t flags, uint32_t dev, uint64_t addr, uint32_t len, uint64_t attr) "cmd=%s, id=0x%"PRIx32", flags=0x%"PRIx32", dev=0x%"PRIx32", addr=0x%"PRIx64", len=0x%"PRIx32", attr=0x%"PRIx64
+
+# remote-port-memory-slave.c
+remote_port_memory_slave_tx_busaccess(const char *cmd, uint32_t id, uint32_t flags, uint32_t dev, uint64_t addr, uint32_t len, uint64_t attr) "cmd=%s, id=0x%"PRIx32", flags=0x%"PRIx32", dev=0x%"PRIx32", addr=0x%"PRIx64", len=0x%"PRIx32", attr=0x%"PRIx64
+remote_port_memory_slave_rx_busaccess(const char *cmd, uint32_t id, uint32_t flags, uint32_t dev, uint64_t addr, uint32_t len, uint64_t attr) "cmd=%s, id=0x%"PRIx32", flags=0x%"PRIx32", dev=0x%"PRIx32", addr=0x%"PRIx64", len=0x%"PRIx32", attr=0x%"PRIx64
diff --git a/include/hw/core/remote-port-memory-slave.h b/include/hw/core/remote-port-memory-slave.h
new file mode 100644
index 0000000000..d88e806ed6
--- /dev/null
+++ b/include/hw/core/remote-port-memory-slave.h
@@ -0,0 +1,34 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * QEMU remote port memory slave. Read and write transactions
+ * recieved from the remote port are translated into an address space.
+ *
+ * Copyright (c) 2013 Xilinx Inc
+ * Written by Edgar E. Iglesias <edgar.iglesias@xilinx.com>
+ * Written by Peter Crosthwaite <peter.crosthwaite@xilinx.com>
+ *
+ * This code is licensed under the GNU GPL.
+ */
+#ifndef REMOTE_PORT_MEMORY_SLAVE_H
+#define REMOTE_PORT_MEMORY_SLAVE_H
+
+#include "hw/core/remote-port-ats.h"
+
+#define TYPE_REMOTE_PORT_MEMORY_SLAVE "remote-port-memory-slave"
+#define REMOTE_PORT_MEMORY_SLAVE(obj) \
+ OBJECT_CHECK(RemotePortMemorySlave, (obj), \
+ TYPE_REMOTE_PORT_MEMORY_SLAVE)
+
+typedef struct RemotePortMemorySlave {
+ /* private */
+ SysBusDevice parent;
+ /* public */
+ struct RemotePort *rp;
+ struct rp_peer_state *peer;
+ MemoryRegion *mr;
+ AddressSpace as;
+ MemTxAttrs attr;
+ RemotePortDynPkt rsp;
+ RemotePortATSCache *ats_cache;
+} RemotePortMemorySlave;
+#endif
--
2.43.0
^ permalink raw reply related [flat|nested] 41+ messages in thread* [PATCH 20/29] hw/core: Add Remote Port GPIO/Interrupt bridge
2026-02-05 19:57 [PATCH 00/29] hw/core: Introduce Remote Port Co-simulation Protocol Ruslan Ruslichenko
` (18 preceding siblings ...)
2026-02-05 19:58 ` [PATCH 19/29] hw/core: Add Remote Port memory slave device Ruslan Ruslichenko
@ 2026-02-05 19:58 ` Ruslan Ruslichenko
2026-02-05 19:58 ` [PATCH 21/29] hw/core: Add Remote Port Stream device Ruslan Ruslichenko
` (8 subsequent siblings)
28 siblings, 0 replies; 41+ messages in thread
From: Ruslan Ruslichenko @ 2026-02-05 19:58 UTC (permalink / raw)
To: qemu-devel
Cc: qemu-arm, peter.maydell, artem_mygaiev, volodymyr_babchuk,
takahiro.nakata.wr, Edgar E . Iglesias, francisco.iglesias,
Ruslan_Ruslichenko, Edgar E . Iglesias
From: Ruslan Ruslichenko <Ruslan_Ruslichenko@epam.com>
This patch adds the `remote-port-gpio` device, which enables
GPIO levels exchange between QEMU and the remote peer.
It maps QEMU's `qemu_irq` lines (GPIOs) to Remote Port `RP_CMD_interrupt`
packets, enabling the synchronization of interrupt lines and logic signals
across the simulation.
This is essential for wiring up interrupt controllers or discrete logic
signals (reset lines, enable pins).
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-gpio.c | 183 +++++++++++++++++++++++++++++
hw/core/trace-events | 4 +
include/hw/core/remote-port-gpio.h | 33 ++++++
3 files changed, 220 insertions(+)
create mode 100644 hw/core/remote-port-gpio.c
create mode 100644 include/hw/core/remote-port-gpio.h
diff --git a/hw/core/remote-port-gpio.c b/hw/core/remote-port-gpio.c
new file mode 100644
index 0000000000..b9bdbcc5a5
--- /dev/null
+++ b/hw/core/remote-port-gpio.c
@@ -0,0 +1,183 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * Copyright (c) 2013 Xilinx Inc
+ * Written by Edgar E. Iglesias <edgar.iglesias@xilinx.com>
+ * Written by Peter Crosthwaite <peter.crosthwaite@xilinx.com>
+ *
+ * This code is licensed under the GNU GPL.
+ */
+
+#include "qemu/osdep.h"
+#include "hw/core/sysbus.h"
+#include "system/system.h"
+#include "system/dma.h"
+#include "qemu/log.h"
+#include "qapi/qmp/qerror.h"
+#include "qapi/error.h"
+#include "migration/vmstate.h"
+#include "hw/core/qdev-properties.h"
+#include "hw/core/irq.h"
+#include "trace.h"
+
+#include "hw/core/remote-port.h"
+#include "hw/core/remote-port-proto.h"
+#include "hw/core/remote-port-gpio.h"
+
+#define CACHE_INVALID -1
+
+static void rp_gpio_handler(void *opaque, int irq, int level)
+{
+ RemotePortGPIO *s = opaque;
+ struct rp_pkt pkt;
+ size_t len;
+ int64_t clk;
+ uint32_t id = rp_new_id(s->rp);
+ uint32_t flags = s->posted_updates ? RP_PKT_FLAGS_posted : 0;
+
+ /* If we hit the cache, return early. */
+ if (s->cache[irq] != CACHE_INVALID && s->cache[irq] == level) {
+ return;
+ }
+ /* Update the cache and update the remote peer. */
+ s->cache[irq] = level;
+
+ clk = rp_normalized_vmclk(s->rp);
+ len = rp_encode_interrupt_f(id, s->rp_dev, &pkt.interrupt, clk,
+ irq, 0, level, flags);
+
+ trace_remote_port_gpio_tx_interrupt(id, flags, s->rp_dev, 0, irq, level);
+
+ if (s->peer->caps.wire_posted_updates && !s->posted_updates) {
+ rp_rsp_mutex_lock(s->rp);
+ }
+
+ rp_write(s->rp, (void *)&pkt, len);
+
+ /*
+ * If peer supports posted updates it will respect our flag and
+ * not respond.
+ */
+ if (s->peer->caps.wire_posted_updates && !s->posted_updates) {
+ RemotePortRespSlot *rsp_slot;
+ struct rp_pkt_interrupt *intr;
+
+ rsp_slot = rp_dev_wait_resp(s->rp, s->rp_dev, id);
+ assert(rsp_slot->rsp.pkt->hdr.id == id);
+
+ intr = &rsp_slot->rsp.pkt->interrupt;
+ trace_remote_port_gpio_rx_interrupt(intr->hdr.id, intr->hdr.flags,
+ intr->hdr.dev, intr->vector, intr->line, intr->val);
+
+ rp_resp_slot_done(s->rp, rsp_slot);
+ rp_rsp_mutex_unlock(s->rp);
+ }
+}
+
+static void rp_gpio_interrupt(RemotePortDevice *rpdev, struct rp_pkt *pkt)
+{
+ RemotePortGPIO *s = REMOTE_PORT_GPIO(rpdev);
+
+ trace_remote_port_gpio_rx_interrupt(pkt->hdr.id, pkt->hdr.flags,
+ pkt->hdr.dev, pkt->interrupt.vector, pkt->interrupt.line,
+ pkt->interrupt.val);
+
+ qemu_set_irq(s->gpio_out[pkt->interrupt.line], pkt->interrupt.val);
+
+ if (s->peer->caps.wire_posted_updates
+ && !(pkt->hdr.flags & RP_PKT_FLAGS_posted)) {
+ RemotePortDynPkt rsp = {0};
+ size_t len;
+
+ /* Need to reply. */
+ rp_dpkt_alloc(&rsp, sizeof(struct rp_pkt_interrupt));
+ len = rp_encode_interrupt_f(pkt->hdr.id, pkt->hdr.dev,
+ &rsp.pkt->interrupt,
+ pkt->interrupt.timestamp,
+ pkt->interrupt.line,
+ pkt->interrupt.vector,
+ pkt->interrupt.val,
+ pkt->hdr.flags | RP_PKT_FLAGS_response);
+
+ trace_remote_port_gpio_tx_interrupt(pkt->hdr.id,
+ pkt->hdr.flags | RP_PKT_FLAGS_response, pkt->hdr.dev,
+ pkt->interrupt.vector, pkt->interrupt.line, pkt->interrupt.val);
+
+ rp_write(s->rp, (void *)rsp.pkt, len);
+ }
+}
+
+static void rp_gpio_reset(DeviceState *dev)
+{
+ RemotePortGPIO *s = REMOTE_PORT_GPIO(dev);
+
+ /* Mark as invalid. */
+ memset(s->cache, CACHE_INVALID, s->num_gpios);
+}
+
+static void rp_gpio_realize(DeviceState *dev, Error **errp)
+{
+ RemotePortGPIO *s = REMOTE_PORT_GPIO(dev);
+ unsigned int i;
+
+ s->peer = rp_get_peer(s->rp);
+
+ s->gpio_out = g_new0(qemu_irq, s->num_gpios);
+ qdev_init_gpio_out(dev, s->gpio_out, s->num_gpios);
+ qdev_init_gpio_in(dev, rp_gpio_handler, s->num_gpios);
+
+ for (i = 0; i < s->num_gpios; i++) {
+ sysbus_init_irq(SYS_BUS_DEVICE(s), &s->gpio_out[i]);
+ }
+}
+
+static void rp_prop_allow_set_link(const Object *obj, const char *name,
+ Object *val, Error **errp)
+{
+}
+
+static void rp_gpio_init(Object *obj)
+{
+ RemotePortGPIO *rpms = REMOTE_PORT_GPIO(obj);
+
+ object_property_add_link(obj, "rp-adaptor0", "remote-port",
+ (Object **)&rpms->rp,
+ rp_prop_allow_set_link,
+ OBJ_PROP_LINK_STRONG);
+}
+
+static Property rp_properties[] = {
+ DEFINE_PROP_UINT32("rp-chan0", RemotePortGPIO, rp_dev, 0),
+ DEFINE_PROP_UINT32("num-gpios", RemotePortGPIO, num_gpios, 16),
+ DEFINE_PROP_UINT16("cell-offset-irq-num", RemotePortGPIO,
+ cell_offset_irq_num, 0),
+ DEFINE_PROP_BOOL("posted-updates", RemotePortGPIO, posted_updates, true),
+};
+
+static void rp_gpio_class_init(ObjectClass *oc, const void *data)
+{
+ RemotePortDeviceClass *rpdc = REMOTE_PORT_DEVICE_CLASS(oc);
+ DeviceClass *dc = DEVICE_CLASS(oc);
+ rpdc->ops[RP_CMD_interrupt] = rp_gpio_interrupt;
+ dc->legacy_reset = rp_gpio_reset;
+ dc->realize = rp_gpio_realize;
+ device_class_set_props_n(dc, rp_properties, ARRAY_SIZE(rp_properties));
+}
+
+static const TypeInfo rp_info = {
+ .name = TYPE_REMOTE_PORT_GPIO,
+ .parent = TYPE_SYS_BUS_DEVICE,
+ .instance_size = sizeof(RemotePortGPIO),
+ .instance_init = rp_gpio_init,
+ .class_init = rp_gpio_class_init,
+ .interfaces = (InterfaceInfo[]) {
+ { TYPE_REMOTE_PORT_DEVICE },
+ { },
+ },
+};
+
+static void rp_register_types(void)
+{
+ type_register_static(&rp_info);
+}
+
+type_init(rp_register_types)
diff --git a/hw/core/trace-events b/hw/core/trace-events
index ee77916010..fb3125b0eb 100644
--- a/hw/core/trace-events
+++ b/hw/core/trace-events
@@ -40,3 +40,7 @@ remote_port_memory_master_rx_busaccess(const char *cmd, uint32_t id, uint32_t fl
# remote-port-memory-slave.c
remote_port_memory_slave_tx_busaccess(const char *cmd, uint32_t id, uint32_t flags, uint32_t dev, uint64_t addr, uint32_t len, uint64_t attr) "cmd=%s, id=0x%"PRIx32", flags=0x%"PRIx32", dev=0x%"PRIx32", addr=0x%"PRIx64", len=0x%"PRIx32", attr=0x%"PRIx64
remote_port_memory_slave_rx_busaccess(const char *cmd, uint32_t id, uint32_t flags, uint32_t dev, uint64_t addr, uint32_t len, uint64_t attr) "cmd=%s, id=0x%"PRIx32", flags=0x%"PRIx32", dev=0x%"PRIx32", addr=0x%"PRIx64", len=0x%"PRIx32", attr=0x%"PRIx64
+
+# remote-port-memory-gpio.c
+remote_port_gpio_tx_interrupt(uint32_t id, uint32_t flags, uint32_t dev, uint64_t vector, uint32_t irq, uint32_t val) "id=0x%"PRIx32", flags=0x%"PRIx32", dev=0x%"PRIx32", vector=0x%"PRIx64", irq=0x%"PRIx32", level=0x%"PRIx32
+remote_port_gpio_rx_interrupt(uint32_t id, uint32_t flags, uint32_t dev, uint64_t vector, uint32_t irq, uint32_t val) "id=0x%"PRIx32", flags=0x%"PRIx32", dev=0x%"PRIx32", vector=0x%"PRIx64", irq=0x%"PRIx32", level=0x%"PRIx32
diff --git a/include/hw/core/remote-port-gpio.h b/include/hw/core/remote-port-gpio.h
new file mode 100644
index 0000000000..940e2ec4fd
--- /dev/null
+++ b/include/hw/core/remote-port-gpio.h
@@ -0,0 +1,33 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * Copyright (c) 2013 Xilinx Inc
+ * Written by Edgar E. Iglesias <edgar.iglesias@xilinx.com>
+ * Written by Peter Crosthwaite <peter.crosthwaite@xilinx.com>
+ *
+ * This code is licensed under the GNU GPL.
+ */
+#ifndef REMOTE_PORT_GPIO_H
+#define REMOTE_PORT_GPIO_H
+
+#define TYPE_REMOTE_PORT_GPIO "remote-port-gpio"
+#define REMOTE_PORT_GPIO(obj) \
+ OBJECT_CHECK(RemotePortGPIO, (obj), TYPE_REMOTE_PORT_GPIO)
+
+#define MAX_GPIOS 960
+
+typedef struct RemotePortGPIO {
+ /* private */
+ SysBusDevice parent;
+ /* public */
+
+ int8_t cache[MAX_GPIOS];
+ uint32_t num_gpios;
+ qemu_irq *gpio_out;
+ uint16_t cell_offset_irq_num;
+
+ bool posted_updates;
+ uint32_t rp_dev;
+ struct RemotePort *rp;
+ struct rp_peer_state *peer;
+} RemotePortGPIO;
+#endif
--
2.43.0
^ permalink raw reply related [flat|nested] 41+ messages in thread* [PATCH 21/29] hw/core: Add Remote Port Stream device
2026-02-05 19:57 [PATCH 00/29] hw/core: Introduce Remote Port Co-simulation Protocol Ruslan Ruslichenko
` (19 preceding siblings ...)
2026-02-05 19:58 ` [PATCH 20/29] hw/core: Add Remote Port GPIO/Interrupt bridge Ruslan Ruslichenko
@ 2026-02-05 19:58 ` Ruslan Ruslichenko
2026-02-05 19:58 ` [PATCH 22/29] hw/core: Add Remote Port files to build Ruslan Ruslichenko
` (7 subsequent siblings)
28 siblings, 0 replies; 41+ messages in thread
From: Ruslan Ruslichenko @ 2026-02-05 19:58 UTC (permalink / raw)
To: qemu-devel
Cc: qemu-arm, peter.maydell, artem_mygaiev, volodymyr_babchuk,
takahiro.nakata.wr, Edgar E . Iglesias, francisco.iglesias,
Ruslan_Ruslichenko, Edgar E . Iglesias
From: Ruslan Ruslichenko <Ruslan_Ruslichenko@epam.com>
Introduce the 'remote-port-stream' device to bridge QEMU StreamSink
interfaces with the Remote Port protocol.
The patch implements the logic to translate local stream push
operations into Remote Port bus-access packets and vice-versa.
This enables bi-directional data streaming between QEMU and
external simulators.
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-stream.c | 239 +++++++++++++++++++++++++++++++++++
hw/core/trace-events | 4 +
2 files changed, 243 insertions(+)
create mode 100644 hw/core/remote-port-stream.c
diff --git a/hw/core/remote-port-stream.c b/hw/core/remote-port-stream.c
new file mode 100644
index 0000000000..6baaf68f08
--- /dev/null
+++ b/hw/core/remote-port-stream.c
@@ -0,0 +1,239 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * Copyright (c) 2013 Xilinx Inc
+ * Copyright (c) 2013 Peter Crosthwaite <peter.crosthwaite@xilinx.com>
+ * Copyright (c) 2013 Edgar E. Iglesias <edgar.iglesias@xilinx.com>
+ *
+ * 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.
+ */
+
+#include "qemu/osdep.h"
+#include "hw/core/sysbus.h"
+#include "qemu/log.h"
+
+#include "qemu/bitops.h"
+#include "qapi/qmp/qerror.h"
+#include "hw/core/stream.h"
+#include "qapi/error.h"
+#include "migration/vmstate.h"
+#include "hw/core/qdev-properties.h"
+#include "hw/core/remote-port.h"
+#include "trace.h"
+
+#ifndef REMOTE_PORT_STREAM_ERR_DEBUG
+#define REMOTE_PORT_STREAM_ERR_DEBUG 0
+#endif
+
+#define TYPE_REMOTE_PORT_STREAM "remote-port-stream"
+
+#define REMOTE_PORT_STREAM(obj) \
+ OBJECT_CHECK(RemotePortStream, (obj), TYPE_REMOTE_PORT_STREAM)
+
+typedef struct RemotePortStream RemotePortStream;
+
+struct RemotePortStream {
+ DeviceState parent_obj;
+
+ RemotePort *rp;
+ uint32_t rp_dev;
+ uint16_t stream_width;
+
+ StreamSink *tx_dev;
+
+ StreamCanPushNotifyFn notify;
+ void *notify_opaque;
+
+ uint8_t *buf;
+ struct rp_pkt pkt;
+
+ bool rsp_pending;
+ uint32_t current_id;
+};
+
+static void rp_stream_notify(void *opaque)
+{
+ RemotePortStream *s = REMOTE_PORT_STREAM(opaque);
+
+ /* FIXME: This is completely bogus. */
+ if (s->buf && stream_can_push(s->tx_dev, rp_stream_notify, s)) {
+ RemotePortDynPkt rsp;
+ struct rp_encode_busaccess_in in = {0};
+ size_t pktlen = sizeof(struct rp_pkt_busaccess_ext_base);
+ size_t enclen;
+ int64_t delay = 0; /* FIXME - Implement */
+
+ size_t ret = stream_push(s->tx_dev, s->buf, 4, 0);
+ assert(ret == 4);
+ s->buf = NULL;
+
+ memset(&rsp, 0, sizeof(rsp));
+ rp_dpkt_alloc(&rsp, pktlen);
+ rp_encode_busaccess_in_rsp_init(&in, &s->pkt);
+ in.clk = s->pkt.busaccess.timestamp + delay;
+ enclen = rp_encode_busaccess(rp_get_peer(s->rp),
+ &rsp.pkt->busaccess_ext_base,
+ &in);
+ assert(enclen <= pktlen);
+ trace_remote_port_stream_tx_busaccess(rp_cmd_to_string(in.cmd),
+ in.id, in.flags, in.dev, in.addr, in.size, in.attr);
+
+ rp_write(s->rp, (void *)rsp.pkt, pktlen);
+ }
+}
+
+static void rp_stream_write(RemotePortDevice *obj, struct rp_pkt *pkt)
+{
+ RemotePortStream *s = REMOTE_PORT_STREAM(obj);
+
+ trace_remote_port_stream_rx_busaccess(rp_cmd_to_string(pkt->hdr.cmd),
+ pkt->hdr.id, pkt->hdr.flags, pkt->hdr.dev, pkt->busaccess.addr,
+ pkt->busaccess.len, pkt->busaccess.attributes);
+
+ assert(pkt->busaccess.width == 0);
+ assert(pkt->busaccess.stream_width == pkt->busaccess.len);
+ assert(pkt->busaccess.addr == 0);
+
+ if (pkt->hdr.flags & RP_PKT_FLAGS_response) {
+ /* FXIME - probably need to do syncs and stuff */
+ assert(s->rsp_pending);
+ s->rsp_pending = false;
+ if (s->notify) {
+ StreamCanPushNotifyFn notify = s->notify;
+ s->notify = NULL;
+ notify(s->notify_opaque);
+ }
+ } else {
+ assert(!s->buf);
+ s->buf = g_memdup2(pkt + 1, 4);
+ s->pkt = *pkt;
+ rp_stream_notify(s);
+ }
+}
+
+static bool rp_stream_stream_can_push(StreamSink *obj,
+ StreamCanPushNotifyFn notify,
+ void *notify_opaque)
+{
+ RemotePortStream *s = REMOTE_PORT_STREAM(obj);
+
+ if (s->rsp_pending) {
+ s->notify = notify;
+ s->notify_opaque = notify_opaque;
+ return false;
+ }
+ return true;
+}
+
+static size_t rp_stream_stream_push(StreamSink *obj, uint8_t *buf,
+ size_t len, bool eop)
+{
+ RemotePortStream *s = REMOTE_PORT_STREAM(obj);
+ RemotePortDynPkt rsp;
+ struct rp_pkt_busaccess_ext_base pkt;
+ struct rp_encode_busaccess_in in = {0};
+ uint64_t rp_attr = eop ? RP_BUS_ATTR_EOP : 0;
+ int64_t clk;
+ int enclen;
+
+ clk = rp_normalized_vmclk(s->rp);
+
+ in.cmd = RP_CMD_write;
+ in.id = rp_new_id(s->rp);
+ in.dev = s->rp_dev;
+ in.clk = clk;
+ in.attr = rp_attr;
+ in.size = len;
+ in.stream_width = s->stream_width;
+ enclen = rp_encode_busaccess(rp_get_peer(s->rp), &pkt, &in);
+
+ trace_remote_port_stream_tx_busaccess(rp_cmd_to_string(in.cmd),
+ in.id, in.flags, in.dev, in.addr, in.size, in.attr);
+
+ rp_rsp_mutex_lock(s->rp);
+ rp_write(s->rp, (void *) &pkt, enclen);
+ rp_write(s->rp, buf, len);
+ rsp = rp_wait_resp(s->rp);
+ assert(rsp.pkt->hdr.id == be32_to_cpu(pkt.hdr.id));
+
+ trace_remote_port_stream_rx_busaccess(
+ rp_cmd_to_string(rsp.pkt->hdr.cmd), rsp.pkt->hdr.id,
+ rsp.pkt->hdr.flags, rsp.pkt->hdr.dev, rsp.pkt->busaccess.addr,
+ rsp.pkt->busaccess.len, rsp.pkt->busaccess.attributes);
+
+ rp_dpkt_invalidate(&rsp);
+ rp_rsp_mutex_unlock(s->rp);
+ rp_restart_sync_timer(s->rp);
+ return len;
+}
+
+static void rp_prop_allow_set_link(const Object *obj, const char *name,
+ Object *val, Error **errp)
+{
+}
+
+static void rp_stream_init(Object *obj)
+{
+ RemotePortStream *s = REMOTE_PORT_STREAM(obj);
+
+ object_property_add_link(obj, "axistream-connected", TYPE_STREAM_SINK,
+ (Object **)&s->tx_dev,
+ qdev_prop_allow_set_link_before_realize,
+ OBJ_PROP_LINK_STRONG);
+ object_property_add_link(obj, "rp-adaptor0", "remote-port",
+ (Object **)&s->rp,
+ rp_prop_allow_set_link,
+ OBJ_PROP_LINK_STRONG);
+}
+
+static Property rp_properties[] = {
+ DEFINE_PROP_UINT32("rp-chan0", RemotePortStream, rp_dev, 0),
+ DEFINE_PROP_UINT16("stream-width", RemotePortStream, stream_width, 4),
+};
+
+static void rp_stream_class_init(ObjectClass *oc, const void *data)
+{
+ DeviceClass *dc = DEVICE_CLASS(oc);
+ StreamSinkClass *ssc = STREAM_SINK_CLASS(oc);
+ RemotePortDeviceClass *rpdc = REMOTE_PORT_DEVICE_CLASS(oc);
+
+ ssc->push = rp_stream_stream_push;
+ ssc->can_push = rp_stream_stream_can_push;
+ device_class_set_props_n(dc, rp_properties, ARRAY_SIZE(rp_properties));
+ rpdc->ops[RP_CMD_write] = rp_stream_write;
+}
+
+static const TypeInfo rp_stream_info = {
+ .name = TYPE_REMOTE_PORT_STREAM,
+ .parent = TYPE_DEVICE,
+ .instance_size = sizeof(RemotePortStream),
+ .class_init = rp_stream_class_init,
+ .instance_init = rp_stream_init,
+ .interfaces = (InterfaceInfo[]) {
+ { TYPE_STREAM_SINK },
+ { TYPE_REMOTE_PORT_DEVICE },
+ { },
+ }
+};
+
+static void rp_stream_register_types(void)
+{
+ type_register_static(&rp_stream_info);
+}
+
+type_init(rp_stream_register_types)
diff --git a/hw/core/trace-events b/hw/core/trace-events
index fb3125b0eb..5b4e5029fe 100644
--- a/hw/core/trace-events
+++ b/hw/core/trace-events
@@ -44,3 +44,7 @@ remote_port_memory_slave_rx_busaccess(const char *cmd, uint32_t id, uint32_t fla
# remote-port-memory-gpio.c
remote_port_gpio_tx_interrupt(uint32_t id, uint32_t flags, uint32_t dev, uint64_t vector, uint32_t irq, uint32_t val) "id=0x%"PRIx32", flags=0x%"PRIx32", dev=0x%"PRIx32", vector=0x%"PRIx64", irq=0x%"PRIx32", level=0x%"PRIx32
remote_port_gpio_rx_interrupt(uint32_t id, uint32_t flags, uint32_t dev, uint64_t vector, uint32_t irq, uint32_t val) "id=0x%"PRIx32", flags=0x%"PRIx32", dev=0x%"PRIx32", vector=0x%"PRIx64", irq=0x%"PRIx32", level=0x%"PRIx32
+
+# remote-port-stream.c
+remote_port_stream_tx_busaccess(const char *cmd, uint32_t id, uint32_t flags, uint32_t dev, uint64_t addr, uint32_t len, uint64_t attr) "cmd=%s, id=0x%"PRIx32", flags=0x%"PRIx32", dev=0x%"PRIx32", addr=0x%"PRIx64", len=0x%"PRIx32", attr=0x%"PRIx64
+remote_port_stream_rx_busaccess(const char *cmd, uint32_t id, uint32_t flags, uint32_t dev, uint64_t addr, uint32_t len, uint64_t attr) "cmd=%s, id=0x%"PRIx32", flags=0x%"PRIx32", dev=0x%"PRIx32", addr=0x%"PRIx64", len=0x%"PRIx32", attr=0x%"PRIx64
--
2.43.0
^ permalink raw reply related [flat|nested] 41+ messages in thread* [PATCH 22/29] hw/core: Add Remote Port files to build
2026-02-05 19:57 [PATCH 00/29] hw/core: Introduce Remote Port Co-simulation Protocol Ruslan Ruslichenko
` (20 preceding siblings ...)
2026-02-05 19:58 ` [PATCH 21/29] hw/core: Add Remote Port Stream device Ruslan Ruslichenko
@ 2026-02-05 19:58 ` Ruslan Ruslichenko
2026-02-05 19:58 ` [PATCH 23/29] system: Introduce -sync-quantum command line option Ruslan Ruslichenko
` (6 subsequent siblings)
28 siblings, 0 replies; 41+ messages in thread
From: Ruslan Ruslichenko @ 2026-02-05 19:58 UTC (permalink / raw)
To: qemu-devel
Cc: qemu-arm, peter.maydell, artem_mygaiev, volodymyr_babchuk,
takahiro.nakata.wr, Edgar E . Iglesias, francisco.iglesias,
Ruslan_Ruslichenko, Paolo Bonzini, Edgar E . Iglesias
From: Ruslan Ruslichenko <Ruslan_Ruslichenko@epam.com>
Add the Remote Port subsystem source files to the build system.
These files are compiled when CONFIG_REMOTE_PORT is enabled.
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/Kconfig | 4 ++++
hw/core/meson.build | 10 ++++++++++
2 files changed, 14 insertions(+)
diff --git a/hw/core/Kconfig b/hw/core/Kconfig
index d1bdf765ee..25f553e189 100644
--- a/hw/core/Kconfig
+++ b/hw/core/Kconfig
@@ -38,3 +38,7 @@ config SPLIT_IRQ
config EIF
bool
depends on LIBCBOR && GNUTLS
+
+config REMOTE_PORT
+ bool
+ depends on PTIMER && XILINX_AXI
diff --git a/hw/core/meson.build b/hw/core/meson.build
index b5ba8160e6..7013638fcf 100644
--- a/hw/core/meson.build
+++ b/hw/core/meson.build
@@ -27,6 +27,16 @@ system_ss.add(when: 'CONFIG_XILINX_AXI', if_true: files('stream.c'))
system_ss.add(when: 'CONFIG_PLATFORM_BUS', if_true: files('sysbus-fdt.c'))
system_ss.add(when: 'CONFIG_EIF', if_true: [files('eif.c'), zlib, libcbor, gnutls])
+system_ss.add(when: 'CONFIG_REMOTE_PORT', if_true: files(
+ 'remote-port-proto.c',
+ 'remote-port.c',
+ 'remote-port-memory-master.c',
+ 'remote-port-memory-slave.c',
+ 'remote-port-gpio.c',
+ 'remote-port-stream.c',
+ 'remote-port-ats.c',
+))
+
system_ss.add(files(
'cpu-system.c',
'fdt_generic.c',
--
2.43.0
^ permalink raw reply related [flat|nested] 41+ messages in thread* [PATCH 23/29] system: Introduce -sync-quantum command line option
2026-02-05 19:57 [PATCH 00/29] hw/core: Introduce Remote Port Co-simulation Protocol Ruslan Ruslichenko
` (21 preceding siblings ...)
2026-02-05 19:58 ` [PATCH 22/29] hw/core: Add Remote Port files to build Ruslan Ruslichenko
@ 2026-02-05 19:58 ` Ruslan Ruslichenko
2026-02-05 20:40 ` Daniel P. Berrangé
2026-02-05 19:58 ` [PATCH 24/29] hw/core: Add FDT support to Remote Port GPIO Ruslan Ruslichenko
` (5 subsequent siblings)
28 siblings, 1 reply; 41+ messages in thread
From: Ruslan Ruslichenko @ 2026-02-05 19:58 UTC (permalink / raw)
To: qemu-devel
Cc: qemu-arm, peter.maydell, artem_mygaiev, volodymyr_babchuk,
takahiro.nakata.wr, Edgar E . Iglesias, francisco.iglesias,
Ruslan_Ruslichenko, Paolo Bonzini, Edgar E . Iglesias
From: Ruslan Ruslichenko <Ruslan_Ruslichenko@epam.com>
Add a new global option '-sync-quantum' to specify the maximum time
interval, in nanoseconds, between synchronizations with remote peers.
In co-simulation environments (such as QEMU coupled with a SystemC
simulator), time must be synchronized periodically to ensure functional
accuracy. The 'sync-quantum' defines the maximum amount of time QEMU
can simulate ahead of its remote peers before a mandatory synchronization
event (sync packet) occurs.
This value is stored in the global 'global_sync_quantum' variable and
serves as a default hint for device models and Remote Port instances
to manage their simulation time.
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>
---
include/system/system.h | 2 ++
qemu-options.hx | 11 +++++++++++
system/vl.c | 8 ++++++++
3 files changed, 21 insertions(+)
diff --git a/include/system/system.h b/include/system/system.h
index 0cd012004d..f187544147 100644
--- a/include/system/system.h
+++ b/include/system/system.h
@@ -59,6 +59,8 @@ extern MlockState mlock_state;
extern const char *machine_path;
+extern uint64_t global_sync_quantum;
+
#define MAX_OPTION_ROMS 16
typedef struct QEMUOptionRom {
const char *name;
diff --git a/qemu-options.hx b/qemu-options.hx
index b7dd2a64f0..8ef0c57507 100644
--- a/qemu-options.hx
+++ b/qemu-options.hx
@@ -765,6 +765,17 @@ SRST
Preallocate memory when using -mem-path.
ERST
+DEF("sync-quantum", HAS_ARG, QEMU_OPTION_sync_quantum,
+ "-sync-quantum Max time between synchroniation, nanoseconds.\n",
+ QEMU_ARCH_ALL)
+SRST
+``-sync-quantum val``
+ Maximum time between synchronization <val>.
+ This value is used to drive periodic synchronization with remote port peers.
+ It is also used to set device models sync-quantum properties controlling
+ the maximum amount of ahead of time simulation that is prefered (only a hint).
+ERST
+
DEF("machine-path", HAS_ARG, QEMU_OPTION_machine_path,
"-machine-path DIR A directory in which to create machine nodes\n",
QEMU_ARCH_ALL)
diff --git a/system/vl.c b/system/vl.c
index 4750b1cf69..467646c3ff 100644
--- a/system/vl.c
+++ b/system/vl.c
@@ -182,6 +182,7 @@ static QemuPluginList plugin_list = QTAILQ_HEAD_INITIALIZER(plugin_list);
static BlockdevOptionsQueue bdo_queue = QSIMPLEQ_HEAD_INITIALIZER(bdo_queue);
static bool nographic = false;
static int mem_prealloc; /* force preallocation of physical target memory */
+uint64_t global_sync_quantum;
const char *machine_path;
static const char *vga_model = NULL;
static DisplayOptions dpy;
@@ -3129,6 +3130,13 @@ void qemu_init(int argc, char **argv)
case QEMU_OPTION_mem_prealloc:
mem_prealloc = 1;
break;
+ case QEMU_OPTION_sync_quantum:
+ if (qemu_strtou64(optarg, &optarg, 10,
+ &global_sync_quantum)) {
+ error_report("failed to parse sync_quantum");
+ exit(1);
+ }
+ break;
case QEMU_OPTION_machine_path:
machine_path = optarg;
break;
--
2.43.0
^ permalink raw reply related [flat|nested] 41+ messages in thread* Re: [PATCH 23/29] system: Introduce -sync-quantum command line option
2026-02-05 19:58 ` [PATCH 23/29] system: Introduce -sync-quantum command line option Ruslan Ruslichenko
@ 2026-02-05 20:40 ` Daniel P. Berrangé
2026-02-06 17:57 ` Ruslan Ruslichenko
0 siblings, 1 reply; 41+ messages in thread
From: Daniel P. Berrangé @ 2026-02-05 20:40 UTC (permalink / raw)
To: Ruslan Ruslichenko
Cc: qemu-devel, qemu-arm, peter.maydell, artem_mygaiev,
volodymyr_babchuk, takahiro.nakata.wr, Edgar E . Iglesias,
francisco.iglesias, Ruslan_Ruslichenko, Paolo Bonzini,
Edgar E . Iglesias
On Thu, Feb 05, 2026 at 08:58:18PM +0100, Ruslan Ruslichenko wrote:
> From: Ruslan Ruslichenko <Ruslan_Ruslichenko@epam.com>
>
> Add a new global option '-sync-quantum' to specify the maximum time
> interval, in nanoseconds, between synchronizations with remote peers.
Again, we don't really want to introduced new top level command
line options, so try to find an existing place that's suitable
to fit this in, perhaps a prop -machine.
>
> In co-simulation environments (such as QEMU coupled with a SystemC
> simulator), time must be synchronized periodically to ensure functional
> accuracy. The 'sync-quantum' defines the maximum amount of time QEMU
> can simulate ahead of its remote peers before a mandatory synchronization
> event (sync packet) occurs.
>
> This value is stored in the global 'global_sync_quantum' variable and
> serves as a default hint for device models and Remote Port instances
> to manage their simulation time.
>
> 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>
> ---
> include/system/system.h | 2 ++
> qemu-options.hx | 11 +++++++++++
> system/vl.c | 8 ++++++++
> 3 files changed, 21 insertions(+)
>
> diff --git a/include/system/system.h b/include/system/system.h
> index 0cd012004d..f187544147 100644
> --- a/include/system/system.h
> +++ b/include/system/system.h
> @@ -59,6 +59,8 @@ extern MlockState mlock_state;
>
> extern const char *machine_path;
>
> +extern uint64_t global_sync_quantum;
> +
> #define MAX_OPTION_ROMS 16
> typedef struct QEMUOptionRom {
> const char *name;
> diff --git a/qemu-options.hx b/qemu-options.hx
> index b7dd2a64f0..8ef0c57507 100644
> --- a/qemu-options.hx
> +++ b/qemu-options.hx
> @@ -765,6 +765,17 @@ SRST
> Preallocate memory when using -mem-path.
> ERST
>
> +DEF("sync-quantum", HAS_ARG, QEMU_OPTION_sync_quantum,
> + "-sync-quantum Max time between synchroniation, nanoseconds.\n",
> + QEMU_ARCH_ALL)
> +SRST
> +``-sync-quantum val``
> + Maximum time between synchronization <val>.
> + This value is used to drive periodic synchronization with remote port peers.
> + It is also used to set device models sync-quantum properties controlling
> + the maximum amount of ahead of time simulation that is prefered (only a hint).
> +ERST
> +
> DEF("machine-path", HAS_ARG, QEMU_OPTION_machine_path,
> "-machine-path DIR A directory in which to create machine nodes\n",
> QEMU_ARCH_ALL)
> diff --git a/system/vl.c b/system/vl.c
> index 4750b1cf69..467646c3ff 100644
> --- a/system/vl.c
> +++ b/system/vl.c
> @@ -182,6 +182,7 @@ static QemuPluginList plugin_list = QTAILQ_HEAD_INITIALIZER(plugin_list);
> static BlockdevOptionsQueue bdo_queue = QSIMPLEQ_HEAD_INITIALIZER(bdo_queue);
> static bool nographic = false;
> static int mem_prealloc; /* force preallocation of physical target memory */
> +uint64_t global_sync_quantum;
> const char *machine_path;
> static const char *vga_model = NULL;
> static DisplayOptions dpy;
> @@ -3129,6 +3130,13 @@ void qemu_init(int argc, char **argv)
> case QEMU_OPTION_mem_prealloc:
> mem_prealloc = 1;
> break;
> + case QEMU_OPTION_sync_quantum:
> + if (qemu_strtou64(optarg, &optarg, 10,
> + &global_sync_quantum)) {
> + error_report("failed to parse sync_quantum");
> + exit(1);
> + }
> + break;
> case QEMU_OPTION_machine_path:
> machine_path = optarg;
> break;
> --
> 2.43.0
>
>
With regards,
Daniel
--
|: https://berrange.com -o- https://www.flickr.com/photos/dberrange :|
|: https://libvirt.org -o- https://fstop138.berrange.com :|
|: https://entangle-photo.org -o- https://www.instagram.com/dberrange :|
^ permalink raw reply [flat|nested] 41+ messages in thread* Re: [PATCH 23/29] system: Introduce -sync-quantum command line option
2026-02-05 20:40 ` Daniel P. Berrangé
@ 2026-02-06 17:57 ` Ruslan Ruslichenko
0 siblings, 0 replies; 41+ messages in thread
From: Ruslan Ruslichenko @ 2026-02-06 17:57 UTC (permalink / raw)
To: Daniel P. Berrangé
Cc: qemu-devel, qemu-arm, peter.maydell, artem_mygaiev,
volodymyr_babchuk, takahiro.nakata.wr, Edgar E . Iglesias,
francisco.iglesias, Ruslan_Ruslichenko, Paolo Bonzini,
Edgar E . Iglesias
On Thu, Feb 5, 2026 at 9:41 PM Daniel P. Berrangé <berrange@redhat.com> wrote:
>
> On Thu, Feb 05, 2026 at 08:58:18PM +0100, Ruslan Ruslichenko wrote:
> > From: Ruslan Ruslichenko <Ruslan_Ruslichenko@epam.com>
> >
> > Add a new global option '-sync-quantum' to specify the maximum time
> > interval, in nanoseconds, between synchronizations with remote peers.
>
> Again, we don't really want to introduced new top level command
> line options, so try to find an existing place that's suitable
> to fit this in, perhaps a prop -machine.
>
Understood, not sure machine options are applicable here,
but encapsulating it within remote-port itself may be possible. I will
investigate it for v2.
> >
> > In co-simulation environments (such as QEMU coupled with a SystemC
> > simulator), time must be synchronized periodically to ensure functional
> > accuracy. The 'sync-quantum' defines the maximum amount of time QEMU
> > can simulate ahead of its remote peers before a mandatory synchronization
> > event (sync packet) occurs.
> >
> > This value is stored in the global 'global_sync_quantum' variable and
> > serves as a default hint for device models and Remote Port instances
> > to manage their simulation time.
> >
> > 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>
> > ---
> > include/system/system.h | 2 ++
> > qemu-options.hx | 11 +++++++++++
> > system/vl.c | 8 ++++++++
> > 3 files changed, 21 insertions(+)
> >
> > diff --git a/include/system/system.h b/include/system/system.h
> > index 0cd012004d..f187544147 100644
> > --- a/include/system/system.h
> > +++ b/include/system/system.h
> > @@ -59,6 +59,8 @@ extern MlockState mlock_state;
> >
> > extern const char *machine_path;
> >
> > +extern uint64_t global_sync_quantum;
> > +
> > #define MAX_OPTION_ROMS 16
> > typedef struct QEMUOptionRom {
> > const char *name;
> > diff --git a/qemu-options.hx b/qemu-options.hx
> > index b7dd2a64f0..8ef0c57507 100644
> > --- a/qemu-options.hx
> > +++ b/qemu-options.hx
> > @@ -765,6 +765,17 @@ SRST
> > Preallocate memory when using -mem-path.
> > ERST
> >
> > +DEF("sync-quantum", HAS_ARG, QEMU_OPTION_sync_quantum,
> > + "-sync-quantum Max time between synchroniation, nanoseconds.\n",
> > + QEMU_ARCH_ALL)
> > +SRST
> > +``-sync-quantum val``
> > + Maximum time between synchronization <val>.
> > + This value is used to drive periodic synchronization with remote port peers.
> > + It is also used to set device models sync-quantum properties controlling
> > + the maximum amount of ahead of time simulation that is prefered (only a hint).
> > +ERST
> > +
> > DEF("machine-path", HAS_ARG, QEMU_OPTION_machine_path,
> > "-machine-path DIR A directory in which to create machine nodes\n",
> > QEMU_ARCH_ALL)
> > diff --git a/system/vl.c b/system/vl.c
> > index 4750b1cf69..467646c3ff 100644
> > --- a/system/vl.c
> > +++ b/system/vl.c
> > @@ -182,6 +182,7 @@ static QemuPluginList plugin_list = QTAILQ_HEAD_INITIALIZER(plugin_list);
> > static BlockdevOptionsQueue bdo_queue = QSIMPLEQ_HEAD_INITIALIZER(bdo_queue);
> > static bool nographic = false;
> > static int mem_prealloc; /* force preallocation of physical target memory */
> > +uint64_t global_sync_quantum;
> > const char *machine_path;
> > static const char *vga_model = NULL;
> > static DisplayOptions dpy;
> > @@ -3129,6 +3130,13 @@ void qemu_init(int argc, char **argv)
> > case QEMU_OPTION_mem_prealloc:
> > mem_prealloc = 1;
> > break;
> > + case QEMU_OPTION_sync_quantum:
> > + if (qemu_strtou64(optarg, &optarg, 10,
> > + &global_sync_quantum)) {
> > + error_report("failed to parse sync_quantum");
> > + exit(1);
> > + }
> > + break;
> > case QEMU_OPTION_machine_path:
> > machine_path = optarg;
> > break;
> > --
> > 2.43.0
> >
> >
>
> With regards,
> Daniel
> --
> |: https://berrange.com -o- https://www.flickr.com/photos/dberrange :|
> |: https://libvirt.org -o- https://fstop138.berrange.com :|
> |: https://entangle-photo.org -o- https://www.instagram.com/dberrange :|
>
--
BR,
Ruslan
^ permalink raw reply [flat|nested] 41+ messages in thread
* [PATCH 24/29] hw/core: Add FDT support to Remote Port GPIO
2026-02-05 19:57 [PATCH 00/29] hw/core: Introduce Remote Port Co-simulation Protocol Ruslan Ruslichenko
` (22 preceding siblings ...)
2026-02-05 19:58 ` [PATCH 23/29] system: Introduce -sync-quantum command line option Ruslan Ruslichenko
@ 2026-02-05 19:58 ` Ruslan Ruslichenko
2026-02-05 19:58 ` [PATCH 25/29] hw/core: Add FDT support to Remote Port memory master Ruslan Ruslichenko
` (4 subsequent siblings)
28 siblings, 0 replies; 41+ messages in thread
From: Ruslan Ruslichenko @ 2026-02-05 19:58 UTC (permalink / raw)
To: qemu-devel
Cc: qemu-arm, peter.maydell, artem_mygaiev, volodymyr_babchuk,
takahiro.nakata.wr, Edgar E . Iglesias, francisco.iglesias,
Ruslan_Ruslichenko, Edgar E . Iglesias
From: Ruslan Ruslichenko <Ruslan_Ruslichenko@epam.com>
Implement the FDTGenericIntc interface for the Remote
Port GPIO device.
This enables the device to be recognized as an interrupt
controller when instantiated via the fdt-generic framework.
It allows other devices defined in the Device Tree to connect
their interrupt lines to this GPIO controller using standard
FDT properties (e.g., 'interrupts' and 'interrupt-parent').
The implementation maps the interrupt specifier from the
Device Tree cells to the corresponding internal GPIO input
line based on the configured cell offset.
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-gpio.c | 23 +++++++++++++++++++++++
1 file changed, 23 insertions(+)
diff --git a/hw/core/remote-port-gpio.c b/hw/core/remote-port-gpio.c
index b9bdbcc5a5..c17a65f63a 100644
--- a/hw/core/remote-port-gpio.c
+++ b/hw/core/remote-port-gpio.c
@@ -19,6 +19,8 @@
#include "hw/core/irq.h"
#include "trace.h"
+#include "hw/core/fdt_generic_util.h"
+
#include "hw/core/remote-port.h"
#include "hw/core/remote-port-proto.h"
#include "hw/core/remote-port-gpio.h"
@@ -153,14 +155,34 @@ static Property rp_properties[] = {
DEFINE_PROP_BOOL("posted-updates", RemotePortGPIO, posted_updates, true),
};
+static int rp_fdt_get_irq(FDTGenericIntc *obj, qemu_irq *irqs,
+ uint32_t *cells, int ncells, int max,
+ Error **errp)
+{
+ RemotePortGPIO *s = REMOTE_PORT_GPIO(obj);
+
+ if (cells[s->cell_offset_irq_num] >= s->num_gpios) {
+ error_setg(errp, "RP-GPIO was setup for %u interrupts: index %"
+ PRIu32 " requested", s->num_gpios,
+ cells[s->cell_offset_irq_num]);
+ return 0;
+ }
+
+ (*irqs) = qdev_get_gpio_in(DEVICE(obj), cells[s->cell_offset_irq_num]);
+ return 1;
+};
+
static void rp_gpio_class_init(ObjectClass *oc, const void *data)
{
RemotePortDeviceClass *rpdc = REMOTE_PORT_DEVICE_CLASS(oc);
DeviceClass *dc = DEVICE_CLASS(oc);
+ FDTGenericIntcClass *fgic = FDT_GENERIC_INTC_CLASS(oc);
+
rpdc->ops[RP_CMD_interrupt] = rp_gpio_interrupt;
dc->legacy_reset = rp_gpio_reset;
dc->realize = rp_gpio_realize;
device_class_set_props_n(dc, rp_properties, ARRAY_SIZE(rp_properties));
+ fgic->get_irq = rp_fdt_get_irq;
}
static const TypeInfo rp_info = {
@@ -171,6 +193,7 @@ static const TypeInfo rp_info = {
.class_init = rp_gpio_class_init,
.interfaces = (InterfaceInfo[]) {
{ TYPE_REMOTE_PORT_DEVICE },
+ { TYPE_FDT_GENERIC_INTC },
{ },
},
};
--
2.43.0
^ permalink raw reply related [flat|nested] 41+ messages in thread* [PATCH 25/29] hw/core: Add FDT support to Remote Port memory master
2026-02-05 19:57 [PATCH 00/29] hw/core: Introduce Remote Port Co-simulation Protocol Ruslan Ruslichenko
` (23 preceding siblings ...)
2026-02-05 19:58 ` [PATCH 24/29] hw/core: Add FDT support to Remote Port GPIO Ruslan Ruslichenko
@ 2026-02-05 19:58 ` Ruslan Ruslichenko
2026-02-05 19:58 ` [PATCH 26/29] hw/core: Add Remote Port connection support to fdt-generic Ruslan Ruslichenko
` (3 subsequent siblings)
28 siblings, 0 replies; 41+ messages in thread
From: Ruslan Ruslichenko @ 2026-02-05 19:58 UTC (permalink / raw)
To: qemu-devel
Cc: qemu-arm, peter.maydell, artem_mygaiev, volodymyr_babchuk,
takahiro.nakata.wr, Edgar E . Iglesias, francisco.iglesias,
Ruslan_Ruslichenko, Edgar E . Iglesias
From: Ruslan Ruslichenko <Ruslan_Ruslichenko@epam.com>
Implement the FDTGenericMMap interface for the Remote Port
Memory Master.
This enables the device to dynamically parse memory ranges
defined in the Device Tree ('reg' property) and register
them as system bus MMIO regions.
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-memory-master.c | 34 +++++++++++++++++++++++++++++
1 file changed, 34 insertions(+)
diff --git a/hw/core/remote-port-memory-master.c b/hw/core/remote-port-memory-master.c
index 4a6c43423d..854b35e99f 100644
--- a/hw/core/remote-port-memory-master.c
+++ b/hw/core/remote-port-memory-master.c
@@ -22,6 +22,8 @@
#include "hw/core/remote-port.h"
#include "hw/core/remote-port-memory-master.h"
+#include "hw/core/fdt_generic_util.h"
+
#ifndef REMOTE_PORT_ERR_DEBUG
#define REMOTE_PORT_DEBUG_LEVEL 0
#else
@@ -277,6 +279,35 @@ static void rp_memory_master_init(Object *obj)
OBJ_PROP_LINK_STRONG);
}
+static bool rp_parse_reg(FDTGenericMMap *obj, FDTGenericRegPropInfo reg,
+ Error **errp)
+{
+ RemotePortMemoryMaster *s = REMOTE_PORT_MEMORY_MASTER(obj);
+ FDTGenericMMapClass *parent_fmc =
+ FDT_GENERIC_MMAP_CLASS(REMOTE_PORT_MEMORY_MASTER_PARENT_CLASS);
+ int i;
+
+ /* Initialize rp_ops from template. */
+ s->rp_ops = g_malloc(sizeof *s->rp_ops);
+ memcpy(s->rp_ops, &rp_ops_template, sizeof *s->rp_ops);
+ s->rp_ops->valid.max_access_size = s->max_access_size;
+ s->rp_ops->impl.max_access_size = s->max_access_size;
+
+ s->mmaps = g_new0(typeof(*s->mmaps), reg.n);
+ for (i = 0; i < reg.n; ++i) {
+ char *name = g_strdup_printf("rp-%d", i);
+
+ s->mmaps[i].offset = reg.a[i];
+ memory_region_init_io(&s->mmaps[i].iomem, OBJECT(obj), s->rp_ops,
+ &s->mmaps[i], name, reg.s[i]);
+ sysbus_init_mmio(SYS_BUS_DEVICE(obj), &s->mmaps[i].iomem);
+ s->mmaps[i].parent = s;
+ g_free(name);
+ }
+
+ return parent_fmc ? parent_fmc->parse_reg(obj, reg, errp) : false;
+}
+
static Property rp_properties[] = {
DEFINE_PROP_UINT32("map-num", RemotePortMemoryMaster, map_num, 0),
DEFINE_PROP_UINT64("map-offset", RemotePortMemoryMaster, map_offset, 0),
@@ -289,9 +320,11 @@ static Property rp_properties[] = {
static void rp_memory_master_class_init(ObjectClass *oc, const void *data)
{
+ FDTGenericMMapClass *fmc = FDT_GENERIC_MMAP_CLASS(oc);
DeviceClass *dc = DEVICE_CLASS(oc);
device_class_set_props_n(dc, rp_properties, ARRAY_SIZE(rp_properties));
dc->realize = rp_memory_master_realize;
+ fmc->parse_reg = rp_parse_reg;
}
static const TypeInfo rp_info = {
@@ -301,6 +334,7 @@ static const TypeInfo rp_info = {
.instance_init = rp_memory_master_init,
.class_init = rp_memory_master_class_init,
.interfaces = (InterfaceInfo[]) {
+ { TYPE_FDT_GENERIC_MMAP },
{ TYPE_REMOTE_PORT_DEVICE },
{ },
},
--
2.43.0
^ permalink raw reply related [flat|nested] 41+ messages in thread* [PATCH 26/29] hw/core: Add Remote Port connection support to fdt-generic
2026-02-05 19:57 [PATCH 00/29] hw/core: Introduce Remote Port Co-simulation Protocol Ruslan Ruslichenko
` (24 preceding siblings ...)
2026-02-05 19:58 ` [PATCH 25/29] hw/core: Add FDT support to Remote Port memory master Ruslan Ruslichenko
@ 2026-02-05 19:58 ` Ruslan Ruslichenko
2026-02-05 19:58 ` [PATCH 27/29] hw/core: Support IOMMU translation for Remote Port memory slave Ruslan Ruslichenko
` (2 subsequent siblings)
28 siblings, 0 replies; 41+ messages in thread
From: Ruslan Ruslichenko @ 2026-02-05 19:58 UTC (permalink / raw)
To: qemu-devel
Cc: qemu-arm, peter.maydell, artem_mygaiev, volodymyr_babchuk,
takahiro.nakata.wr, Edgar E . Iglesias, francisco.iglesias,
Ruslan_Ruslichenko, Edgar E . Iglesias
From: Ruslan Ruslichenko <Ruslan_Ruslichenko@epam.com>
Extend the fdt-generic utility to automatically configure Remote Port
devices defined in the Device Tree.
This patch enables the system to:
- Identify Remote Port devices and their configuration parameters directly
from the Device Tree description.
- Automatically establish connections between these devices and the
central Remote Port adaptor.
- Register legacy reset handlers for devices instantiated via FDT,
as required by specific Remote Port devices to ensure they reset
properly.
This allows users to define complex co-simulation environments purely
through Device Tree data, removing the need for hardcoded initialization.
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/fdt_generic_util.c | 120 +++++++++++++++++++++++++++++++++----
1 file changed, 107 insertions(+), 13 deletions(-)
diff --git a/hw/core/fdt_generic_util.c b/hw/core/fdt_generic_util.c
index d183a44433..180038baf6 100644
--- a/hw/core/fdt_generic_util.c
+++ b/hw/core/fdt_generic_util.c
@@ -61,6 +61,9 @@
} \
} while (0)
+#include "hw/core/remote-port-memory-slave.h"
+#include "hw/core/remote-port.h"
+
#define PROP_ARRAY_LEN_PREFIX "len-"
/* FIXME: wrap direct calls into libfdt */
@@ -1097,6 +1100,20 @@ static int fdt_init_qdev(char *node_path, FDTMachineInfo *fdti, char *compat)
}
fdt_init_set_opaque(fdti, node_path, dev);
+ /*
+ * Set the default sync-quantum based on the global one. Node properties
+ * in the dtb can later override this value.
+ */
+ if (global_sync_quantum) {
+ ObjectProperty *p;
+
+ p = object_property_find(OBJECT(dev), "sync-quantum");
+ if (p) {
+ object_property_set_int(OBJECT(dev), "sync-quantum",
+ global_sync_quantum, &errp);
+ }
+ }
+
props = qemu_devtree_get_props(fdti->fdt, node_path);
for (prop = props; prop->name; prop++) {
const char *propname = trim_vendor(prop->name);
@@ -1142,25 +1159,99 @@ static int fdt_init_qdev(char *node_path, FDTMachineInfo *fdti, char *compat)
fdt_init_qdev_scalar_prop(OBJECT(dev), p, fdti, node_path, prop);
}
+ if (object_dynamic_cast(dev, TYPE_REMOTE_PORT_DEVICE)) {
+ for (i = 0; ; ++i) {
+ char adaptor_node_path[DT_PATH_LENGTH];
+ uint32_t adaptor_phandle, chan;
+ DeviceState *adaptor;
+ char *name;
+
+ adaptor_phandle = qemu_fdt_getprop_cell(fdti->fdt, node_path,
+ "remote-ports",
+ 2 * i, false, &errp);
+ if (errp) {
+ fprintf(stderr, "cant get phandle from \"remote-ports\" "
+ "property\n");
+ break;
+ }
+ if (qemu_devtree_get_node_by_phandle(fdti->fdt, adaptor_node_path,
+ adaptor_phandle)) {
+ fprintf(stderr, "cant get node from phandle\n");
+ break;
+ }
+ while (!fdt_init_has_opaque(fdti, adaptor_node_path)) {
+ fdt_init_yield(fdti);
+ }
+ adaptor = DEVICE(fdt_init_get_opaque(fdti, adaptor_node_path));
+ name = g_strdup_printf("rp-adaptor%" PRId32, i);
+ object_property_set_link(OBJECT(dev), name, OBJECT(adaptor), &errp);
+ fprintf(stderr, "connecting RP to adaptor %s channel %d",
+ object_get_canonical_path(OBJECT(adaptor)), i);
+ g_free(name);
+ if (errp) {
+ fprintf(stderr, "cant set adaptor link for device property\n");
+ break;
+ }
+
+ chan = qemu_fdt_getprop_cell(fdti->fdt, node_path, "remote-ports",
+ 2 * i + 1, false, &errp);
+ if (errp) {
+ fprintf(stderr, "cant get channel from \"remote-ports\" "
+ "property\n");
+ break;
+ }
+
+ name = g_strdup_printf("rp-chan%" PRId32, i);
+ object_property_set_int(OBJECT(dev), name, chan, &errp);
+ /*
+ * Not critical - device has right to not care about channel
+ * numbers if its a pure slave (only responses).
+ */
+ if (errp) {
+ fprintf(stderr, "cant set %s property %s\n", name,
+ error_get_pretty(errp));
+ errp = NULL;
+ }
+ g_free(name);
+
+ name = g_strdup_printf("remote-port-dev%d", chan);
+ object_property_set_link(OBJECT(adaptor), name, OBJECT(dev), &errp);
+ g_free(name);
+ if (errp) {
+ fprintf(stderr, "cant set device link for adaptor\n");
+ break;
+ }
+ }
+ errp = NULL;
+ }
+
if (object_dynamic_cast(dev, TYPE_DEVICE)) {
+ DeviceClass *dc = DEVICE_GET_CLASS(dev);
const char *short_name = qemu_devtree_get_node_name(fdti->fdt,
node_path);
- /* Connect chardev if we can */
- if (serial_hd(fdt_serial_ports)) {
- Chardev *value = (Chardev *) serial_hd(fdt_serial_ports);
- char *chardev;
-
- /* Check if the device already has a chardev. */
- chardev = object_property_get_str(dev, "chardev", &errp);
- if (!errp && !strcmp(chardev, "")) {
- object_property_set_str(dev, "chardev", value->label, &errp);
- if (!errp) {
- /* It worked, the device is a charecter device */
- fdt_serial_ports++;
+ /*
+ * We don't want to connect remote port chardev's to the user facing
+ * serial devices.
+ */
+ if (!object_dynamic_cast(dev, TYPE_REMOTE_PORT)) {
+ /* Connect chardev if we can */
+ if (serial_hd(fdt_serial_ports)) {
+ Chardev *value = (Chardev *) serial_hd(fdt_serial_ports);
+ char *chardev;
+
+ /* Check if the device already has a chardev. */
+ chardev = object_property_get_str(dev, "chardev", &errp);
+ if (!errp && !strcmp(chardev, "")) {
+ object_property_set_str(dev, "chardev", value->label,
+ &errp);
+ if (!errp) {
+ /* It worked, the device is a charecter device */
+ fdt_serial_ports++;
+ }
}
+ errp = NULL;
}
- errp = NULL;
}
/* Regular TYPE_DEVICE houskeeping */
@@ -1176,6 +1267,9 @@ static int fdt_init_qdev(char *node_path, FDTMachineInfo *fdti, char *compat)
} else {
object_property_set_bool(OBJECT(dev), "realized", true,
&error_fatal);
+ if (dc->legacy_reset) {
+ qemu_register_reset((void (*)(void *))dc->legacy_reset, dev);
+ }
}
}
--
2.43.0
^ permalink raw reply related [flat|nested] 41+ messages in thread* [PATCH 27/29] hw/core: Support IOMMU translation for Remote Port memory slave
2026-02-05 19:57 [PATCH 00/29] hw/core: Introduce Remote Port Co-simulation Protocol Ruslan Ruslichenko
` (25 preceding siblings ...)
2026-02-05 19:58 ` [PATCH 26/29] hw/core: Add Remote Port connection support to fdt-generic Ruslan Ruslichenko
@ 2026-02-05 19:58 ` Ruslan Ruslichenko
2026-02-05 19:58 ` [PATCH 28/29] hw/core: Add Remote Port attachment helpers Ruslan Ruslichenko
2026-02-05 19:58 ` [PATCH 29/29] hw/core: Add ATS support to Remote Port memory slave Ruslan Ruslichenko
28 siblings, 0 replies; 41+ messages in thread
From: Ruslan Ruslichenko @ 2026-02-05 19:58 UTC (permalink / raw)
To: qemu-devel
Cc: qemu-arm, peter.maydell, artem_mygaiev, volodymyr_babchuk,
takahiro.nakata.wr, Edgar E . Iglesias, francisco.iglesias,
Ruslan_Ruslichenko, Mirsad Ostrakovic
From: Mirsad Ostrakovic <mirsad_ostrakovic@epam.com>
Update the Remote Port memory slave to support transactions targeting
an AddressSpace managed by an IOMMU.
This patch:
- Adds 'iommu-id' and 'rp-chan0' properties to configure the connection.
- Implements logic to retrieve the specific AddressSpace for a
given stream ID from the default system bus IOMMU registry.
This enables external simulators (connected via Remote Port) to
perform DMA operations that are correctly translated by the
guest's IOMMU/SMMU logic.
Signed-off-by: Mirsad Ostrakovic <mirsad_ostrakovic@epam.com>
Signed-off-by: Ruslan Ruslichenko <Ruslan_Ruslichenko@epam.com>
---
hw/core/remote-port-memory-slave.c | 35 +++++++++++++++++++++-
include/hw/core/remote-port-memory-slave.h | 3 ++
2 files changed, 37 insertions(+), 1 deletion(-)
diff --git a/hw/core/remote-port-memory-slave.c b/hw/core/remote-port-memory-slave.c
index bb23325469..0419e75bfa 100644
--- a/hw/core/remote-port-memory-slave.c
+++ b/hw/core/remote-port-memory-slave.c
@@ -182,12 +182,38 @@ static void rp_cmd_rw(RemotePortMemorySlave *s, struct rp_pkt *pkt,
rp_write(s->rp, (void *)s->rsp.pkt, enclen);
}
+static AddressSpace *bus_iommu_address_space(BusState *iommu_bus,
+ uint32_t iommu_id, uint16_t devid)
+{
+ if (iommu_bus && iommu_bus->iommu[iommu_id].iommu_ops) {
+ return iommu_bus->iommu[iommu_id].iommu_ops->get_address_space(
+ iommu_bus, iommu_bus->iommu[iommu_id].iommu_opaque, devid);
+ }
+ return &address_space_memory;
+}
+
+static void rp_memory_slave_init_done(Notifier *notifier, void *data)
+{
+ RemotePortMemorySlave *s = container_of(notifier, RemotePortMemorySlave,
+ machine_done);
+ AddressSpace *as;
+
+ if (s->channel_id) {
+ as = bus_iommu_address_space(sysbus_get_default(), s->iommu_id,
+ s->rp_stream_id);
+ address_space_init(&s->as, as->root, "dma");
+ } else {
+ address_space_init(&s->as, s->mr ? s->mr : get_system_memory(), "dma");
+ }
+}
+
static void rp_memory_slave_realize(DeviceState *dev, Error **errp)
{
RemotePortMemorySlave *s = REMOTE_PORT_MEMORY_SLAVE(dev);
s->peer = rp_get_peer(s->rp);
- address_space_init(&s->as, s->mr ? s->mr : get_system_memory(), "dma");
+ s->machine_done.notify = rp_memory_slave_init_done;
+ qemu_add_machine_init_done_notifier(&s->machine_done);
}
static void rp_memory_slave_write(RemotePortDevice *s, struct rp_pkt *pkt)
@@ -232,10 +258,17 @@ static void rp_memory_slave_unrealize(DeviceState *dev)
address_space_destroy(&s->as);
}
+static Property rp_properties[] = {
+ DEFINE_PROP_UINT32("iommu-id", RemotePortMemorySlave, iommu_id, 0),
+ DEFINE_PROP_UINT32("rp-chan0", RemotePortMemorySlave, channel_id, 0),
+ DEFINE_PROP_END_OF_LIST()
+};
+
static void rp_memory_slave_class_init(ObjectClass *oc, const void *data)
{
RemotePortDeviceClass *rpdc = REMOTE_PORT_DEVICE_CLASS(oc);
DeviceClass *dc = DEVICE_CLASS(oc);
+ device_class_set_props_n(dc, rp_properties, ARRAY_SIZE(rp_properties));
rpdc->ops[RP_CMD_write] = rp_memory_slave_write;
rpdc->ops[RP_CMD_read] = rp_memory_slave_read;
diff --git a/include/hw/core/remote-port-memory-slave.h b/include/hw/core/remote-port-memory-slave.h
index d88e806ed6..3161073a3f 100644
--- a/include/hw/core/remote-port-memory-slave.h
+++ b/include/hw/core/remote-port-memory-slave.h
@@ -23,6 +23,7 @@ typedef struct RemotePortMemorySlave {
/* private */
SysBusDevice parent;
/* public */
+ uint32_t channel_id;
struct RemotePort *rp;
struct rp_peer_state *peer;
MemoryRegion *mr;
@@ -30,5 +31,7 @@ typedef struct RemotePortMemorySlave {
MemTxAttrs attr;
RemotePortDynPkt rsp;
RemotePortATSCache *ats_cache;
+ Notifier machine_done;
+ uint32_t iommu_id;
} RemotePortMemorySlave;
#endif
--
2.43.0
^ permalink raw reply related [flat|nested] 41+ messages in thread* [PATCH 28/29] hw/core: Add Remote Port attachment helpers
2026-02-05 19:57 [PATCH 00/29] hw/core: Introduce Remote Port Co-simulation Protocol Ruslan Ruslichenko
` (26 preceding siblings ...)
2026-02-05 19:58 ` [PATCH 27/29] hw/core: Support IOMMU translation for Remote Port memory slave Ruslan Ruslichenko
@ 2026-02-05 19:58 ` Ruslan Ruslichenko
2026-02-05 19:58 ` [PATCH 29/29] hw/core: Add ATS support to Remote Port memory slave Ruslan Ruslichenko
28 siblings, 0 replies; 41+ messages in thread
From: Ruslan Ruslichenko @ 2026-02-05 19:58 UTC (permalink / raw)
To: qemu-devel
Cc: qemu-arm, peter.maydell, artem_mygaiev, volodymyr_babchuk,
takahiro.nakata.wr, Edgar E . Iglesias, francisco.iglesias,
Ruslan_Ruslichenko, Mirsad Ostrakovic, Edgar E . Iglesias
From: Mirsad Ostrakovic <mirsad_ostrakovic@epam.com>
Introduce helper functions to dynamically attach and detach Remote Port
devices to an adaptor.
This patch adds the logic to:
- Connect devices to an adaptor via QOM links ('rp_device_attach').
- Parse command-line options ('rp_device_add') to automatically wire up
devices using 'rp-adaptor' and 'rp-chan' properties.
Signed-off-by: Edgar E. Iglesias <edgar.iglesias@amd.com>
Signed-off-by: Takahiro Nakata <takahiro.nakata.wr@renesas.com>
Signed-off-by: Mirsad Ostrakovic <mirsad_ostrakovic@epam.com>
Signed-off-by: Ruslan Ruslichenko <Ruslan_Ruslichenko@epam.com>
---
hw/core/meson.build | 1 +
hw/core/remote-port-qdev.c | 181 ++++++++++++++++++++++++++++++++++
include/hw/core/remote-port.h | 27 +++++
3 files changed, 209 insertions(+)
create mode 100644 hw/core/remote-port-qdev.c
diff --git a/hw/core/meson.build b/hw/core/meson.build
index 7013638fcf..4638106e92 100644
--- a/hw/core/meson.build
+++ b/hw/core/meson.build
@@ -35,6 +35,7 @@ system_ss.add(when: 'CONFIG_REMOTE_PORT', if_true: files(
'remote-port-gpio.c',
'remote-port-stream.c',
'remote-port-ats.c',
+ 'remote-port-qdev.c'
))
system_ss.add(files(
diff --git a/hw/core/remote-port-qdev.c b/hw/core/remote-port-qdev.c
new file mode 100644
index 0000000000..35c9a6790d
--- /dev/null
+++ b/hw/core/remote-port-qdev.c
@@ -0,0 +1,181 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * QEMU remote attach
+ *
+ * Copyright (c) 2013 Xilinx Inc
+ * Written by Edgar E. Iglesias <edgar.iglesias@xilinx.com>
+ *
+ * This code is licensed under the GNU GPL.
+ */
+
+#include "qemu/osdep.h"
+#include "hw/core/sysbus.h"
+#include "monitor/monitor.h"
+#include "monitor/qdev.h"
+#include "system/arch_init.h"
+#include "qapi/error.h"
+#include "qemu/config-file.h"
+#include "qemu/error-report.h"
+#include "qemu/help_option.h"
+#include "qemu/cutils.h"
+#include "qemu/option.h"
+
+#include "hw/core/remote-port.h"
+
+/* RP helper function to attach a device to an adaptor. */
+void rp_device_attach(Object *adaptor, Object *dev,
+ int rp_nr, int dev_nr,
+ Error **errp)
+{
+ Error *err = NULL;
+ uint32_t nr_devs;
+ char *name;
+ int i;
+
+ assert(adaptor);
+ assert(dev);
+
+ /* Verify that the adaptor is of Remote Port type. */
+ if (!object_dynamic_cast(adaptor, TYPE_REMOTE_PORT)) {
+ error_setg(errp, "%s is not a Remote-Port adaptor!",
+ object_get_canonical_path(adaptor));
+ return;
+ }
+
+ name = g_strdup_printf("rp-adaptor%d", rp_nr);
+ object_property_set_link(dev, name, adaptor, &err);
+ g_free(name);
+ if (err != NULL) {
+ error_propagate(errp, err);
+ return;
+ }
+
+ name = g_strdup_printf("rp-chan%d", rp_nr);
+ object_property_set_int(dev, name, dev_nr, &err);
+ g_free(name);
+ if (err != NULL
+ && !object_dynamic_cast(dev, TYPE_REMOTE_PORT_DEVICE)) {
+ /*
+ * RP devices that only receive requests may not need to
+ * know their channel/dev number. If not, treat this as
+ * an error.
+ */
+ error_propagate(errp, err);
+ return;
+ }
+ err = NULL;
+
+ nr_devs = object_property_get_int(dev, "nr-devs", &err);
+ if (err) {
+ nr_devs = 1;
+ err = NULL;
+ }
+
+ /* Multi-channel devs use consecutive numbering. */
+ for (i = 0; i < nr_devs; i++) {
+ name = g_strdup_printf("remote-port-dev%d", dev_nr + i);
+ object_property_set_link(adaptor, name, dev, &err);
+ g_free(name);
+ if (err != NULL) {
+ error_propagate(errp, err);
+ return;
+ }
+ }
+}
+
+/* RP helper function to detach a device to an adaptor. */
+void rp_device_detach(Object *adaptor, Object *dev,
+ int rp_nr, int dev_nr,
+ Error **errp)
+{
+ Error *err = NULL;
+ uint32_t nr_devs;
+ char *name;
+ int i;
+
+ assert(adaptor);
+ assert(dev);
+
+ name = g_strdup_printf("rp-adaptor%d", rp_nr);
+ object_property_set_link(dev, name, NULL, NULL);
+ g_free(name);
+
+ nr_devs = object_property_get_int(dev, "nr-devs", &err);
+ if (err) {
+ nr_devs = 1;
+ err = NULL;
+ }
+
+ for (i = 0; i < nr_devs; i++) {
+ name = g_strdup_printf("remote-port-dev%d", dev_nr + i);
+ object_property_set_link(adaptor, name, NULL, &err);
+ g_free(name);
+ if (err != NULL) {
+ error_propagate(errp, err);
+ return;
+ }
+ }
+}
+
+/* Scan for remote-port links to be setup. */
+bool rp_device_add(const QDict *opts, DeviceState *dev, Error **errp)
+{
+ Error *err = NULL;
+ Object *adaptor;
+ bool ambiguous;
+ const char *path;
+ char *name;
+ int i;
+
+ /*
+ * Find the adaptor this remote-port device is connected to.
+ * At the moment, we only support one adaptor per device.
+ */
+ name = g_strdup_printf("rp-adaptor%d", 0);
+ path = qdict_get_try_str(opts, name);
+ g_free(name);
+ if (!path) {
+ /* This is not a remote-port device. Treat as success. */
+ return true;
+ }
+ adaptor = object_resolve_path(path, &ambiguous);
+ if (!adaptor) {
+ error_setg(errp, "Did not find rp adaptor %s!", path);
+ return false;
+ }
+
+ /*
+ * Loop through the channels this device provides and attach
+ * them to the adaptor.
+ */
+ for (i = 0; i < INT_MAX; i++) {
+ unsigned long dev_nr;
+ const char *dev_nr_str;
+
+ name = g_strdup_printf("rp-chan%d", i);
+ dev_nr_str = qdict_get_try_str(opts, name);
+ g_free(name);
+
+ if (!dev_nr_str) {
+ if (i == 0) {
+ /* At least one channel must be provided. */
+ error_setg(errp, "Did not find rp-chan%d!", i);
+ return false;
+ }
+ return true;
+ }
+
+ if (qemu_strtoul(dev_nr_str, NULL, 0, &dev_nr)) {
+ error_setg(errp, "Invalid rp-chan%d!", i);
+ return false;
+ }
+
+ /* Now, attach the device to the adaptor. */
+ rp_device_attach(adaptor, OBJECT(dev), 0, dev_nr, &err);
+ if (err != NULL) {
+ error_propagate(errp, err);
+ return false;
+ }
+ }
+ return true;
+}
diff --git a/include/hw/core/remote-port.h b/include/hw/core/remote-port.h
index 172c4b6204..252230961b 100644
--- a/include/hw/core/remote-port.h
+++ b/include/hw/core/remote-port.h
@@ -134,6 +134,33 @@ struct RemotePort {
RemotePortDevice *devs[REMOTE_PORT_MAX_DEVS];
};
+/**
+ * rp_device_attach:
+ * @adaptor: The adaptor onto which to attach the device
+ * @dev: The device to be attached to the adaptor
+ * @rp_nr: The remote-port adaptor nr. A device may attach to multiple
+ * adaptors.
+ * @dev_nr: The device/channel number to bind the device to.
+ * @errp: returns an error if this function fails
+ *
+ * Attaches a device onto an adaptor and binds it to a device number.
+ */
+void rp_device_attach(Object *adaptor, Object *dev,
+ int rp_nr, int dev_nr,
+ Error **errp);
+void rp_device_detach(Object *adaptor, Object *dev,
+ int rp_nr, int dev_nr,
+ Error **errp);
+/**
+ * rp_device_add
+ * @opts: qdev opts created by the qdev subsystem
+ * @dev: The device to be connected
+ * @errp: Returns an error if the function fails
+ *
+ * Function used in qdev-monitor.c to connect remote port devices.
+ * Returns teue on success and false on failure.
+ */
+bool rp_device_add(const QDict *opts, DeviceState *dev, Error **errp);
void rp_rsp_mutex_lock(RemotePort *s);
void rp_rsp_mutex_unlock(RemotePort *s);
--
2.43.0
^ permalink raw reply related [flat|nested] 41+ messages in thread* [PATCH 29/29] hw/core: Add ATS support to Remote Port memory slave
2026-02-05 19:57 [PATCH 00/29] hw/core: Introduce Remote Port Co-simulation Protocol Ruslan Ruslichenko
` (27 preceding siblings ...)
2026-02-05 19:58 ` [PATCH 28/29] hw/core: Add Remote Port attachment helpers Ruslan Ruslichenko
@ 2026-02-05 19:58 ` Ruslan Ruslichenko
28 siblings, 0 replies; 41+ messages in thread
From: Ruslan Ruslichenko @ 2026-02-05 19:58 UTC (permalink / raw)
To: qemu-devel
Cc: qemu-arm, peter.maydell, artem_mygaiev, volodymyr_babchuk,
takahiro.nakata.wr, Edgar E . Iglesias, francisco.iglesias,
Ruslan_Ruslichenko, Mirsad Ostrakovic
From: Mirsad Ostrakovic <mirsad_ostrakovic@epam.com>
Integrate Address Translation Services (ATS) into the Remote Port Memory Slave
to support IOMMU translation for remote transactions.
This patch implements:
- Support for creation of RP ATS as RP Memory Slave subdevice
- Dynamic configuration of the ATS device, linking it to the
same IOMMU context (iommu-id, stream-id) as the parent memory slave.
- Update for FDT generic utility to parse 'remote-port-ats'
properties and propagate the configuration to the device.
This allows external masters to request address translations via the Remote
Port protocol, which are then serviced by the QEMU IOMMU infrastructure.
Signed-off-by: Mirsad Ostrakovic <mirsad_ostrakovic@epam.com>
Signed-off-by: Ruslan Ruslichenko <Ruslan_Ruslichenko@epam.com>
---
hw/core/fdt_generic_util.c | 10 +++
hw/core/remote-port-ats.c | 97 +++++++++++++++++++++-
hw/core/remote-port-memory-slave.c | 36 +++++++-
include/hw/core/remote-port-ats.h | 3 +
include/hw/core/remote-port-memory-slave.h | 4 +-
5 files changed, 145 insertions(+), 5 deletions(-)
diff --git a/hw/core/fdt_generic_util.c b/hw/core/fdt_generic_util.c
index 180038baf6..817e7209e1 100644
--- a/hw/core/fdt_generic_util.c
+++ b/hw/core/fdt_generic_util.c
@@ -1221,6 +1221,16 @@ static int fdt_init_qdev(char *node_path, FDTMachineInfo *fdti, char *compat)
fprintf(stderr, "cant set device link for adaptor\n");
break;
}
+
+ if (object_dynamic_cast(dev, TYPE_REMOTE_PORT_MEMORY_SLAVE)) {
+ uint32_t rp_ats_id;
+ rp_ats_id = qemu_fdt_getprop_cell(fdti->fdt, node_path,
+ "remote-port-ats", 0,
+ false, &errp);
+
+ object_property_set_int(OBJECT(dev), "rp-ats-id",
+ rp_ats_id, &errp);
+ }
}
errp = NULL;
}
diff --git a/hw/core/remote-port-ats.c b/hw/core/remote-port-ats.c
index 1f6c8d7410..27cf8c6c7b 100644
--- a/hw/core/remote-port-ats.c
+++ b/hw/core/remote-port-ats.c
@@ -33,10 +33,29 @@
#include "migration/vmstate.h"
#include "hw/core/qdev-properties.h"
#include "trace.h"
+#include "qemu/cutils.h"
#include "hw/core/remote-port-proto.h"
#include "hw/core/remote-port-ats.h"
+#define RP_TRACE_LVL_NONE (0u)
+#define RR_TRACE_LVL_ERROR (1u)
+#define RP_TRACE_LVL_WARN (2u)
+#define RP_TRACE_LVL_INFO (3u)
+
+#define RP_TRACE_LEVEL (RP_TRACE_LVL_INFO)
+
+#ifndef RP_TRACE_LEVEL
+#define RP_TRACE_LEVEL (RP_TRACE_LVL_NONE)
+#endif
+
+#define RP_TRACE(level, ...) do { \
+ if (RP_TRACE_LEVEL >= level) { \
+ fprintf(stderr, "[TRACE] %s(): ", __func__); \
+ fprintf(stderr, ## __VA_ARGS__); \
+ } \
+} while (0)
+
typedef struct ATSIOMMUNotifier {
IOMMUNotifier n;
MemoryRegion *mr;
@@ -190,6 +209,17 @@ static void rp_ats_cache_insert(RemotePortATS *s,
iotlb->addr_mask = mask;
iotlb->target_as = target_as;
g_array_append_val(s->cache, iotlb);
+
+ char *target_as_name = strdup(iotlb->target_as->name);
+ RP_TRACE(RP_TRACE_LVL_INFO, "IOMMUTLBEntry entry added to cache\r\n");
+ RP_TRACE(RP_TRACE_LVL_INFO, "iova: 0x%" PRIx64 " translated_addr: 0x%" \
+ PRIx64 " addr_mask: 0x%" PRIx64 " target_as: %s perm: 0x%" PRIx32 "\r\n",
+ iotlb->iova,
+ iotlb->translated_addr,
+ iotlb->addr_mask,
+ target_as_name,
+ iotlb->perm);
+ free(target_as_name);
}
static void rp_ats_iommu_unmap_notify(IOMMUNotifier *n, IOMMUTLBEntry *iotlb)
@@ -215,11 +245,20 @@ static bool ats_translate_address(RemotePortATS *s, struct rp_pkt *pkt,
mr = ats_do_translate(&s->as, pkt->ats.addr, phys_addr, phys_len,
&target_as, &prot, attrs);
if (!mr) {
+ RP_TRACE(RP_TRACE_LVL_INFO, "Remote Port ATS translation failed\r\n");
return false;
}
+ RP_TRACE(RP_TRACE_LVL_INFO,
+ "Remote Port ATS translation succeeded - aka. ats_do_translate()\r\n");
+ RP_TRACE(RP_TRACE_LVL_INFO,
+ "phys_addr: 0x%" PRIx64 " phys_len: 0x%" PRIx64 "\r\n",
+ *phys_addr, *phys_len);
+
iommu_mr = memory_region_get_iommu(mr);
if (iommu_mr) {
+ RP_TRACE(RP_TRACE_LVL_INFO,
+ "Memory region obtained from ats_do_translate() is associated with IOMMU\r\n");
int iommu_idx = memory_region_iommu_attrs_to_index(iommu_mr, attrs);
ATSIOMMUNotifier *notifier;
int i;
@@ -284,11 +323,23 @@ static void rp_ats_req(RemotePortDevice *dev, struct rp_pkt *pkt)
assert(!(pkt->hdr.flags & RP_PKT_FLAGS_response));
+ RP_TRACE(RP_TRACE_LVL_INFO, "Remote Port ATS request is received\r\n")
+ RP_TRACE(RP_TRACE_LVL_INFO, "cmd 0x%" PRIx32 " len: 0x%" PRIx32 " id: 0x%" \
+ PRIx32 " flags: 0x%" PRIx32 " dev: 0x%" PRIx32 "\r\n",
+ pkt->hdr.cmd, pkt->hdr.len, pkt->hdr.id, pkt->hdr.flags, pkt->hdr.dev);
+ RP_TRACE(RP_TRACE_LVL_INFO, "timestamp 0x%" PRIx64 " attributes: 0x%" PRIx64
+ " addr: 0x%" PRIx64 " len: 0x%" PRIx64 " result: 0x%" PRIx32 "\r\n",
+ pkt->ats.timestamp, pkt->ats.attributes, pkt->ats.addr, pkt->ats.len,
+ pkt->ats.result);
+
rp_dpkt_alloc(&s->rsp, pktlen);
result = ats_translate_address(s, pkt, &phys_addr, &phys_len) ?
RP_ATS_RESULT_ok : RP_ATS_RESULT_error;
+ RP_TRACE(RP_TRACE_LVL_INFO, "Remote Port ATS translation is %s\r\n",
+ ((result == RP_ATS_RESULT_ok) ? "successful" : "unsuccessful"));
+
/*
* delay here could be set to the annotated cost of doing issuing
* these accesses. QEMU doesn't support this kind of annotations
@@ -307,18 +358,60 @@ static void rp_ats_req(RemotePortDevice *dev, struct rp_pkt *pkt)
pkt->hdr.flags | RP_PKT_FLAGS_response);
assert(enclen == pktlen);
+ RP_TRACE(RP_TRACE_LVL_INFO, "Remote Port ATS response is generated\r\n")
+ RP_TRACE(RP_TRACE_LVL_INFO, "cmd 0x%" PRIx32 " len: 0x%" PRIx32 " id: 0x%" \
+ PRIx32 " flags: 0x%" PRIx32 " dev: 0x%" PRIx32 "\r\n",
+ be32toh(s->rsp.pkt->hdr.cmd),
+ be32toh(s->rsp.pkt->hdr.len),
+ be32toh(s->rsp.pkt->hdr.id),
+ be32toh(s->rsp.pkt->hdr.flags),
+ be32toh(s->rsp.pkt->hdr.dev));
+ RP_TRACE(RP_TRACE_LVL_INFO, "timestamp 0x%" PRIx64 " attributes: 0x%" \
+ PRIx64 " addr: 0x%" PRIx64 " len: 0x%" PRIx64 " result: 0x%" PRIx32 "\r\n",
+ be64toh(s->rsp.pkt->ats.timestamp),
+ be64toh(s->rsp.pkt->ats.attributes),
+ be64toh(s->rsp.pkt->ats.addr),
+ be64toh(s->rsp.pkt->ats.len),
+ be32toh(s->rsp.pkt->ats.result));
+
rp_write(s->rp, (void *)s->rsp.pkt, enclen);
}
+static AddressSpace *bus_iommu_address_space(BusState *iommu_bus,
+ uint32_t iommu_id, uint16_t devid)
+{
+ if (iommu_bus && iommu_bus->iommu[iommu_id].iommu_ops) {
+ return iommu_bus->iommu[iommu_id].iommu_ops->get_address_space(
+ iommu_bus, iommu_bus->iommu[iommu_id].iommu_opaque, devid);
+ }
+ return &address_space_memory;
+}
+
+static void rp_ats_init_done(Notifier *notifier, void *data)
+{
+ RemotePortATS *s = container_of(notifier, RemotePortATS, machine_done);
+ AddressSpace *as;
+
+ if (s->rp_stream_id) {
+ as = bus_iommu_address_space(sysbus_get_default(),
+ s->iommu_id, s->rp_stream_id);
+ address_space_init(&s->as, as->root, "dma");
+ } else {
+ address_space_init(&s->as, s->mr ? s->mr : get_system_memory(), "dma");
+ }
+}
+
static void rp_ats_realize(DeviceState *dev, Error **errp)
{
RemotePortATS *s = REMOTE_PORT_ATS(dev);
s->peer = rp_get_peer(s->rp);
- address_space_init(&s->as, s->mr ? s->mr : get_system_memory(), "ats-as");
s->iommu_notifiers = g_array_new(false, true, sizeof(ATSIOMMUNotifier *));
s->cache = g_array_new(false, true, sizeof(IOMMUTLBEntry *));
+
+ s->machine_done.notify = rp_ats_init_done;
+ qemu_add_machine_init_done_notifier(&s->machine_done);
}
static void rp_prop_allow_set_link(const Object *obj, const char *name,
@@ -363,7 +456,9 @@ static void rp_ats_unrealize(DeviceState *dev)
}
static Property rp_properties[] = {
+ DEFINE_PROP_UINT32("iommu-id", RemotePortATS, iommu_id, 0),
DEFINE_PROP_UINT32("rp-chan0", RemotePortATS, rp_dev, 0),
+ DEFINE_PROP_UINT32("rp-stream-id", RemotePortATS, rp_stream_id, 0),
};
static void rp_ats_class_init(ObjectClass *oc, const void *data)
diff --git a/hw/core/remote-port-memory-slave.c b/hw/core/remote-port-memory-slave.c
index 0419e75bfa..2694f3edae 100644
--- a/hw/core/remote-port-memory-slave.c
+++ b/hw/core/remote-port-memory-slave.c
@@ -192,13 +192,38 @@ static AddressSpace *bus_iommu_address_space(BusState *iommu_bus,
return &address_space_memory;
}
+static void rp_memory_slave_create_ats(RemotePortMemorySlave *s)
+{
+ Object *tmp_obj;
+
+ /* Create RP ATS dev. */
+ tmp_obj = object_new(TYPE_REMOTE_PORT_ATS);
+ s->rp_ats = REMOTE_PORT_ATS(tmp_obj);
+ object_property_add_child(OBJECT(s), "rp-ats", tmp_obj);
+ object_unref(tmp_obj);
+
+ /* Setup the RP ATS dev. */
+ rp_device_attach(OBJECT(s->rp), OBJECT(s->rp_ats), 0,
+ s->rp_ats_id, &error_abort);
+ object_property_set_int(OBJECT(s->rp_ats), "iommu-id",
+ s->iommu_id, &error_abort);
+ object_property_set_int(OBJECT(s->rp_ats), "rp-stream-id",
+ s->rp_stream_id, &error_abort);
+ object_property_set_bool(OBJECT(s->rp_ats), "realized",
+ true, &error_abort);
+
+ /* Setup the RP Slave dev. */
+ object_property_set_link(OBJECT(s), "rp-ats-cache",
+ OBJECT(s->rp_ats), &error_abort);
+}
+
static void rp_memory_slave_init_done(Notifier *notifier, void *data)
{
RemotePortMemorySlave *s = container_of(notifier, RemotePortMemorySlave,
machine_done);
AddressSpace *as;
- if (s->channel_id) {
+ if (s->rp_stream_id) {
as = bus_iommu_address_space(sysbus_get_default(), s->iommu_id,
s->rp_stream_id);
address_space_init(&s->as, as->root, "dma");
@@ -212,6 +237,11 @@ static void rp_memory_slave_realize(DeviceState *dev, Error **errp)
RemotePortMemorySlave *s = REMOTE_PORT_MEMORY_SLAVE(dev);
s->peer = rp_get_peer(s->rp);
+
+ if (s->rp_ats_id > 0) {
+ rp_memory_slave_create_ats(s);
+ }
+
s->machine_done.notify = rp_memory_slave_init_done;
qemu_add_machine_init_done_notifier(&s->machine_done);
}
@@ -260,8 +290,8 @@ static void rp_memory_slave_unrealize(DeviceState *dev)
static Property rp_properties[] = {
DEFINE_PROP_UINT32("iommu-id", RemotePortMemorySlave, iommu_id, 0),
- DEFINE_PROP_UINT32("rp-chan0", RemotePortMemorySlave, channel_id, 0),
- DEFINE_PROP_END_OF_LIST()
+ DEFINE_PROP_UINT32("rp-chan0", RemotePortMemorySlave, rp_stream_id, 0),
+ DEFINE_PROP_UINT32("rp-ats-id", RemotePortMemorySlave, rp_ats_id, 0),
};
static void rp_memory_slave_class_init(ObjectClass *oc, const void *data)
diff --git a/include/hw/core/remote-port-ats.h b/include/hw/core/remote-port-ats.h
index 8bdae1bce9..9e1a2cf419 100644
--- a/include/hw/core/remote-port-ats.h
+++ b/include/hw/core/remote-port-ats.h
@@ -45,6 +45,9 @@ typedef struct {
GArray *iommu_notifiers;
uint32_t rp_dev;
GArray *cache; /* Translation cache */
+ Notifier machine_done;
+ uint32_t rp_stream_id;
+ uint32_t iommu_id;
} RemotePortATS;
#define TYPE_REMOTE_PORT_ATS_CACHE "remote-port-ats-cache"
diff --git a/include/hw/core/remote-port-memory-slave.h b/include/hw/core/remote-port-memory-slave.h
index 3161073a3f..56959ee124 100644
--- a/include/hw/core/remote-port-memory-slave.h
+++ b/include/hw/core/remote-port-memory-slave.h
@@ -23,7 +23,6 @@ typedef struct RemotePortMemorySlave {
/* private */
SysBusDevice parent;
/* public */
- uint32_t channel_id;
struct RemotePort *rp;
struct rp_peer_state *peer;
MemoryRegion *mr;
@@ -32,6 +31,9 @@ typedef struct RemotePortMemorySlave {
RemotePortDynPkt rsp;
RemotePortATSCache *ats_cache;
Notifier machine_done;
+ uint32_t rp_stream_id;
+ RemotePortATS *rp_ats;
+ uint32_t rp_ats_id;
uint32_t iommu_id;
} RemotePortMemorySlave;
#endif
--
2.43.0
^ permalink raw reply related [flat|nested] 41+ messages in thread