lguest: make Launcher see device status updates
This brings us closer to Real Life, where we'd examine the device features once it's set the DRIVER_OK status bit. Signed-off-by: Rusty Russell <rusty@rustcorp.com.au>
This commit is contained in:
parent
9f3f746741
commit
a007a751d9
@ -131,6 +131,9 @@ struct device
|
|||||||
/* Any queues attached to this device */
|
/* Any queues attached to this device */
|
||||||
struct virtqueue *vq;
|
struct virtqueue *vq;
|
||||||
|
|
||||||
|
/* Handle status being finalized (ie. feature bits stable). */
|
||||||
|
void (*ready)(struct device *me);
|
||||||
|
|
||||||
/* Device-specific data. */
|
/* Device-specific data. */
|
||||||
void *priv;
|
void *priv;
|
||||||
};
|
};
|
||||||
@ -925,14 +928,14 @@ static void enable_fd(int fd, struct virtqueue *vq)
|
|||||||
write(waker_fd, &vq->dev->fd, sizeof(vq->dev->fd));
|
write(waker_fd, &vq->dev->fd, sizeof(vq->dev->fd));
|
||||||
}
|
}
|
||||||
|
|
||||||
/* When the Guest asks us to reset a device, it's is fairly easy. */
|
/* When the Guest tells us they updated the status field, we handle it. */
|
||||||
static void reset_device(struct device *dev)
|
static void update_device_status(struct device *dev)
|
||||||
{
|
{
|
||||||
struct virtqueue *vq;
|
struct virtqueue *vq;
|
||||||
|
|
||||||
|
/* This is a reset. */
|
||||||
|
if (dev->desc->status == 0) {
|
||||||
verbose("Resetting device %s\n", dev->name);
|
verbose("Resetting device %s\n", dev->name);
|
||||||
/* Clear the status. */
|
|
||||||
dev->desc->status = 0;
|
|
||||||
|
|
||||||
/* Clear any features they've acked. */
|
/* Clear any features they've acked. */
|
||||||
memset(get_feature_bits(dev) + dev->desc->feature_len, 0,
|
memset(get_feature_bits(dev) + dev->desc->feature_len, 0,
|
||||||
@ -944,6 +947,22 @@ static void reset_device(struct device *dev)
|
|||||||
vring_size(vq->config.num, getpagesize()));
|
vring_size(vq->config.num, getpagesize()));
|
||||||
vq->last_avail_idx = 0;
|
vq->last_avail_idx = 0;
|
||||||
}
|
}
|
||||||
|
} else if (dev->desc->status & VIRTIO_CONFIG_S_FAILED) {
|
||||||
|
warnx("Device %s configuration FAILED", dev->name);
|
||||||
|
} else if (dev->desc->status & VIRTIO_CONFIG_S_DRIVER_OK) {
|
||||||
|
unsigned int i;
|
||||||
|
|
||||||
|
verbose("Device %s OK: offered", dev->name);
|
||||||
|
for (i = 0; i < dev->desc->feature_len; i++)
|
||||||
|
verbose(" %08x", get_feature_bits(dev)[i]);
|
||||||
|
verbose(", accepted");
|
||||||
|
for (i = 0; i < dev->desc->feature_len; i++)
|
||||||
|
verbose(" %08x", get_feature_bits(dev)
|
||||||
|
[dev->desc->feature_len+i]);
|
||||||
|
|
||||||
|
if (dev->ready)
|
||||||
|
dev->ready(dev);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/* This is the generic routine we call when the Guest uses LHCALL_NOTIFY. */
|
/* This is the generic routine we call when the Guest uses LHCALL_NOTIFY. */
|
||||||
@ -954,9 +973,9 @@ static void handle_output(int fd, unsigned long addr)
|
|||||||
|
|
||||||
/* Check each device and virtqueue. */
|
/* Check each device and virtqueue. */
|
||||||
for (i = devices.dev; i; i = i->next) {
|
for (i = devices.dev; i; i = i->next) {
|
||||||
/* Notifications to device descriptors reset the device. */
|
/* Notifications to device descriptors update device status. */
|
||||||
if (from_guest_phys(addr) == i->desc) {
|
if (from_guest_phys(addr) == i->desc) {
|
||||||
reset_device(i);
|
update_device_status(i);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1170,6 +1189,7 @@ static struct device *new_device(const char *name, u16 type, int fd,
|
|||||||
dev->handle_input = handle_input;
|
dev->handle_input = handle_input;
|
||||||
dev->name = name;
|
dev->name = name;
|
||||||
dev->vq = NULL;
|
dev->vq = NULL;
|
||||||
|
dev->ready = NULL;
|
||||||
|
|
||||||
/* Append to device list. Prepending to a single-linked list is
|
/* Append to device list. Prepending to a single-linked list is
|
||||||
* easier, but the user expects the devices to be arranged on the bus
|
* easier, but the user expects the devices to be arranged on the bus
|
||||||
|
@ -144,22 +144,28 @@ static u8 lg_get_status(struct virtio_device *vdev)
|
|||||||
return to_lgdev(vdev)->desc->status;
|
return to_lgdev(vdev)->desc->status;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void lg_set_status(struct virtio_device *vdev, u8 status)
|
/* To notify on status updates, we (ab)use the NOTIFY hypercall, with the
|
||||||
{
|
* descriptor address of the device. A zero status means "reset". */
|
||||||
BUG_ON(!status);
|
static void set_status(struct virtio_device *vdev, u8 status)
|
||||||
to_lgdev(vdev)->desc->status = status;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* To reset the device, we (ab)use the NOTIFY hypercall, with the descriptor
|
|
||||||
* address of the device. The Host will zero the status and all the
|
|
||||||
* features. */
|
|
||||||
static void lg_reset(struct virtio_device *vdev)
|
|
||||||
{
|
{
|
||||||
unsigned long offset = (void *)to_lgdev(vdev)->desc - lguest_devices;
|
unsigned long offset = (void *)to_lgdev(vdev)->desc - lguest_devices;
|
||||||
|
|
||||||
|
/* We set the status. */
|
||||||
|
to_lgdev(vdev)->desc->status = status;
|
||||||
hcall(LHCALL_NOTIFY, (max_pfn<<PAGE_SHIFT) + offset, 0, 0);
|
hcall(LHCALL_NOTIFY, (max_pfn<<PAGE_SHIFT) + offset, 0, 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void lg_set_status(struct virtio_device *vdev, u8 status)
|
||||||
|
{
|
||||||
|
BUG_ON(!status);
|
||||||
|
set_status(vdev, status);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void lg_reset(struct virtio_device *vdev)
|
||||||
|
{
|
||||||
|
set_status(vdev, 0);
|
||||||
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Virtqueues
|
* Virtqueues
|
||||||
*
|
*
|
||||||
|
Loading…
Reference in New Issue
Block a user