multi-process: PCI BAR read/write handling for proxy & remote endpoints
Proxy device object implements handler for PCI BAR writes and reads. The handler uses BAR_WRITE/BAR_READ message to communicate to the remote process with the BAR address and value to be written/read. The remote process implements handler for BAR_WRITE/BAR_READ message. Signed-off-by: Jagannathan Raman <jag.raman@oracle.com> Signed-off-by: Elena Ufimtseva <elena.ufimtseva@oracle.com> Signed-off-by: John G Johnson <john.g.johnson@oracle.com> Reviewed-by: Stefan Hajnoczi <stefanha@redhat.com> Message-id: a8b76714a9688be5552c4c92d089bc9e8a4707ff.1611938319.git.jag.raman@oracle.com Signed-off-by: Stefan Hajnoczi <stefanha@redhat.com>
This commit is contained in:
parent
11ab872588
commit
7ee3f82384
@ -16,11 +16,14 @@
|
||||
#include "qapi/error.h"
|
||||
#include "sysemu/runstate.h"
|
||||
#include "hw/pci/pci.h"
|
||||
#include "exec/memattrs.h"
|
||||
|
||||
static void process_config_write(QIOChannel *ioc, PCIDevice *dev,
|
||||
MPQemuMsg *msg, Error **errp);
|
||||
static void process_config_read(QIOChannel *ioc, PCIDevice *dev,
|
||||
MPQemuMsg *msg, Error **errp);
|
||||
static void process_bar_write(QIOChannel *ioc, MPQemuMsg *msg, Error **errp);
|
||||
static void process_bar_read(QIOChannel *ioc, MPQemuMsg *msg, Error **errp);
|
||||
|
||||
void coroutine_fn mpqemu_remote_msg_loop_co(void *data)
|
||||
{
|
||||
@ -52,6 +55,12 @@ void coroutine_fn mpqemu_remote_msg_loop_co(void *data)
|
||||
case MPQEMU_CMD_PCI_CFGREAD:
|
||||
process_config_read(com->ioc, pci_dev, &msg, &local_err);
|
||||
break;
|
||||
case MPQEMU_CMD_BAR_WRITE:
|
||||
process_bar_write(com->ioc, &msg, &local_err);
|
||||
break;
|
||||
case MPQEMU_CMD_BAR_READ:
|
||||
process_bar_read(com->ioc, &msg, &local_err);
|
||||
break;
|
||||
default:
|
||||
error_setg(&local_err,
|
||||
"Unknown command (%d) received for device %s"
|
||||
@ -115,3 +124,77 @@ static void process_config_read(QIOChannel *ioc, PCIDevice *dev,
|
||||
getpid());
|
||||
}
|
||||
}
|
||||
|
||||
static void process_bar_write(QIOChannel *ioc, MPQemuMsg *msg, Error **errp)
|
||||
{
|
||||
ERRP_GUARD();
|
||||
BarAccessMsg *bar_access = &msg->data.bar_access;
|
||||
AddressSpace *as =
|
||||
bar_access->memory ? &address_space_memory : &address_space_io;
|
||||
MPQemuMsg ret = { 0 };
|
||||
MemTxResult res;
|
||||
uint64_t val;
|
||||
|
||||
if (!is_power_of_2(bar_access->size) ||
|
||||
(bar_access->size > sizeof(uint64_t))) {
|
||||
ret.data.u64 = UINT64_MAX;
|
||||
goto fail;
|
||||
}
|
||||
|
||||
val = cpu_to_le64(bar_access->val);
|
||||
|
||||
res = address_space_rw(as, bar_access->addr, MEMTXATTRS_UNSPECIFIED,
|
||||
(void *)&val, bar_access->size, true);
|
||||
|
||||
if (res != MEMTX_OK) {
|
||||
error_setg(errp, "Bad address %"PRIx64" for mem write, pid "FMT_pid".",
|
||||
bar_access->addr, getpid());
|
||||
ret.data.u64 = -1;
|
||||
}
|
||||
|
||||
fail:
|
||||
ret.cmd = MPQEMU_CMD_RET;
|
||||
ret.size = sizeof(ret.data.u64);
|
||||
|
||||
if (!mpqemu_msg_send(&ret, ioc, NULL)) {
|
||||
error_prepend(errp, "Error returning code to proxy, pid "FMT_pid": ",
|
||||
getpid());
|
||||
}
|
||||
}
|
||||
|
||||
static void process_bar_read(QIOChannel *ioc, MPQemuMsg *msg, Error **errp)
|
||||
{
|
||||
ERRP_GUARD();
|
||||
BarAccessMsg *bar_access = &msg->data.bar_access;
|
||||
MPQemuMsg ret = { 0 };
|
||||
AddressSpace *as;
|
||||
MemTxResult res;
|
||||
uint64_t val = 0;
|
||||
|
||||
as = bar_access->memory ? &address_space_memory : &address_space_io;
|
||||
|
||||
if (!is_power_of_2(bar_access->size) ||
|
||||
(bar_access->size > sizeof(uint64_t))) {
|
||||
val = UINT64_MAX;
|
||||
goto fail;
|
||||
}
|
||||
|
||||
res = address_space_rw(as, bar_access->addr, MEMTXATTRS_UNSPECIFIED,
|
||||
(void *)&val, bar_access->size, false);
|
||||
|
||||
if (res != MEMTX_OK) {
|
||||
error_setg(errp, "Bad address %"PRIx64" for mem read, pid "FMT_pid".",
|
||||
bar_access->addr, getpid());
|
||||
val = UINT64_MAX;
|
||||
}
|
||||
|
||||
fail:
|
||||
ret.cmd = MPQEMU_CMD_RET;
|
||||
ret.data.u64 = le64_to_cpu(val);
|
||||
ret.size = sizeof(ret.data.u64);
|
||||
|
||||
if (!mpqemu_msg_send(&ret, ioc, NULL)) {
|
||||
error_prepend(errp, "Error returning code to proxy, pid "FMT_pid": ",
|
||||
getpid());
|
||||
}
|
||||
}
|
||||
|
@ -248,6 +248,12 @@ bool mpqemu_msg_valid(MPQemuMsg *msg)
|
||||
return false;
|
||||
}
|
||||
break;
|
||||
case MPQEMU_CMD_BAR_WRITE:
|
||||
case MPQEMU_CMD_BAR_READ:
|
||||
if ((msg->size != sizeof(BarAccessMsg)) || (msg->num_fds != 0)) {
|
||||
return false;
|
||||
}
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
@ -152,3 +152,63 @@ static void pci_proxy_dev_register_types(void)
|
||||
}
|
||||
|
||||
type_init(pci_proxy_dev_register_types)
|
||||
|
||||
static void send_bar_access_msg(PCIProxyDev *pdev, MemoryRegion *mr,
|
||||
bool write, hwaddr addr, uint64_t *val,
|
||||
unsigned size, bool memory)
|
||||
{
|
||||
MPQemuMsg msg = { 0 };
|
||||
long ret = -EINVAL;
|
||||
Error *local_err = NULL;
|
||||
|
||||
msg.size = sizeof(BarAccessMsg);
|
||||
msg.data.bar_access.addr = mr->addr + addr;
|
||||
msg.data.bar_access.size = size;
|
||||
msg.data.bar_access.memory = memory;
|
||||
|
||||
if (write) {
|
||||
msg.cmd = MPQEMU_CMD_BAR_WRITE;
|
||||
msg.data.bar_access.val = *val;
|
||||
} else {
|
||||
msg.cmd = MPQEMU_CMD_BAR_READ;
|
||||
}
|
||||
|
||||
ret = mpqemu_msg_send_and_await_reply(&msg, pdev, &local_err);
|
||||
if (local_err) {
|
||||
error_report_err(local_err);
|
||||
}
|
||||
|
||||
if (!write) {
|
||||
*val = ret;
|
||||
}
|
||||
}
|
||||
|
||||
static void proxy_bar_write(void *opaque, hwaddr addr, uint64_t val,
|
||||
unsigned size)
|
||||
{
|
||||
ProxyMemoryRegion *pmr = opaque;
|
||||
|
||||
send_bar_access_msg(pmr->dev, &pmr->mr, true, addr, &val, size,
|
||||
pmr->memory);
|
||||
}
|
||||
|
||||
static uint64_t proxy_bar_read(void *opaque, hwaddr addr, unsigned size)
|
||||
{
|
||||
ProxyMemoryRegion *pmr = opaque;
|
||||
uint64_t val;
|
||||
|
||||
send_bar_access_msg(pmr->dev, &pmr->mr, false, addr, &val, size,
|
||||
pmr->memory);
|
||||
|
||||
return val;
|
||||
}
|
||||
|
||||
const MemoryRegionOps proxy_mr_ops = {
|
||||
.read = proxy_bar_read,
|
||||
.write = proxy_bar_write,
|
||||
.endianness = DEVICE_NATIVE_ENDIAN,
|
||||
.impl = {
|
||||
.min_access_size = 1,
|
||||
.max_access_size = 8,
|
||||
},
|
||||
};
|
||||
|
@ -37,6 +37,8 @@ typedef enum {
|
||||
MPQEMU_CMD_RET,
|
||||
MPQEMU_CMD_PCI_CFGWRITE,
|
||||
MPQEMU_CMD_PCI_CFGREAD,
|
||||
MPQEMU_CMD_BAR_WRITE,
|
||||
MPQEMU_CMD_BAR_READ,
|
||||
MPQEMU_CMD_MAX,
|
||||
} MPQemuCmd;
|
||||
|
||||
@ -52,6 +54,13 @@ typedef struct {
|
||||
int len;
|
||||
} PciConfDataMsg;
|
||||
|
||||
typedef struct {
|
||||
hwaddr addr;
|
||||
uint64_t val;
|
||||
unsigned size;
|
||||
bool memory;
|
||||
} BarAccessMsg;
|
||||
|
||||
/**
|
||||
* MPQemuMsg:
|
||||
* @cmd: The remote command
|
||||
@ -71,6 +80,7 @@ typedef struct {
|
||||
uint64_t u64;
|
||||
PciConfDataMsg pci_conf_data;
|
||||
SyncSysmemMsg sync_sysmem;
|
||||
BarAccessMsg bar_access;
|
||||
} data;
|
||||
|
||||
int fds[REMOTE_MAX_FDS];
|
||||
|
@ -15,6 +15,14 @@
|
||||
#define TYPE_PCI_PROXY_DEV "x-pci-proxy-dev"
|
||||
OBJECT_DECLARE_SIMPLE_TYPE(PCIProxyDev, PCI_PROXY_DEV)
|
||||
|
||||
typedef struct ProxyMemoryRegion {
|
||||
PCIProxyDev *dev;
|
||||
MemoryRegion mr;
|
||||
bool memory;
|
||||
bool present;
|
||||
uint8_t type;
|
||||
} ProxyMemoryRegion;
|
||||
|
||||
struct PCIProxyDev {
|
||||
PCIDevice parent_dev;
|
||||
char *fd;
|
||||
@ -28,6 +36,7 @@ struct PCIProxyDev {
|
||||
QemuMutex io_mutex;
|
||||
QIOChannel *ioc;
|
||||
Error *migration_blocker;
|
||||
ProxyMemoryRegion region[PCI_NUM_REGIONS];
|
||||
};
|
||||
|
||||
#endif /* PROXY_H */
|
||||
|
Loading…
Reference in New Issue
Block a user