On 2026-10-13 18:09, Philippe Mathieu-Daudé wrote:
Hi,On 2026-09-15 18:00, Ruslan Ruslichenko wrote:From: Ruslan Ruslichenko <[email protected]> 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 memory region read/write into Remote Port read/write packets - Support for propagating of transaction attributes (Secure, MasterID) - Synchronous response waiting with optional timeout (timeout configuration to be added within later patches) - Add trace events for monitoring TX/RX bus transactions Signed-off-by: Edgar E. Iglesias <[email protected]> Signed-off-by: Takahiro Nakata <[email protected]> Signed-off-by: Ruslan Ruslichenko <[email protected]> --- hw/core/remote-port-memory-master.c | 191 +++++++++++++++++++++++++++- hw/core/remote-port.c | 5 + hw/core/trace-events | 4 + include/hw/core/remote-port.h | 11 ++ 4 files changed, 207 insertions(+), 4 deletions(-)diff --git a/hw/core/remote-port-memory-master.c b/hw/core/remote- port-memory-master.cindex 4b0283f9fe..41ef1df518 100644 --- a/hw/core/remote-port-memory-master.c +++ b/hw/core/remote-port-memory-master.c @@ -29,18 +29,201 @@ #define RP_MAX_ACCESS_SIZE 4096 +static int rp_mm_get_timeout(RPMemoryTransaction *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, + RPMemoryTransaction *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 (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);I don't understand this direct memory access. Shouldn't this be handled by the FlatView and end as a subpage_t leaf instead? Doing some archeology to understand: https://lore.kernel.org/qemu-devel/20170204115048.GM12995@toto/
I meant v3: https://lore.kernel.org/qemu-devel/[email protected]/
Amusingly a related backtrace:https://lore.kernel.org/qemu-devel/CAKmqyKOqbJ+-t0SeyZDzDM9RLEQX63WM3- [email protected]/+ } + } + + 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); + return ret; +} + +MemTxResult rp_mm_access(RemotePort *rp, uint32_t rp_dev, + struct rp_peer_state *peer, + RPMemoryTransaction *tr, + bool relative, uint64_t offset) +{+ return rp_mm_access_with_def_attr(rp, rp_dev, peer, tr, relative, offset,+ 0); +} +static MemTxResult rp_mm_read(void *opaque, hwaddr addr, uint64_t *data,unsigned size, MemTxAttrs attrs) { - /* TBD */ - return MEMTX_OK; + MemTxResult ret; + RemotePortMap *map = opaque; + RemotePortMemoryMaster *s = map->parent; + + RPMemoryTransaction tr = {}; + + tr.opaque = map; + tr.rw = false; + tr.addr = addr; + tr.size = size; + tr.attr = attrs; + + ret = rp_mm_access(s->rp, s->rp_dev, s->peer, &tr, s->relative, + map->offset); + + if (ret == MEMTX_OK) { + *data = tr.data.u64; + } + + return ret; }static MemTxResult rp_mm_write(void *opaque, hwaddr addr, uint64_t data,unsigned size, MemTxAttrs attrs) { - /* TBD */ - return MEMTX_OK; + RemotePortMap *map = opaque; + RemotePortMemoryMaster *s = map->parent; + + RPMemoryTransaction tr = {}; + + tr.opaque = map; + tr.rw = true; + tr.addr = addr; + tr.size = size; + tr.attr = attrs; + tr.data.u64 = data; + + 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 b4c9fa1528..6a6effdd19 100644 --- a/hw/core/remote-port.c +++ b/hw/core/remote-port.c @@ -39,6 +39,11 @@ static void rp_event_read_and_process(RemotePort *s); static void sync_timer_hit(void *opaque); static void syncresp_timer_hit(void *opaque); +uint32_t rp_new_id(RemotePort *s) +{ + return qatomic_fetch_inc(&s->current_id); +}
