Skip to content

Commit

Permalink
hw/xen: Add gnttab operations to allow redirection to internal emulation
Browse files Browse the repository at this point in the history
Move the existing code using libxengnttab to xen-operations.c and allow
the operations to be redirected so that we can add emulation of grant
table mapping for backend drivers.

In emulation, mapping more than one grant ref to be virtually contiguous
would be fairly difficult. The best way to do it might be to make the
ram_block mappings actually backed by a file (shmem or a deleted file,
perhaps) so that we can have multiple *shared* mappings of it. But that
would be fairly intrusive.

Making the backend drivers cope with page *lists* instead of expecting
the mapping to be contiguous is also non-trivial, since some structures
would actually *cross* page boundaries (e.g. the 32-bit blkif responses
which are 12 bytes).

So for now, we'll support only single-page mappings in emulation. Add a
XEN_GNTTAB_OP_FEATURE_MAP_MULTIPLE flag to indicate that the native Xen
implementation *does* support multi-page maps, and a helper function to
query it.

Signed-off-by: David Woodhouse <dwmw@amazon.co.uk>
Reviewed-by: Paul Durrant <paul@xen.org>
  • Loading branch information
dwmw2 committed Mar 7, 2023
1 parent b6cacfe commit c412ba4
Show file tree
Hide file tree
Showing 9 changed files with 280 additions and 272 deletions.
112 changes: 8 additions & 104 deletions hw/xen/xen-bus.c
Expand Up @@ -947,7 +947,7 @@ static void xen_device_frontend_destroy(XenDevice *xendev)
void xen_device_set_max_grant_refs(XenDevice *xendev, unsigned int nr_refs,
Error **errp)
{
if (xengnttab_set_max_grants(xendev->xgth, nr_refs)) {
if (qemu_xen_gnttab_set_max_grants(xendev->xgth, nr_refs)) {
error_setg_errno(errp, errno, "xengnttab_set_max_grants failed");
}
}
Expand All @@ -956,9 +956,8 @@ void *xen_device_map_grant_refs(XenDevice *xendev, uint32_t *refs,
unsigned int nr_refs, int prot,
Error **errp)
{
void *map = xengnttab_map_domain_grant_refs(xendev->xgth, nr_refs,
xendev->frontend_id, refs,
prot);
void *map = qemu_xen_gnttab_map_refs(xendev->xgth, nr_refs,
xendev->frontend_id, refs, prot);

if (!map) {
error_setg_errno(errp, errno,
Expand All @@ -971,109 +970,17 @@ void *xen_device_map_grant_refs(XenDevice *xendev, uint32_t *refs,
void xen_device_unmap_grant_refs(XenDevice *xendev, void *map,
unsigned int nr_refs, Error **errp)
{
if (xengnttab_unmap(xendev->xgth, map, nr_refs)) {
if (qemu_xen_gnttab_unmap(xendev->xgth, map, nr_refs)) {
error_setg_errno(errp, errno, "xengnttab_unmap failed");
}
}

static void compat_copy_grant_refs(XenDevice *xendev, bool to_domain,
XenDeviceGrantCopySegment segs[],
unsigned int nr_segs, Error **errp)
{
uint32_t *refs = g_new(uint32_t, nr_segs);
int prot = to_domain ? PROT_WRITE : PROT_READ;
void *map;
unsigned int i;

for (i = 0; i < nr_segs; i++) {
XenDeviceGrantCopySegment *seg = &segs[i];

refs[i] = to_domain ? seg->dest.foreign.ref :
seg->source.foreign.ref;
}

map = xengnttab_map_domain_grant_refs(xendev->xgth, nr_segs,
xendev->frontend_id, refs,
prot);
if (!map) {
error_setg_errno(errp, errno,
"xengnttab_map_domain_grant_refs failed");
goto done;
}

for (i = 0; i < nr_segs; i++) {
XenDeviceGrantCopySegment *seg = &segs[i];
void *page = map + (i * XC_PAGE_SIZE);

if (to_domain) {
memcpy(page + seg->dest.foreign.offset, seg->source.virt,
seg->len);
} else {
memcpy(seg->dest.virt, page + seg->source.foreign.offset,
seg->len);
}
}

if (xengnttab_unmap(xendev->xgth, map, nr_segs)) {
error_setg_errno(errp, errno, "xengnttab_unmap failed");
}

done:
g_free(refs);
}

void xen_device_copy_grant_refs(XenDevice *xendev, bool to_domain,
XenDeviceGrantCopySegment segs[],
unsigned int nr_segs, Error **errp)
{
xengnttab_grant_copy_segment_t *xengnttab_segs;
unsigned int i;

if (!xendev->feature_grant_copy) {
compat_copy_grant_refs(xendev, to_domain, segs, nr_segs, errp);
return;
}

xengnttab_segs = g_new0(xengnttab_grant_copy_segment_t, nr_segs);

for (i = 0; i < nr_segs; i++) {
XenDeviceGrantCopySegment *seg = &segs[i];
xengnttab_grant_copy_segment_t *xengnttab_seg = &xengnttab_segs[i];

if (to_domain) {
xengnttab_seg->flags = GNTCOPY_dest_gref;
xengnttab_seg->dest.foreign.domid = xendev->frontend_id;
xengnttab_seg->dest.foreign.ref = seg->dest.foreign.ref;
xengnttab_seg->dest.foreign.offset = seg->dest.foreign.offset;
xengnttab_seg->source.virt = seg->source.virt;
} else {
xengnttab_seg->flags = GNTCOPY_source_gref;
xengnttab_seg->source.foreign.domid = xendev->frontend_id;
xengnttab_seg->source.foreign.ref = seg->source.foreign.ref;
xengnttab_seg->source.foreign.offset =
seg->source.foreign.offset;
xengnttab_seg->dest.virt = seg->dest.virt;
}

xengnttab_seg->len = seg->len;
}

if (xengnttab_grant_copy(xendev->xgth, nr_segs, xengnttab_segs)) {
error_setg_errno(errp, errno, "xengnttab_grant_copy failed");
goto done;
}

for (i = 0; i < nr_segs; i++) {
xengnttab_grant_copy_segment_t *xengnttab_seg = &xengnttab_segs[i];

if (xengnttab_seg->status != GNTST_okay) {
error_setg(errp, "xengnttab_grant_copy seg[%u] failed", i);
break;
}
}

done:
g_free(xengnttab_segs);
qemu_xen_gnttab_grant_copy(xendev->xgth, to_domain, xendev->frontend_id,
(XenGrantCopySegment *)segs, nr_segs, errp);
}

struct XenEventChannel {
Expand Down Expand Up @@ -1235,7 +1142,7 @@ static void xen_device_unrealize(DeviceState *dev)
xen_device_backend_destroy(xendev);

if (xendev->xgth) {
xengnttab_close(xendev->xgth);
qemu_xen_gnttab_close(xendev->xgth);
xendev->xgth = NULL;
}

Expand Down Expand Up @@ -1298,15 +1205,12 @@ static void xen_device_realize(DeviceState *dev, Error **errp)

xendev->watch_list = watch_list_create(xendev->xsh);

xendev->xgth = xengnttab_open(NULL, 0);
xendev->xgth = qemu_xen_gnttab_open();
if (!xendev->xgth) {
error_setg_errno(errp, errno, "failed xengnttab_open");
goto unrealize;
}

xendev->feature_grant_copy =
(xengnttab_grant_copy(xendev->xgth, 0, NULL) == 0);

xen_device_backend_create(xendev, errp);
if (*errp) {
goto unrealize;
Expand Down
125 changes: 11 additions & 114 deletions hw/xen/xen-legacy-backend.c
Expand Up @@ -43,7 +43,6 @@ struct xs_handle *xenstore;
const char *xen_protocol;

/* private */
static bool xen_feature_grant_copy;
static int debug;

int xenstore_write_be_str(struct XenLegacyDevice *xendev, const char *node,
Expand Down Expand Up @@ -113,7 +112,7 @@ void xen_be_set_max_grant_refs(struct XenLegacyDevice *xendev,
{
assert(xendev->ops->flags & DEVOPS_FLAG_NEED_GNTDEV);

if (xengnttab_set_max_grants(xendev->gnttabdev, nr_refs)) {
if (qemu_xen_gnttab_set_max_grants(xendev->gnttabdev, nr_refs)) {
xen_pv_printf(xendev, 0, "xengnttab_set_max_grants failed: %s\n",
strerror(errno));
}
Expand All @@ -126,8 +125,8 @@ void *xen_be_map_grant_refs(struct XenLegacyDevice *xendev, uint32_t *refs,

assert(xendev->ops->flags & DEVOPS_FLAG_NEED_GNTDEV);

ptr = xengnttab_map_domain_grant_refs(xendev->gnttabdev, nr_refs,
xen_domid, refs, prot);
ptr = qemu_xen_gnttab_map_refs(xendev->gnttabdev, nr_refs, xen_domid, refs,
prot);
if (!ptr) {
xen_pv_printf(xendev, 0,
"xengnttab_map_domain_grant_refs failed: %s\n",
Expand All @@ -142,119 +141,27 @@ void xen_be_unmap_grant_refs(struct XenLegacyDevice *xendev, void *ptr,
{
assert(xendev->ops->flags & DEVOPS_FLAG_NEED_GNTDEV);

if (xengnttab_unmap(xendev->gnttabdev, ptr, nr_refs)) {
if (qemu_xen_gnttab_unmap(xendev->gnttabdev, ptr, nr_refs)) {
xen_pv_printf(xendev, 0, "xengnttab_unmap failed: %s\n",
strerror(errno));
}
}

static int compat_copy_grant_refs(struct XenLegacyDevice *xendev,
bool to_domain,
XenGrantCopySegment segs[],
unsigned int nr_segs)
{
uint32_t *refs = g_new(uint32_t, nr_segs);
int prot = to_domain ? PROT_WRITE : PROT_READ;
void *pages;
unsigned int i;

for (i = 0; i < nr_segs; i++) {
XenGrantCopySegment *seg = &segs[i];

refs[i] = to_domain ?
seg->dest.foreign.ref : seg->source.foreign.ref;
}

pages = xengnttab_map_domain_grant_refs(xendev->gnttabdev, nr_segs,
xen_domid, refs, prot);
if (!pages) {
xen_pv_printf(xendev, 0,
"xengnttab_map_domain_grant_refs failed: %s\n",
strerror(errno));
g_free(refs);
return -1;
}

for (i = 0; i < nr_segs; i++) {
XenGrantCopySegment *seg = &segs[i];
void *page = pages + (i * XC_PAGE_SIZE);

if (to_domain) {
memcpy(page + seg->dest.foreign.offset, seg->source.virt,
seg->len);
} else {
memcpy(seg->dest.virt, page + seg->source.foreign.offset,
seg->len);
}
}

if (xengnttab_unmap(xendev->gnttabdev, pages, nr_segs)) {
xen_pv_printf(xendev, 0, "xengnttab_unmap failed: %s\n",
strerror(errno));
}

g_free(refs);
return 0;
}

int xen_be_copy_grant_refs(struct XenLegacyDevice *xendev,
bool to_domain,
XenGrantCopySegment segs[],
unsigned int nr_segs)
{
xengnttab_grant_copy_segment_t *xengnttab_segs;
unsigned int i;
int rc;

assert(xendev->ops->flags & DEVOPS_FLAG_NEED_GNTDEV);

if (!xen_feature_grant_copy) {
return compat_copy_grant_refs(xendev, to_domain, segs, nr_segs);
}

xengnttab_segs = g_new0(xengnttab_grant_copy_segment_t, nr_segs);

for (i = 0; i < nr_segs; i++) {
XenGrantCopySegment *seg = &segs[i];
xengnttab_grant_copy_segment_t *xengnttab_seg = &xengnttab_segs[i];

if (to_domain) {
xengnttab_seg->flags = GNTCOPY_dest_gref;
xengnttab_seg->dest.foreign.domid = xen_domid;
xengnttab_seg->dest.foreign.ref = seg->dest.foreign.ref;
xengnttab_seg->dest.foreign.offset = seg->dest.foreign.offset;
xengnttab_seg->source.virt = seg->source.virt;
} else {
xengnttab_seg->flags = GNTCOPY_source_gref;
xengnttab_seg->source.foreign.domid = xen_domid;
xengnttab_seg->source.foreign.ref = seg->source.foreign.ref;
xengnttab_seg->source.foreign.offset =
seg->source.foreign.offset;
xengnttab_seg->dest.virt = seg->dest.virt;
}

xengnttab_seg->len = seg->len;
}

rc = xengnttab_grant_copy(xendev->gnttabdev, nr_segs, xengnttab_segs);

rc = qemu_xen_gnttab_grant_copy(xendev->gnttabdev, to_domain, xen_domid,
segs, nr_segs, NULL);
if (rc) {
xen_pv_printf(xendev, 0, "xengnttab_copy failed: %s\n",
strerror(errno));
}

for (i = 0; i < nr_segs; i++) {
xengnttab_grant_copy_segment_t *xengnttab_seg =
&xengnttab_segs[i];

if (xengnttab_seg->status != GNTST_okay) {
xen_pv_printf(xendev, 0, "segment[%u] status: %d\n", i,
xengnttab_seg->status);
rc = -1;
}
xen_pv_printf(xendev, 0, "xengnttab_grant_copy failed: %s\n",
strerror(-rc));
}

g_free(xengnttab_segs);
return rc;
}

Expand Down Expand Up @@ -466,7 +373,7 @@ static int xen_be_try_initialise(struct XenLegacyDevice *xendev)
}

if (xendev->ops->flags & DEVOPS_FLAG_NEED_GNTDEV) {
xendev->gnttabdev = xengnttab_open(NULL, 0);
xendev->gnttabdev = qemu_xen_gnttab_open();
if (xendev->gnttabdev == NULL) {
xen_pv_printf(NULL, 0, "can't open gnttab device\n");
return -1;
Expand Down Expand Up @@ -524,7 +431,7 @@ static void xen_be_disconnect(struct XenLegacyDevice *xendev,
xendev->ops->disconnect(xendev);
}
if (xendev->gnttabdev) {
xengnttab_close(xendev->gnttabdev);
qemu_xen_gnttab_close(xendev->gnttabdev);
xendev->gnttabdev = NULL;
}
if (xendev->be_state != state) {
Expand Down Expand Up @@ -687,8 +594,6 @@ static void xen_set_dynamic_sysbus(void)

void xen_be_init(void)
{
xengnttab_handle *gnttabdev;

xenstore = xs_daemon_open();
if (!xenstore) {
xen_pv_printf(NULL, 0, "can't connect to xenstored\n");
Expand All @@ -697,19 +602,11 @@ void xen_be_init(void)

qemu_set_fd_handler(xs_fileno(xenstore), xenstore_update, NULL, NULL);

if (xen_xc == NULL || xen_fmem == NULL) {
if (xen_evtchn_ops == NULL || xen_gnttab_ops == NULL) {
xen_pv_printf(NULL, 0, "Xen operations not set up\n");
exit(1);
}

gnttabdev = xengnttab_open(NULL, 0);
if (gnttabdev != NULL) {
if (xengnttab_grant_copy(gnttabdev, 0, NULL) == 0) {
xen_feature_grant_copy = true;
}
xengnttab_close(gnttabdev);
}

xen_sysdev = qdev_new(TYPE_XENSYSDEV);
sysbus_realize_and_unref(SYS_BUS_DEVICE(xen_sysdev), &error_fatal);
xen_sysbus = qbus_new(TYPE_XENSYSBUS, xen_sysdev, "xen-sysbus");
Expand Down

0 comments on commit c412ba4

Please sign in to comment.