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 "qapi/error.h"
|
||||||
#include "sysemu/runstate.h"
|
#include "sysemu/runstate.h"
|
||||||
#include "hw/pci/pci.h"
|
#include "hw/pci/pci.h"
|
||||||
|
#include "exec/memattrs.h"
|
||||||
|
|
||||||
static void process_config_write(QIOChannel *ioc, PCIDevice *dev,
|
static void process_config_write(QIOChannel *ioc, PCIDevice *dev,
|
||||||
MPQemuMsg *msg, Error **errp);
|
MPQemuMsg *msg, Error **errp);
|
||||||
static void process_config_read(QIOChannel *ioc, PCIDevice *dev,
|
static void process_config_read(QIOChannel *ioc, PCIDevice *dev,
|
||||||
MPQemuMsg *msg, Error **errp);
|
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)
|
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:
|
case MPQEMU_CMD_PCI_CFGREAD:
|
||||||
process_config_read(com->ioc, pci_dev, &msg, &local_err);
|
process_config_read(com->ioc, pci_dev, &msg, &local_err);
|
||||||
break;
|
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:
|
default:
|
||||||
error_setg(&local_err,
|
error_setg(&local_err,
|
||||||
"Unknown command (%d) received for device %s"
|
"Unknown command (%d) received for device %s"
|
||||||
@ -115,3 +124,77 @@ static void process_config_read(QIOChannel *ioc, PCIDevice *dev,
|
|||||||
getpid());
|
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;
|
return false;
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
case MPQEMU_CMD_BAR_WRITE:
|
||||||
|
case MPQEMU_CMD_BAR_READ:
|
||||||
|
if ((msg->size != sizeof(BarAccessMsg)) || (msg->num_fds != 0)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
break;
|
||||||
default:
|
default:
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
@ -152,3 +152,63 @@ static void pci_proxy_dev_register_types(void)
|
|||||||
}
|
}
|
||||||
|
|
||||||
type_init(pci_proxy_dev_register_types)
|
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_RET,
|
||||||
MPQEMU_CMD_PCI_CFGWRITE,
|
MPQEMU_CMD_PCI_CFGWRITE,
|
||||||
MPQEMU_CMD_PCI_CFGREAD,
|
MPQEMU_CMD_PCI_CFGREAD,
|
||||||
|
MPQEMU_CMD_BAR_WRITE,
|
||||||
|
MPQEMU_CMD_BAR_READ,
|
||||||
MPQEMU_CMD_MAX,
|
MPQEMU_CMD_MAX,
|
||||||
} MPQemuCmd;
|
} MPQemuCmd;
|
||||||
|
|
||||||
@ -52,6 +54,13 @@ typedef struct {
|
|||||||
int len;
|
int len;
|
||||||
} PciConfDataMsg;
|
} PciConfDataMsg;
|
||||||
|
|
||||||
|
typedef struct {
|
||||||
|
hwaddr addr;
|
||||||
|
uint64_t val;
|
||||||
|
unsigned size;
|
||||||
|
bool memory;
|
||||||
|
} BarAccessMsg;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* MPQemuMsg:
|
* MPQemuMsg:
|
||||||
* @cmd: The remote command
|
* @cmd: The remote command
|
||||||
@ -71,6 +80,7 @@ typedef struct {
|
|||||||
uint64_t u64;
|
uint64_t u64;
|
||||||
PciConfDataMsg pci_conf_data;
|
PciConfDataMsg pci_conf_data;
|
||||||
SyncSysmemMsg sync_sysmem;
|
SyncSysmemMsg sync_sysmem;
|
||||||
|
BarAccessMsg bar_access;
|
||||||
} data;
|
} data;
|
||||||
|
|
||||||
int fds[REMOTE_MAX_FDS];
|
int fds[REMOTE_MAX_FDS];
|
||||||
|
@ -15,6 +15,14 @@
|
|||||||
#define TYPE_PCI_PROXY_DEV "x-pci-proxy-dev"
|
#define TYPE_PCI_PROXY_DEV "x-pci-proxy-dev"
|
||||||
OBJECT_DECLARE_SIMPLE_TYPE(PCIProxyDev, 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 {
|
struct PCIProxyDev {
|
||||||
PCIDevice parent_dev;
|
PCIDevice parent_dev;
|
||||||
char *fd;
|
char *fd;
|
||||||
@ -28,6 +36,7 @@ struct PCIProxyDev {
|
|||||||
QemuMutex io_mutex;
|
QemuMutex io_mutex;
|
||||||
QIOChannel *ioc;
|
QIOChannel *ioc;
|
||||||
Error *migration_blocker;
|
Error *migration_blocker;
|
||||||
|
ProxyMemoryRegion region[PCI_NUM_REGIONS];
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif /* PROXY_H */
|
#endif /* PROXY_H */
|
||||||
|
Loading…
Reference in New Issue
Block a user