mirror of
https://github.com/Motorhead1991/qemu.git
synced 2025-08-01 06:43:53 -06:00
Xen PV guest support for 8.2
Add Xen PV console and network support, the former of which enables the Xen "PV shim" to be used to support PV guests. Also clean up the block support and make it work when the user passes just 'drive file=IMAGE,if=xen' on the command line. Update the documentation to reflect all of these, taking the opportunity to simplify what it says about q35 by making unplug work for AHCI. Ignore the VCPU_SSHOTTMR_future timer flag, and advertise the 'fixed' per-vCPU upcall vector support, as newer upstream Xen do. -----BEGIN PGP SIGNATURE----- iQJIBAABCAAyFiEEvgfZ/VSAmrLEsP9fY3Ys2mfi81kFAmVJ/7EUHGR3bXcyQGlu ZnJhZGVhZC5vcmcACgkQY3Ys2mfi81k+/xAAswivVR4+nwz3wTSN7EboGogS3hy+ ZsTpvbJnfprGQJAK8vv8OP4eunaCJkO/dy3M/33Dh270msmV6I/1ki0E1RIPG45D n5wKM1Zxk0ABvjIgdp3xiLwITTdruJ+k9aqV8U9quhjgNFdOa7yjBOG8MD32GEPZ KHbavJ++huOu7+DZHJRNRq4gI/fREIULoPGHVg7WuEiRDYokOOmMROXqmTHTaUkV yFhkofzWxlpYhh7qRQx6/A80CSf7xwCof8krjdMCOYj3XGzYVZND0z5ZfHQYEwqt fowhargA8gH4V3d21S/MWCaZ+QrswFXZhcnl5wuGgWakV4ChvFETKs+fz2mODWUx 2T13trqeFJ5ElTrSpH1iWCoSEy6KCeLecvx7c/6HPSkDYQ3w5q8dXPpqgEtXY24S Wcmw4PkQ+HrLX7wbSU7QLyTZjvCQLFZ3Sb0uTf2zwsJZyeCCiT2lqAaogoMm6Kg0 m/jG1JzE+9AC3j0Upp1lS3EK1qdxIuLdBuIcaEBEjy7Am+Y14PlZYoU2c751KbRF kqnIOYMoijX0PJDomPqCQtYNE0mrtogo0AbcFFIu+4k25vGbkl7xS5p2du9qw2Rd ++IdqQYzdzrUcIwmxocFQqFBJQ2dcbOGB1d7+VJ+A1Uj3yY2/DnFG5WqSaqS0KJi ZhBdFs3OTlPnRoM= =Dg79 -----END PGP SIGNATURE----- Merge tag 'pull-xenfv.for-upstream-20231107' of git://git.infradead.org/users/dwmw2/qemu into staging Xen PV guest support for 8.2 Add Xen PV console and network support, the former of which enables the Xen "PV shim" to be used to support PV guests. Also clean up the block support and make it work when the user passes just 'drive file=IMAGE,if=xen' on the command line. Update the documentation to reflect all of these, taking the opportunity to simplify what it says about q35 by making unplug work for AHCI. Ignore the VCPU_SSHOTTMR_future timer flag, and advertise the 'fixed' per-vCPU upcall vector support, as newer upstream Xen do. # -----BEGIN PGP SIGNATURE----- # # iQJIBAABCAAyFiEEvgfZ/VSAmrLEsP9fY3Ys2mfi81kFAmVJ/7EUHGR3bXcyQGlu # ZnJhZGVhZC5vcmcACgkQY3Ys2mfi81k+/xAAswivVR4+nwz3wTSN7EboGogS3hy+ # ZsTpvbJnfprGQJAK8vv8OP4eunaCJkO/dy3M/33Dh270msmV6I/1ki0E1RIPG45D # n5wKM1Zxk0ABvjIgdp3xiLwITTdruJ+k9aqV8U9quhjgNFdOa7yjBOG8MD32GEPZ # KHbavJ++huOu7+DZHJRNRq4gI/fREIULoPGHVg7WuEiRDYokOOmMROXqmTHTaUkV # yFhkofzWxlpYhh7qRQx6/A80CSf7xwCof8krjdMCOYj3XGzYVZND0z5ZfHQYEwqt # fowhargA8gH4V3d21S/MWCaZ+QrswFXZhcnl5wuGgWakV4ChvFETKs+fz2mODWUx # 2T13trqeFJ5ElTrSpH1iWCoSEy6KCeLecvx7c/6HPSkDYQ3w5q8dXPpqgEtXY24S # Wcmw4PkQ+HrLX7wbSU7QLyTZjvCQLFZ3Sb0uTf2zwsJZyeCCiT2lqAaogoMm6Kg0 # m/jG1JzE+9AC3j0Upp1lS3EK1qdxIuLdBuIcaEBEjy7Am+Y14PlZYoU2c751KbRF # kqnIOYMoijX0PJDomPqCQtYNE0mrtogo0AbcFFIu+4k25vGbkl7xS5p2du9qw2Rd # ++IdqQYzdzrUcIwmxocFQqFBJQ2dcbOGB1d7+VJ+A1Uj3yY2/DnFG5WqSaqS0KJi # ZhBdFs3OTlPnRoM= # =Dg79 # -----END PGP SIGNATURE----- # gpg: Signature made Tue 07 Nov 2023 17:13:21 HKT # gpg: using RSA key BE07D9FD54809AB2C4B0FF5F63762CDA67E2F359 # gpg: issuer "dwmw2@infradead.org" # gpg: Good signature from "David Woodhouse <dwmw2@infradead.org>" [unknown] # gpg: aka "David Woodhouse <dwmw2@exim.org>" [unknown] # gpg: aka "David Woodhouse <david@woodhou.se>" [unknown] # gpg: aka "David Woodhouse <dwmw2@kernel.org>" [unknown] # gpg: WARNING: The key's User ID is not certified with a trusted signature! # gpg: There is no indication that the signature belongs to the owner. # Primary key fingerprint: BE07 D9FD 5480 9AB2 C4B0 FF5F 6376 2CDA 67E2 F359 * tag 'pull-xenfv.for-upstream-20231107' of git://git.infradead.org/users/dwmw2/qemu: docs: update Xen-on-KVM documentation xen-platform: unplug AHCI disks hw/i386/pc: support '-nic' for xen-net-device hw/xen: update Xen PV NIC to XenDevice model hw/xen: only remove peers of PCI NICs on unplug hw/xen: add support for Xen primary console in emulated mode hw/xen: update Xen console to XenDevice model hw/xen: do not repeatedly try to create a failing backend device hw/xen: add get_frontend_path() method to XenDeviceClass hw/xen: automatically assign device index to block devices hw/xen: populate store frontend nodes with XenStore PFN/port i386/xen: advertise XEN_HVM_CPUID_UPCALL_VECTOR in CPUID include: update Xen public headers to Xen 4.17.2 release hw/xen: Clean up event channel 'type_val' handling to use union i386/xen: Ignore VCPU_SSHOTTMR_future flag in set_singleshot_timer() Signed-off-by: Stefan Hajnoczi <stefanha@redhat.com>
This commit is contained in:
commit
3e34860a3a
61 changed files with 1652 additions and 995 deletions
|
@ -27,13 +27,119 @@
|
|||
#include "sysemu/block-backend.h"
|
||||
#include "sysemu/iothread.h"
|
||||
#include "dataplane/xen-block.h"
|
||||
#include "hw/xen/interface/io/xs_wire.h"
|
||||
#include "trace.h"
|
||||
|
||||
#define XVDA_MAJOR 202
|
||||
#define XVDQ_MAJOR (1 << 20)
|
||||
#define XVDBGQCV_MAJOR ((1 << 21) - 1)
|
||||
#define HDA_MAJOR 3
|
||||
#define HDC_MAJOR 22
|
||||
#define SDA_MAJOR 8
|
||||
|
||||
|
||||
static int vdev_to_diskno(unsigned int vdev_nr)
|
||||
{
|
||||
switch (vdev_nr >> 8) {
|
||||
case XVDA_MAJOR:
|
||||
case SDA_MAJOR:
|
||||
return (vdev_nr >> 4) & 0x15;
|
||||
|
||||
case HDA_MAJOR:
|
||||
return (vdev_nr >> 6) & 1;
|
||||
|
||||
case HDC_MAJOR:
|
||||
return ((vdev_nr >> 6) & 1) + 2;
|
||||
|
||||
case XVDQ_MAJOR ... XVDBGQCV_MAJOR:
|
||||
return (vdev_nr >> 8) & 0xfffff;
|
||||
|
||||
default:
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
#define MAX_AUTO_VDEV 4096
|
||||
|
||||
/*
|
||||
* Find a free device name in the xvda → xvdfan range and set it in
|
||||
* blockdev->props.vdev. Our definition of "free" is that there must
|
||||
* be no other disk or partition with the same disk number.
|
||||
*
|
||||
* You are technically permitted to have all of hda, hda1, sda, sda1,
|
||||
* xvda and xvda1 as *separate* PV block devices with separate backing
|
||||
* stores. That doesn't make it a good idea. This code will skip xvda
|
||||
* if *any* of those "conflicting" devices already exists.
|
||||
*
|
||||
* The limit of xvdfan (disk 4095) is fairly arbitrary just to avoid a
|
||||
* stupidly sized bitmap, but Linux as of v6.6 doesn't support anything
|
||||
* higher than that anyway.
|
||||
*/
|
||||
static bool xen_block_find_free_vdev(XenBlockDevice *blockdev, Error **errp)
|
||||
{
|
||||
XenBus *xenbus = XEN_BUS(qdev_get_parent_bus(DEVICE(blockdev)));
|
||||
unsigned long used_devs[BITS_TO_LONGS(MAX_AUTO_VDEV)];
|
||||
XenBlockVdev *vdev = &blockdev->props.vdev;
|
||||
char fe_path[XENSTORE_ABS_PATH_MAX + 1];
|
||||
char **existing_frontends;
|
||||
unsigned int nr_existing = 0;
|
||||
unsigned int vdev_nr;
|
||||
int i, disk = 0;
|
||||
|
||||
snprintf(fe_path, sizeof(fe_path), "/local/domain/%u/device/vbd",
|
||||
blockdev->xendev.frontend_id);
|
||||
|
||||
existing_frontends = qemu_xen_xs_directory(xenbus->xsh, XBT_NULL, fe_path,
|
||||
&nr_existing);
|
||||
if (!existing_frontends && errno != ENOENT) {
|
||||
error_setg_errno(errp, errno, "cannot read %s", fe_path);
|
||||
return false;
|
||||
}
|
||||
|
||||
memset(used_devs, 0, sizeof(used_devs));
|
||||
for (i = 0; i < nr_existing; i++) {
|
||||
if (qemu_strtoui(existing_frontends[i], NULL, 10, &vdev_nr)) {
|
||||
free(existing_frontends[i]);
|
||||
continue;
|
||||
}
|
||||
|
||||
free(existing_frontends[i]);
|
||||
|
||||
disk = vdev_to_diskno(vdev_nr);
|
||||
if (disk < 0 || disk >= MAX_AUTO_VDEV) {
|
||||
continue;
|
||||
}
|
||||
|
||||
set_bit(disk, used_devs);
|
||||
}
|
||||
free(existing_frontends);
|
||||
|
||||
disk = find_first_zero_bit(used_devs, MAX_AUTO_VDEV);
|
||||
if (disk == MAX_AUTO_VDEV) {
|
||||
error_setg(errp, "cannot find device vdev for block device");
|
||||
return false;
|
||||
}
|
||||
|
||||
vdev->type = XEN_BLOCK_VDEV_TYPE_XVD;
|
||||
vdev->partition = 0;
|
||||
vdev->disk = disk;
|
||||
if (disk < (1 << 4)) {
|
||||
vdev->number = (XVDA_MAJOR << 8) | (disk << 4);
|
||||
} else {
|
||||
vdev->number = (XVDQ_MAJOR << 8) | (disk << 8);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
static char *xen_block_get_name(XenDevice *xendev, Error **errp)
|
||||
{
|
||||
XenBlockDevice *blockdev = XEN_BLOCK_DEVICE(xendev);
|
||||
XenBlockVdev *vdev = &blockdev->props.vdev;
|
||||
|
||||
if (vdev->type == XEN_BLOCK_VDEV_TYPE_INVALID &&
|
||||
!xen_block_find_free_vdev(blockdev, errp)) {
|
||||
return NULL;
|
||||
}
|
||||
return g_strdup_printf("%lu", vdev->number);
|
||||
}
|
||||
|
||||
|
@ -482,10 +588,10 @@ static void xen_block_set_vdev(Object *obj, Visitor *v, const char *name,
|
|||
case XEN_BLOCK_VDEV_TYPE_DP:
|
||||
case XEN_BLOCK_VDEV_TYPE_XVD:
|
||||
if (vdev->disk < (1 << 4) && vdev->partition < (1 << 4)) {
|
||||
vdev->number = (202 << 8) | (vdev->disk << 4) |
|
||||
vdev->number = (XVDA_MAJOR << 8) | (vdev->disk << 4) |
|
||||
vdev->partition;
|
||||
} else if (vdev->disk < (1 << 20) && vdev->partition < (1 << 8)) {
|
||||
vdev->number = (1 << 28) | (vdev->disk << 8) |
|
||||
vdev->number = (XVDQ_MAJOR << 8) | (vdev->disk << 8) |
|
||||
vdev->partition;
|
||||
} else {
|
||||
goto invalid;
|
||||
|
@ -495,10 +601,11 @@ static void xen_block_set_vdev(Object *obj, Visitor *v, const char *name,
|
|||
case XEN_BLOCK_VDEV_TYPE_HD:
|
||||
if ((vdev->disk == 0 || vdev->disk == 1) &&
|
||||
vdev->partition < (1 << 6)) {
|
||||
vdev->number = (3 << 8) | (vdev->disk << 6) | vdev->partition;
|
||||
vdev->number = (HDA_MAJOR << 8) | (vdev->disk << 6) |
|
||||
vdev->partition;
|
||||
} else if ((vdev->disk == 2 || vdev->disk == 3) &&
|
||||
vdev->partition < (1 << 6)) {
|
||||
vdev->number = (22 << 8) | ((vdev->disk - 2) << 6) |
|
||||
vdev->number = (HDC_MAJOR << 8) | ((vdev->disk - 2) << 6) |
|
||||
vdev->partition;
|
||||
} else {
|
||||
goto invalid;
|
||||
|
@ -507,7 +614,8 @@ static void xen_block_set_vdev(Object *obj, Visitor *v, const char *name,
|
|||
|
||||
case XEN_BLOCK_VDEV_TYPE_SD:
|
||||
if (vdev->disk < (1 << 4) && vdev->partition < (1 << 4)) {
|
||||
vdev->number = (8 << 8) | (vdev->disk << 4) | vdev->partition;
|
||||
vdev->number = (SDA_MAJOR << 8) | (vdev->disk << 4) |
|
||||
vdev->partition;
|
||||
} else {
|
||||
goto invalid;
|
||||
}
|
||||
|
|
|
@ -105,3 +105,11 @@ cadence_uart_baudrate(unsigned baudrate) "baudrate %u"
|
|||
# sh_serial.c
|
||||
sh_serial_read(char *id, unsigned size, uint64_t offs, uint64_t val) " %s size %d offs 0x%02" PRIx64 " -> 0x%02" PRIx64
|
||||
sh_serial_write(char *id, unsigned size, uint64_t offs, uint64_t val) "%s size %d offs 0x%02" PRIx64 " <- 0x%02" PRIx64
|
||||
|
||||
# xen_console.c
|
||||
xen_console_connect(unsigned int idx, unsigned int ring_ref, unsigned int port, unsigned int limit) "idx %u ring_ref %u port %u limit %u"
|
||||
xen_console_disconnect(unsigned int idx) "idx %u"
|
||||
xen_console_unrealize(unsigned int idx) "idx %u"
|
||||
xen_console_realize(unsigned int idx, const char *chrdev) "idx %u chrdev %s"
|
||||
xen_console_device_create(unsigned int idx) "idx %u"
|
||||
xen_console_device_destroy(unsigned int idx) "idx %u"
|
||||
|
|
|
@ -20,15 +20,22 @@
|
|||
*/
|
||||
|
||||
#include "qemu/osdep.h"
|
||||
#include "qemu/cutils.h"
|
||||
#include <sys/select.h>
|
||||
#include <termios.h>
|
||||
|
||||
#include "qapi/error.h"
|
||||
#include "sysemu/sysemu.h"
|
||||
#include "chardev/char-fe.h"
|
||||
#include "hw/xen/xen-legacy-backend.h"
|
||||
|
||||
#include "hw/xen/xen-backend.h"
|
||||
#include "hw/xen/xen-bus-helper.h"
|
||||
#include "hw/qdev-properties.h"
|
||||
#include "hw/qdev-properties-system.h"
|
||||
#include "hw/xen/interface/io/console.h"
|
||||
#include "hw/xen/interface/io/xs_wire.h"
|
||||
#include "hw/xen/interface/grant_table.h"
|
||||
#include "hw/i386/kvm/xen_primary_console.h"
|
||||
#include "trace.h"
|
||||
|
||||
struct buffer {
|
||||
uint8_t *data;
|
||||
|
@ -39,16 +46,22 @@ struct buffer {
|
|||
};
|
||||
|
||||
struct XenConsole {
|
||||
struct XenLegacyDevice xendev; /* must be first */
|
||||
struct XenDevice xendev; /* must be first */
|
||||
XenEventChannel *event_channel;
|
||||
int dev;
|
||||
struct buffer buffer;
|
||||
char console[XEN_BUFSIZE];
|
||||
int ring_ref;
|
||||
char *fe_path;
|
||||
unsigned int ring_ref;
|
||||
void *sring;
|
||||
CharBackend chr;
|
||||
int backlog;
|
||||
};
|
||||
typedef struct XenConsole XenConsole;
|
||||
|
||||
static void buffer_append(struct XenConsole *con)
|
||||
#define TYPE_XEN_CONSOLE_DEVICE "xen-console"
|
||||
OBJECT_DECLARE_SIMPLE_TYPE(XenConsole, XEN_CONSOLE_DEVICE)
|
||||
|
||||
static bool buffer_append(XenConsole *con)
|
||||
{
|
||||
struct buffer *buffer = &con->buffer;
|
||||
XENCONS_RING_IDX cons, prod, size;
|
||||
|
@ -60,7 +73,7 @@ static void buffer_append(struct XenConsole *con)
|
|||
|
||||
size = prod - cons;
|
||||
if ((size == 0) || (size > sizeof(intf->out)))
|
||||
return;
|
||||
return false;
|
||||
|
||||
if ((buffer->capacity - buffer->size) < size) {
|
||||
buffer->capacity += (size + 1024);
|
||||
|
@ -73,7 +86,7 @@ static void buffer_append(struct XenConsole *con)
|
|||
|
||||
xen_mb();
|
||||
intf->out_cons = cons;
|
||||
xen_pv_send_notify(&con->xendev);
|
||||
xen_device_notify_event_channel(XEN_DEVICE(con), con->event_channel, NULL);
|
||||
|
||||
if (buffer->max_capacity &&
|
||||
buffer->size > buffer->max_capacity) {
|
||||
|
@ -89,6 +102,7 @@ static void buffer_append(struct XenConsole *con)
|
|||
if (buffer->consumed > buffer->max_capacity - over)
|
||||
buffer->consumed = buffer->max_capacity - over;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
static void buffer_advance(struct buffer *buffer, size_t len)
|
||||
|
@ -100,7 +114,7 @@ static void buffer_advance(struct buffer *buffer, size_t len)
|
|||
}
|
||||
}
|
||||
|
||||
static int ring_free_bytes(struct XenConsole *con)
|
||||
static int ring_free_bytes(XenConsole *con)
|
||||
{
|
||||
struct xencons_interface *intf = con->sring;
|
||||
XENCONS_RING_IDX cons, prod, space;
|
||||
|
@ -118,13 +132,13 @@ static int ring_free_bytes(struct XenConsole *con)
|
|||
|
||||
static int xencons_can_receive(void *opaque)
|
||||
{
|
||||
struct XenConsole *con = opaque;
|
||||
XenConsole *con = opaque;
|
||||
return ring_free_bytes(con);
|
||||
}
|
||||
|
||||
static void xencons_receive(void *opaque, const uint8_t *buf, int len)
|
||||
{
|
||||
struct XenConsole *con = opaque;
|
||||
XenConsole *con = opaque;
|
||||
struct xencons_interface *intf = con->sring;
|
||||
XENCONS_RING_IDX prod;
|
||||
int i, max;
|
||||
|
@ -141,10 +155,10 @@ static void xencons_receive(void *opaque, const uint8_t *buf, int len)
|
|||
}
|
||||
xen_wmb();
|
||||
intf->in_prod = prod;
|
||||
xen_pv_send_notify(&con->xendev);
|
||||
xen_device_notify_event_channel(XEN_DEVICE(con), con->event_channel, NULL);
|
||||
}
|
||||
|
||||
static void xencons_send(struct XenConsole *con)
|
||||
static bool xencons_send(XenConsole *con)
|
||||
{
|
||||
ssize_t len, size;
|
||||
|
||||
|
@ -159,174 +173,472 @@ static void xencons_send(struct XenConsole *con)
|
|||
if (len < 1) {
|
||||
if (!con->backlog) {
|
||||
con->backlog = 1;
|
||||
xen_pv_printf(&con->xendev, 1,
|
||||
"backlog piling up, nobody listening?\n");
|
||||
}
|
||||
} else {
|
||||
buffer_advance(&con->buffer, len);
|
||||
if (con->backlog && len == size) {
|
||||
con->backlog = 0;
|
||||
xen_pv_printf(&con->xendev, 1, "backlog is gone\n");
|
||||
}
|
||||
}
|
||||
return len > 0;
|
||||
}
|
||||
|
||||
/* -------------------------------------------------------------------- */
|
||||
|
||||
static int store_con_info(struct XenConsole *con)
|
||||
static bool con_event(void *_xendev)
|
||||
{
|
||||
Chardev *cs = qemu_chr_fe_get_driver(&con->chr);
|
||||
char *pts = NULL;
|
||||
char *dom_path;
|
||||
g_autoptr(GString) path = NULL;
|
||||
XenConsole *con = XEN_CONSOLE_DEVICE(_xendev);
|
||||
bool done_something;
|
||||
|
||||
/* Only continue if we're talking to a pty. */
|
||||
if (!CHARDEV_IS_PTY(cs)) {
|
||||
return 0;
|
||||
}
|
||||
pts = cs->filename + 4;
|
||||
|
||||
dom_path = qemu_xen_xs_get_domain_path(xenstore, xen_domid);
|
||||
if (!dom_path) {
|
||||
return 0;
|
||||
if (xen_device_backend_get_state(&con->xendev) != XenbusStateConnected) {
|
||||
return false;
|
||||
}
|
||||
|
||||
path = g_string_new(dom_path);
|
||||
free(dom_path);
|
||||
done_something = buffer_append(con);
|
||||
|
||||
if (con->xendev.dev) {
|
||||
g_string_append_printf(path, "/device/console/%d", con->xendev.dev);
|
||||
} else {
|
||||
g_string_append(path, "/console");
|
||||
if (con->buffer.size - con->buffer.consumed) {
|
||||
done_something |= xencons_send(con);
|
||||
}
|
||||
g_string_append(path, "/tty");
|
||||
|
||||
if (xenstore_write_str(con->console, path->str, pts)) {
|
||||
fprintf(stderr, "xenstore_write_str for '%s' fail", path->str);
|
||||
return -1;
|
||||
}
|
||||
return 0;
|
||||
return done_something;
|
||||
}
|
||||
|
||||
static int con_init(struct XenLegacyDevice *xendev)
|
||||
/* -------------------------------------------------------------------- */
|
||||
|
||||
static bool xen_console_connect(XenDevice *xendev, Error **errp)
|
||||
{
|
||||
struct XenConsole *con = container_of(xendev, struct XenConsole, xendev);
|
||||
char *type, *dom, label[32];
|
||||
int ret = 0;
|
||||
const char *output;
|
||||
XenConsole *con = XEN_CONSOLE_DEVICE(xendev);
|
||||
unsigned int port, limit;
|
||||
|
||||
/* setup */
|
||||
dom = qemu_xen_xs_get_domain_path(xenstore, con->xendev.dom);
|
||||
if (!xendev->dev) {
|
||||
snprintf(con->console, sizeof(con->console), "%s/console", dom);
|
||||
} else {
|
||||
snprintf(con->console, sizeof(con->console), "%s/device/console/%d", dom, xendev->dev);
|
||||
}
|
||||
free(dom);
|
||||
|
||||
type = xenstore_read_str(con->console, "type");
|
||||
if (!type || strcmp(type, "ioemu") != 0) {
|
||||
xen_pv_printf(xendev, 1, "not for me (type=%s)\n", type);
|
||||
ret = -1;
|
||||
goto out;
|
||||
if (xen_device_frontend_scanf(xendev, "ring-ref", "%u",
|
||||
&con->ring_ref) != 1) {
|
||||
error_setg(errp, "failed to read ring-ref");
|
||||
return false;
|
||||
}
|
||||
|
||||
output = xenstore_read_str(con->console, "output");
|
||||
|
||||
/* no Xen override, use qemu output device */
|
||||
if (output == NULL) {
|
||||
if (con->xendev.dev) {
|
||||
qemu_chr_fe_init(&con->chr, serial_hd(con->xendev.dev),
|
||||
&error_abort);
|
||||
}
|
||||
} else {
|
||||
snprintf(label, sizeof(label), "xencons%d", con->xendev.dev);
|
||||
qemu_chr_fe_init(&con->chr,
|
||||
/*
|
||||
* FIXME: sure we want to support implicit
|
||||
* muxed monitors here?
|
||||
*/
|
||||
qemu_chr_new_mux_mon(label, output, NULL),
|
||||
&error_abort);
|
||||
if (xen_device_frontend_scanf(xendev, "port", "%u", &port) != 1) {
|
||||
error_setg(errp, "failed to read remote port");
|
||||
return false;
|
||||
}
|
||||
|
||||
store_con_info(con);
|
||||
|
||||
out:
|
||||
g_free(type);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int con_initialise(struct XenLegacyDevice *xendev)
|
||||
{
|
||||
struct XenConsole *con = container_of(xendev, struct XenConsole, xendev);
|
||||
int limit;
|
||||
|
||||
if (xenstore_read_int(con->console, "ring-ref", &con->ring_ref) == -1)
|
||||
return -1;
|
||||
if (xenstore_read_int(con->console, "port", &con->xendev.remote_port) == -1)
|
||||
return -1;
|
||||
if (xenstore_read_int(con->console, "limit", &limit) == 0)
|
||||
if (xen_device_frontend_scanf(xendev, "limit", "%u", &limit) == 1) {
|
||||
con->buffer.max_capacity = limit;
|
||||
|
||||
if (!xendev->dev) {
|
||||
xen_pfn_t mfn = con->ring_ref;
|
||||
con->sring = qemu_xen_foreignmem_map(con->xendev.dom, NULL,
|
||||
PROT_READ | PROT_WRITE,
|
||||
1, &mfn, NULL);
|
||||
} else {
|
||||
con->sring = xen_be_map_grant_ref(xendev, con->ring_ref,
|
||||
PROT_READ | PROT_WRITE);
|
||||
}
|
||||
if (!con->sring)
|
||||
return -1;
|
||||
|
||||
xen_be_bind_evtchn(&con->xendev);
|
||||
con->event_channel = xen_device_bind_event_channel(xendev, port,
|
||||
con_event,
|
||||
con,
|
||||
errp);
|
||||
if (!con->event_channel) {
|
||||
return false;
|
||||
}
|
||||
|
||||
switch (con->dev) {
|
||||
case 0:
|
||||
/*
|
||||
* The primary console is special. For real Xen the ring-ref is
|
||||
* actually a GFN which needs to be mapped as foreignmem.
|
||||
*/
|
||||
if (xen_mode != XEN_EMULATE) {
|
||||
xen_pfn_t mfn = (xen_pfn_t)con->ring_ref;
|
||||
con->sring = qemu_xen_foreignmem_map(xendev->frontend_id, NULL,
|
||||
PROT_READ | PROT_WRITE,
|
||||
1, &mfn, NULL);
|
||||
if (!con->sring) {
|
||||
error_setg(errp, "failed to map console page");
|
||||
return false;
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
/*
|
||||
* For Xen emulation, we still follow the convention of ring-ref
|
||||
* holding the GFN, but we map the fixed GNTTAB_RESERVED_CONSOLE
|
||||
* grant ref because there is no implementation of foreignmem
|
||||
* operations for emulated mode. The emulation code which handles
|
||||
* the guest-side page and event channel also needs to be informed
|
||||
* of the backend event channel port, in order to reconnect to it
|
||||
* after a soft reset.
|
||||
*/
|
||||
xen_primary_console_set_be_port(
|
||||
xen_event_channel_get_local_port(con->event_channel));
|
||||
con->ring_ref = GNTTAB_RESERVED_CONSOLE;
|
||||
/* fallthrough */
|
||||
default:
|
||||
con->sring = xen_device_map_grant_refs(xendev,
|
||||
&con->ring_ref, 1,
|
||||
PROT_READ | PROT_WRITE,
|
||||
errp);
|
||||
if (!con->sring) {
|
||||
error_prepend(errp, "failed to map console grant ref: ");
|
||||
return false;
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
trace_xen_console_connect(con->dev, con->ring_ref, port,
|
||||
con->buffer.max_capacity);
|
||||
|
||||
qemu_chr_fe_set_handlers(&con->chr, xencons_can_receive,
|
||||
xencons_receive, NULL, NULL, con, NULL, true);
|
||||
|
||||
xen_pv_printf(xendev, 1,
|
||||
"ring mfn %d, remote port %d, local port %d, limit %zd\n",
|
||||
con->ring_ref,
|
||||
con->xendev.remote_port,
|
||||
con->xendev.local_port,
|
||||
con->buffer.max_capacity);
|
||||
return 0;
|
||||
xencons_receive, NULL, NULL, con, NULL,
|
||||
true);
|
||||
return true;
|
||||
}
|
||||
|
||||
static void con_disconnect(struct XenLegacyDevice *xendev)
|
||||
static void xen_console_disconnect(XenDevice *xendev, Error **errp)
|
||||
{
|
||||
struct XenConsole *con = container_of(xendev, struct XenConsole, xendev);
|
||||
XenConsole *con = XEN_CONSOLE_DEVICE(xendev);
|
||||
|
||||
qemu_chr_fe_deinit(&con->chr, false);
|
||||
xen_pv_unbind_evtchn(&con->xendev);
|
||||
trace_xen_console_disconnect(con->dev);
|
||||
|
||||
qemu_chr_fe_set_handlers(&con->chr, NULL, NULL, NULL, NULL,
|
||||
con, NULL, true);
|
||||
|
||||
if (con->event_channel) {
|
||||
xen_device_unbind_event_channel(xendev, con->event_channel,
|
||||
errp);
|
||||
con->event_channel = NULL;
|
||||
|
||||
if (xen_mode == XEN_EMULATE && !con->dev) {
|
||||
xen_primary_console_set_be_port(0);
|
||||
}
|
||||
}
|
||||
|
||||
if (con->sring) {
|
||||
if (!xendev->dev) {
|
||||
if (!con->dev && xen_mode != XEN_EMULATE) {
|
||||
qemu_xen_foreignmem_unmap(con->sring, 1);
|
||||
} else {
|
||||
xen_be_unmap_grant_ref(xendev, con->sring, con->ring_ref);
|
||||
xen_device_unmap_grant_refs(xendev, con->sring,
|
||||
&con->ring_ref, 1, errp);
|
||||
}
|
||||
con->sring = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
static void con_event(struct XenLegacyDevice *xendev)
|
||||
static void xen_console_frontend_changed(XenDevice *xendev,
|
||||
enum xenbus_state frontend_state,
|
||||
Error **errp)
|
||||
{
|
||||
struct XenConsole *con = container_of(xendev, struct XenConsole, xendev);
|
||||
ERRP_GUARD();
|
||||
enum xenbus_state backend_state = xen_device_backend_get_state(xendev);
|
||||
|
||||
buffer_append(con);
|
||||
if (con->buffer.size - con->buffer.consumed)
|
||||
xencons_send(con);
|
||||
switch (frontend_state) {
|
||||
case XenbusStateInitialised:
|
||||
case XenbusStateConnected:
|
||||
if (backend_state == XenbusStateConnected) {
|
||||
break;
|
||||
}
|
||||
|
||||
xen_console_disconnect(xendev, errp);
|
||||
if (*errp) {
|
||||
break;
|
||||
}
|
||||
|
||||
if (!xen_console_connect(xendev, errp)) {
|
||||
xen_device_backend_set_state(xendev, XenbusStateClosing);
|
||||
break;
|
||||
}
|
||||
|
||||
xen_device_backend_set_state(xendev, XenbusStateConnected);
|
||||
break;
|
||||
|
||||
case XenbusStateClosing:
|
||||
xen_device_backend_set_state(xendev, XenbusStateClosing);
|
||||
break;
|
||||
|
||||
case XenbusStateClosed:
|
||||
case XenbusStateUnknown:
|
||||
xen_console_disconnect(xendev, errp);
|
||||
if (*errp) {
|
||||
break;
|
||||
}
|
||||
|
||||
xen_device_backend_set_state(xendev, XenbusStateClosed);
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/* -------------------------------------------------------------------- */
|
||||
static char *xen_console_get_name(XenDevice *xendev, Error **errp)
|
||||
{
|
||||
XenConsole *con = XEN_CONSOLE_DEVICE(xendev);
|
||||
|
||||
struct XenDevOps xen_console_ops = {
|
||||
.size = sizeof(struct XenConsole),
|
||||
.flags = DEVOPS_FLAG_IGNORE_STATE|DEVOPS_FLAG_NEED_GNTDEV,
|
||||
.init = con_init,
|
||||
.initialise = con_initialise,
|
||||
.event = con_event,
|
||||
.disconnect = con_disconnect,
|
||||
if (con->dev == -1) {
|
||||
XenBus *xenbus = XEN_BUS(qdev_get_parent_bus(DEVICE(xendev)));
|
||||
char fe_path[XENSTORE_ABS_PATH_MAX + 1];
|
||||
int idx = (xen_mode == XEN_EMULATE) ? 0 : 1;
|
||||
char *value;
|
||||
|
||||
/* Theoretically we could go up to INT_MAX here but that's overkill */
|
||||
while (idx < 100) {
|
||||
if (!idx) {
|
||||
snprintf(fe_path, sizeof(fe_path),
|
||||
"/local/domain/%u/console", xendev->frontend_id);
|
||||
} else {
|
||||
snprintf(fe_path, sizeof(fe_path),
|
||||
"/local/domain/%u/device/console/%u",
|
||||
xendev->frontend_id, idx);
|
||||
}
|
||||
value = qemu_xen_xs_read(xenbus->xsh, XBT_NULL, fe_path, NULL);
|
||||
if (!value) {
|
||||
if (errno == ENOENT) {
|
||||
con->dev = idx;
|
||||
goto found;
|
||||
}
|
||||
error_setg(errp, "cannot read %s: %s", fe_path,
|
||||
strerror(errno));
|
||||
return NULL;
|
||||
}
|
||||
free(value);
|
||||
idx++;
|
||||
}
|
||||
error_setg(errp, "cannot find device index for console device");
|
||||
return NULL;
|
||||
}
|
||||
found:
|
||||
return g_strdup_printf("%u", con->dev);
|
||||
}
|
||||
|
||||
static void xen_console_unrealize(XenDevice *xendev)
|
||||
{
|
||||
XenConsole *con = XEN_CONSOLE_DEVICE(xendev);
|
||||
|
||||
trace_xen_console_unrealize(con->dev);
|
||||
|
||||
/* Disconnect from the frontend in case this has not already happened */
|
||||
xen_console_disconnect(xendev, NULL);
|
||||
|
||||
qemu_chr_fe_deinit(&con->chr, false);
|
||||
}
|
||||
|
||||
static void xen_console_realize(XenDevice *xendev, Error **errp)
|
||||
{
|
||||
ERRP_GUARD();
|
||||
XenConsole *con = XEN_CONSOLE_DEVICE(xendev);
|
||||
Chardev *cs = qemu_chr_fe_get_driver(&con->chr);
|
||||
unsigned int u;
|
||||
|
||||
if (!cs) {
|
||||
error_setg(errp, "no backing character device");
|
||||
return;
|
||||
}
|
||||
|
||||
if (con->dev == -1) {
|
||||
error_setg(errp, "no device index provided");
|
||||
return;
|
||||
}
|
||||
|
||||
/*
|
||||
* The Xen primary console is special. The ring-ref is actually a GFN to
|
||||
* be mapped directly as foreignmem (not a grant ref), and the guest port
|
||||
* was allocated *for* the guest by the toolstack. The guest gets these
|
||||
* through HVMOP_get_param and can use the console long before it's got
|
||||
* XenStore up and running. We cannot create those for a true Xen guest,
|
||||
* but we can for Xen emulation.
|
||||
*/
|
||||
if (!con->dev) {
|
||||
if (xen_mode == XEN_EMULATE) {
|
||||
xen_primary_console_create();
|
||||
} else if (xen_device_frontend_scanf(xendev, "ring-ref", "%u", &u)
|
||||
!= 1 ||
|
||||
xen_device_frontend_scanf(xendev, "port", "%u", &u) != 1) {
|
||||
error_setg(errp, "cannot create primary Xen console");
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
trace_xen_console_realize(con->dev, object_get_typename(OBJECT(cs)));
|
||||
|
||||
if (CHARDEV_IS_PTY(cs)) {
|
||||
/* Strip the leading 'pty:' */
|
||||
xen_device_frontend_printf(xendev, "tty", "%s", cs->filename + 4);
|
||||
}
|
||||
|
||||
/* No normal PV driver initialization for the primary console under Xen */
|
||||
if (!con->dev && xen_mode != XEN_EMULATE) {
|
||||
xen_console_connect(xendev, errp);
|
||||
}
|
||||
}
|
||||
|
||||
static char *console_frontend_path(struct qemu_xs_handle *xenstore,
|
||||
unsigned int dom_id, unsigned int dev)
|
||||
{
|
||||
if (!dev) {
|
||||
return g_strdup_printf("/local/domain/%u/console", dom_id);
|
||||
} else {
|
||||
return g_strdup_printf("/local/domain/%u/device/console/%u", dom_id,
|
||||
dev);
|
||||
}
|
||||
}
|
||||
|
||||
static char *xen_console_get_frontend_path(XenDevice *xendev, Error **errp)
|
||||
{
|
||||
XenConsole *con = XEN_CONSOLE_DEVICE(xendev);
|
||||
XenBus *xenbus = XEN_BUS(qdev_get_parent_bus(DEVICE(xendev)));
|
||||
char *ret = console_frontend_path(xenbus->xsh, xendev->frontend_id,
|
||||
con->dev);
|
||||
|
||||
if (!ret) {
|
||||
error_setg(errp, "failed to create frontend path");
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
||||
static Property xen_console_properties[] = {
|
||||
DEFINE_PROP_CHR("chardev", XenConsole, chr),
|
||||
DEFINE_PROP_INT32("idx", XenConsole, dev, -1),
|
||||
DEFINE_PROP_END_OF_LIST(),
|
||||
};
|
||||
|
||||
static void xen_console_class_init(ObjectClass *class, void *data)
|
||||
{
|
||||
DeviceClass *dev_class = DEVICE_CLASS(class);
|
||||
XenDeviceClass *xendev_class = XEN_DEVICE_CLASS(class);
|
||||
|
||||
xendev_class->backend = "console";
|
||||
xendev_class->device = "console";
|
||||
xendev_class->get_name = xen_console_get_name;
|
||||
xendev_class->realize = xen_console_realize;
|
||||
xendev_class->frontend_changed = xen_console_frontend_changed;
|
||||
xendev_class->unrealize = xen_console_unrealize;
|
||||
xendev_class->get_frontend_path = xen_console_get_frontend_path;
|
||||
|
||||
device_class_set_props(dev_class, xen_console_properties);
|
||||
}
|
||||
|
||||
static const TypeInfo xen_console_type_info = {
|
||||
.name = TYPE_XEN_CONSOLE_DEVICE,
|
||||
.parent = TYPE_XEN_DEVICE,
|
||||
.instance_size = sizeof(XenConsole),
|
||||
.class_init = xen_console_class_init,
|
||||
};
|
||||
|
||||
static void xen_console_register_types(void)
|
||||
{
|
||||
type_register_static(&xen_console_type_info);
|
||||
}
|
||||
|
||||
type_init(xen_console_register_types)
|
||||
|
||||
/* Called to instantiate a XenConsole when the backend is detected. */
|
||||
static void xen_console_device_create(XenBackendInstance *backend,
|
||||
QDict *opts, Error **errp)
|
||||
{
|
||||
ERRP_GUARD();
|
||||
XenBus *xenbus = xen_backend_get_bus(backend);
|
||||
const char *name = xen_backend_get_name(backend);
|
||||
unsigned long number;
|
||||
char *fe = NULL, *type = NULL, *output = NULL;
|
||||
char label[32];
|
||||
XenDevice *xendev = NULL;
|
||||
XenConsole *con;
|
||||
Chardev *cd = NULL;
|
||||
struct qemu_xs_handle *xsh = xenbus->xsh;
|
||||
|
||||
if (qemu_strtoul(name, NULL, 10, &number) || number > INT_MAX) {
|
||||
error_setg(errp, "failed to parse name '%s'", name);
|
||||
goto fail;
|
||||
}
|
||||
|
||||
trace_xen_console_device_create(number);
|
||||
|
||||
fe = console_frontend_path(xsh, xen_domid, number);
|
||||
if (fe == NULL) {
|
||||
error_setg(errp, "failed to generate frontend path");
|
||||
goto fail;
|
||||
}
|
||||
|
||||
if (xs_node_scanf(xsh, XBT_NULL, fe, "type", errp, "%ms", &type) != 1) {
|
||||
error_prepend(errp, "failed to read console device type: ");
|
||||
goto fail;
|
||||
}
|
||||
|
||||
if (strcmp(type, "ioemu")) {
|
||||
error_setg(errp, "declining to handle console type '%s'",
|
||||
type);
|
||||
goto fail;
|
||||
}
|
||||
|
||||
xendev = XEN_DEVICE(qdev_new(TYPE_XEN_CONSOLE_DEVICE));
|
||||
con = XEN_CONSOLE_DEVICE(xendev);
|
||||
|
||||
con->dev = number;
|
||||
|
||||
snprintf(label, sizeof(label), "xencons%ld", number);
|
||||
|
||||
if (xs_node_scanf(xsh, XBT_NULL, fe, "output", NULL, "%ms", &output) == 1) {
|
||||
/*
|
||||
* FIXME: sure we want to support implicit
|
||||
* muxed monitors here?
|
||||
*/
|
||||
cd = qemu_chr_new_mux_mon(label, output, NULL);
|
||||
if (!cd) {
|
||||
error_setg(errp, "console: No valid chardev found at '%s': ",
|
||||
output);
|
||||
goto fail;
|
||||
}
|
||||
} else if (number) {
|
||||
cd = serial_hd(number);
|
||||
if (!cd) {
|
||||
error_prepend(errp, "console: No serial device #%ld found: ",
|
||||
number);
|
||||
goto fail;
|
||||
}
|
||||
} else {
|
||||
/* No 'output' node on primary console: use null. */
|
||||
cd = qemu_chr_new(label, "null", NULL);
|
||||
if (!cd) {
|
||||
error_setg(errp, "console: failed to create null device");
|
||||
goto fail;
|
||||
}
|
||||
}
|
||||
|
||||
if (!qemu_chr_fe_init(&con->chr, cd, errp)) {
|
||||
error_prepend(errp, "console: failed to initialize backing chardev: ");
|
||||
goto fail;
|
||||
}
|
||||
|
||||
if (qdev_realize_and_unref(DEVICE(xendev), BUS(xenbus), errp)) {
|
||||
xen_backend_set_device(backend, xendev);
|
||||
goto done;
|
||||
}
|
||||
|
||||
error_prepend(errp, "realization of console device %lu failed: ",
|
||||
number);
|
||||
|
||||
fail:
|
||||
if (xendev) {
|
||||
object_unparent(OBJECT(xendev));
|
||||
}
|
||||
done:
|
||||
g_free(fe);
|
||||
free(type);
|
||||
free(output);
|
||||
}
|
||||
|
||||
static void xen_console_device_destroy(XenBackendInstance *backend,
|
||||
Error **errp)
|
||||
{
|
||||
ERRP_GUARD();
|
||||
XenDevice *xendev = xen_backend_get_device(backend);
|
||||
XenConsole *con = XEN_CONSOLE_DEVICE(xendev);
|
||||
|
||||
trace_xen_console_device_destroy(con->dev);
|
||||
|
||||
object_unparent(OBJECT(xendev));
|
||||
}
|
||||
|
||||
static const XenBackendInfo xen_console_backend_info = {
|
||||
.type = "console",
|
||||
.create = xen_console_device_create,
|
||||
.destroy = xen_console_device_destroy,
|
||||
};
|
||||
|
||||
static void xen_console_register_backend(void)
|
||||
{
|
||||
xen_backend_register(&xen_console_backend_info);
|
||||
}
|
||||
|
||||
xen_backend_init(xen_console_register_backend);
|
||||
|
|
|
@ -9,6 +9,7 @@ i386_kvm_ss.add(when: 'CONFIG_XEN_EMU', if_true: files(
|
|||
'xen_evtchn.c',
|
||||
'xen_gnttab.c',
|
||||
'xen_xenstore.c',
|
||||
'xen_primary_console.c',
|
||||
'xenstore_impl.c',
|
||||
))
|
||||
|
||||
|
|
|
@ -18,3 +18,5 @@ xenstore_watch(const char *path, const char *token) "path %s token %s"
|
|||
xenstore_unwatch(const char *path, const char *token) "path %s token %s"
|
||||
xenstore_reset_watches(void) ""
|
||||
xenstore_watch_event(const char *path, const char *token) "path %s token %s"
|
||||
xen_primary_console_create(void) ""
|
||||
xen_primary_console_reset(int port) "port %u"
|
||||
|
|
|
@ -15,6 +15,7 @@
|
|||
#include "qapi/qapi-commands-misc-target.h"
|
||||
|
||||
#include "xen_evtchn.h"
|
||||
#include "xen_primary_console.h"
|
||||
|
||||
void xen_evtchn_snoop_msi(PCIDevice *dev, bool is_msix, unsigned int vector,
|
||||
uint64_t addr, uint32_t data, bool is_masked)
|
||||
|
@ -30,6 +31,13 @@ bool xen_evtchn_deliver_pirq_msi(uint64_t address, uint32_t data)
|
|||
return false;
|
||||
}
|
||||
|
||||
void xen_primary_console_create(void)
|
||||
{
|
||||
}
|
||||
|
||||
void xen_primary_console_set_be_port(uint16_t port)
|
||||
{
|
||||
}
|
||||
#ifdef TARGET_I386
|
||||
EvtchnInfoList *qmp_xen_event_list(Error **errp)
|
||||
{
|
||||
|
|
|
@ -58,7 +58,15 @@ OBJECT_DECLARE_SIMPLE_TYPE(XenEvtchnState, XEN_EVTCHN)
|
|||
typedef struct XenEvtchnPort {
|
||||
uint32_t vcpu; /* Xen/ACPI vcpu_id */
|
||||
uint16_t type; /* EVTCHNSTAT_xxxx */
|
||||
uint16_t type_val; /* pirq# / virq# / remote port according to type */
|
||||
union {
|
||||
uint16_t val; /* raw value for serialization etc. */
|
||||
uint16_t pirq;
|
||||
uint16_t virq;
|
||||
struct {
|
||||
uint16_t port:15;
|
||||
uint16_t to_qemu:1; /* Only two targets; qemu or loopback */
|
||||
} interdomain;
|
||||
} u;
|
||||
} XenEvtchnPort;
|
||||
|
||||
/* 32-bit compatibility definitions, also used natively in 32-bit build */
|
||||
|
@ -105,14 +113,6 @@ struct xenevtchn_handle {
|
|||
int fd;
|
||||
};
|
||||
|
||||
/*
|
||||
* For unbound/interdomain ports there are only two possible remote
|
||||
* domains; self and QEMU. Use a single high bit in type_val for that,
|
||||
* and the low bits for the remote port number (or 0 for unbound).
|
||||
*/
|
||||
#define PORT_INFO_TYPEVAL_REMOTE_QEMU 0x8000
|
||||
#define PORT_INFO_TYPEVAL_REMOTE_PORT_MASK 0x7FFF
|
||||
|
||||
/*
|
||||
* These 'emuirq' values are used by Xen in the LM stream... and yes, I am
|
||||
* insane enough to think about guest-transparent live migration from actual
|
||||
|
@ -210,16 +210,16 @@ static int xen_evtchn_post_load(void *opaque, int version_id)
|
|||
XenEvtchnPort *p = &s->port_table[i];
|
||||
|
||||
if (p->type == EVTCHNSTAT_pirq) {
|
||||
assert(p->type_val);
|
||||
assert(p->type_val < s->nr_pirqs);
|
||||
assert(p->u.pirq);
|
||||
assert(p->u.pirq < s->nr_pirqs);
|
||||
|
||||
/*
|
||||
* Set the gsi to IRQ_UNBOUND; it may be changed to an actual
|
||||
* GSI# below, or to IRQ_MSI_EMU when the MSI table snooping
|
||||
* catches up with it.
|
||||
*/
|
||||
s->pirq[p->type_val].gsi = IRQ_UNBOUND;
|
||||
s->pirq[p->type_val].port = i;
|
||||
s->pirq[p->u.pirq].gsi = IRQ_UNBOUND;
|
||||
s->pirq[p->u.pirq].port = i;
|
||||
}
|
||||
}
|
||||
/* Rebuild s->pirq[].gsi mapping */
|
||||
|
@ -243,7 +243,7 @@ static const VMStateDescription xen_evtchn_port_vmstate = {
|
|||
.fields = (VMStateField[]) {
|
||||
VMSTATE_UINT32(vcpu, XenEvtchnPort),
|
||||
VMSTATE_UINT16(type, XenEvtchnPort),
|
||||
VMSTATE_UINT16(type_val, XenEvtchnPort),
|
||||
VMSTATE_UINT16(u.val, XenEvtchnPort),
|
||||
VMSTATE_END_OF_LIST()
|
||||
}
|
||||
};
|
||||
|
@ -605,14 +605,13 @@ static void unbind_backend_ports(XenEvtchnState *s)
|
|||
|
||||
for (i = 1; i < s->nr_ports; i++) {
|
||||
p = &s->port_table[i];
|
||||
if (p->type == EVTCHNSTAT_interdomain &&
|
||||
(p->type_val & PORT_INFO_TYPEVAL_REMOTE_QEMU)) {
|
||||
evtchn_port_t be_port = p->type_val & PORT_INFO_TYPEVAL_REMOTE_PORT_MASK;
|
||||
if (p->type == EVTCHNSTAT_interdomain && p->u.interdomain.to_qemu) {
|
||||
evtchn_port_t be_port = p->u.interdomain.port;
|
||||
|
||||
if (s->be_handles[be_port]) {
|
||||
/* This part will be overwritten on the load anyway. */
|
||||
p->type = EVTCHNSTAT_unbound;
|
||||
p->type_val = PORT_INFO_TYPEVAL_REMOTE_QEMU;
|
||||
p->u.interdomain.port = 0;
|
||||
|
||||
/* Leave the backend port open and unbound too. */
|
||||
if (kvm_xen_has_cap(EVTCHN_SEND)) {
|
||||
|
@ -650,30 +649,22 @@ int xen_evtchn_status_op(struct evtchn_status *status)
|
|||
|
||||
switch (p->type) {
|
||||
case EVTCHNSTAT_unbound:
|
||||
if (p->type_val & PORT_INFO_TYPEVAL_REMOTE_QEMU) {
|
||||
status->u.unbound.dom = DOMID_QEMU;
|
||||
} else {
|
||||
status->u.unbound.dom = xen_domid;
|
||||
}
|
||||
status->u.unbound.dom = p->u.interdomain.to_qemu ? DOMID_QEMU
|
||||
: xen_domid;
|
||||
break;
|
||||
|
||||
case EVTCHNSTAT_interdomain:
|
||||
if (p->type_val & PORT_INFO_TYPEVAL_REMOTE_QEMU) {
|
||||
status->u.interdomain.dom = DOMID_QEMU;
|
||||
} else {
|
||||
status->u.interdomain.dom = xen_domid;
|
||||
}
|
||||
|
||||
status->u.interdomain.port = p->type_val &
|
||||
PORT_INFO_TYPEVAL_REMOTE_PORT_MASK;
|
||||
status->u.interdomain.dom = p->u.interdomain.to_qemu ? DOMID_QEMU
|
||||
: xen_domid;
|
||||
status->u.interdomain.port = p->u.interdomain.port;
|
||||
break;
|
||||
|
||||
case EVTCHNSTAT_pirq:
|
||||
status->u.pirq = p->type_val;
|
||||
status->u.pirq = p->u.pirq;
|
||||
break;
|
||||
|
||||
case EVTCHNSTAT_virq:
|
||||
status->u.virq = p->type_val;
|
||||
status->u.virq = p->u.virq;
|
||||
break;
|
||||
}
|
||||
|
||||
|
@ -989,7 +980,7 @@ static int clear_port_pending(XenEvtchnState *s, evtchn_port_t port)
|
|||
static void free_port(XenEvtchnState *s, evtchn_port_t port)
|
||||
{
|
||||
s->port_table[port].type = EVTCHNSTAT_closed;
|
||||
s->port_table[port].type_val = 0;
|
||||
s->port_table[port].u.val = 0;
|
||||
s->port_table[port].vcpu = 0;
|
||||
|
||||
if (s->nr_ports == port + 1) {
|
||||
|
@ -1012,7 +1003,7 @@ static int allocate_port(XenEvtchnState *s, uint32_t vcpu, uint16_t type,
|
|||
if (s->port_table[p].type == EVTCHNSTAT_closed) {
|
||||
s->port_table[p].vcpu = vcpu;
|
||||
s->port_table[p].type = type;
|
||||
s->port_table[p].type_val = val;
|
||||
s->port_table[p].u.val = val;
|
||||
|
||||
*port = p;
|
||||
|
||||
|
@ -1053,15 +1044,15 @@ static int close_port(XenEvtchnState *s, evtchn_port_t port,
|
|||
return -ENOENT;
|
||||
|
||||
case EVTCHNSTAT_pirq:
|
||||
s->pirq[p->type_val].port = 0;
|
||||
if (s->pirq[p->type_val].is_translated) {
|
||||
s->pirq[p->u.pirq].port = 0;
|
||||
if (s->pirq[p->u.pirq].is_translated) {
|
||||
*flush_kvm_routes = true;
|
||||
}
|
||||
break;
|
||||
|
||||
case EVTCHNSTAT_virq:
|
||||
kvm_xen_set_vcpu_virq(virq_is_global(p->type_val) ? 0 : p->vcpu,
|
||||
p->type_val, 0);
|
||||
kvm_xen_set_vcpu_virq(virq_is_global(p->u.virq) ? 0 : p->vcpu,
|
||||
p->u.virq, 0);
|
||||
break;
|
||||
|
||||
case EVTCHNSTAT_ipi:
|
||||
|
@ -1071,8 +1062,8 @@ static int close_port(XenEvtchnState *s, evtchn_port_t port,
|
|||
break;
|
||||
|
||||
case EVTCHNSTAT_interdomain:
|
||||
if (p->type_val & PORT_INFO_TYPEVAL_REMOTE_QEMU) {
|
||||
uint16_t be_port = p->type_val & ~PORT_INFO_TYPEVAL_REMOTE_QEMU;
|
||||
if (p->u.interdomain.to_qemu) {
|
||||
uint16_t be_port = p->u.interdomain.port;
|
||||
struct xenevtchn_handle *xc = s->be_handles[be_port];
|
||||
if (xc) {
|
||||
if (kvm_xen_has_cap(EVTCHN_SEND)) {
|
||||
|
@ -1082,14 +1073,15 @@ static int close_port(XenEvtchnState *s, evtchn_port_t port,
|
|||
}
|
||||
} else {
|
||||
/* Loopback interdomain */
|
||||
XenEvtchnPort *rp = &s->port_table[p->type_val];
|
||||
if (!valid_port(p->type_val) || rp->type_val != port ||
|
||||
XenEvtchnPort *rp = &s->port_table[p->u.interdomain.port];
|
||||
if (!valid_port(p->u.interdomain.port) ||
|
||||
rp->u.interdomain.port != port ||
|
||||
rp->type != EVTCHNSTAT_interdomain) {
|
||||
error_report("Inconsistent state for interdomain unbind");
|
||||
} else {
|
||||
/* Set the other end back to unbound */
|
||||
rp->type = EVTCHNSTAT_unbound;
|
||||
rp->type_val = 0;
|
||||
rp->u.interdomain.port = 0;
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
@ -1214,7 +1206,7 @@ int xen_evtchn_bind_vcpu_op(struct evtchn_bind_vcpu *vcpu)
|
|||
if (p->type == EVTCHNSTAT_interdomain ||
|
||||
p->type == EVTCHNSTAT_unbound ||
|
||||
p->type == EVTCHNSTAT_pirq ||
|
||||
(p->type == EVTCHNSTAT_virq && virq_is_global(p->type_val))) {
|
||||
(p->type == EVTCHNSTAT_virq && virq_is_global(p->u.virq))) {
|
||||
/*
|
||||
* unmask_port() with do_unmask==false will just raise the event
|
||||
* on the new vCPU if the port was already pending.
|
||||
|
@ -1359,19 +1351,15 @@ int xen_evtchn_bind_ipi_op(struct evtchn_bind_ipi *ipi)
|
|||
int xen_evtchn_bind_interdomain_op(struct evtchn_bind_interdomain *interdomain)
|
||||
{
|
||||
XenEvtchnState *s = xen_evtchn_singleton;
|
||||
uint16_t type_val;
|
||||
int ret;
|
||||
|
||||
if (!s) {
|
||||
return -ENOTSUP;
|
||||
}
|
||||
|
||||
if (interdomain->remote_dom == DOMID_QEMU) {
|
||||
type_val = PORT_INFO_TYPEVAL_REMOTE_QEMU;
|
||||
} else if (interdomain->remote_dom == DOMID_SELF ||
|
||||
interdomain->remote_dom == xen_domid) {
|
||||
type_val = 0;
|
||||
} else {
|
||||
if (interdomain->remote_dom != DOMID_QEMU &&
|
||||
interdomain->remote_dom != DOMID_SELF &&
|
||||
interdomain->remote_dom != xen_domid) {
|
||||
return -ESRCH;
|
||||
}
|
||||
|
||||
|
@ -1382,8 +1370,8 @@ int xen_evtchn_bind_interdomain_op(struct evtchn_bind_interdomain *interdomain)
|
|||
qemu_mutex_lock(&s->port_lock);
|
||||
|
||||
/* The newly allocated port starts out as unbound */
|
||||
ret = allocate_port(s, 0, EVTCHNSTAT_unbound, type_val,
|
||||
&interdomain->local_port);
|
||||
ret = allocate_port(s, 0, EVTCHNSTAT_unbound, 0, &interdomain->local_port);
|
||||
|
||||
if (ret) {
|
||||
goto out;
|
||||
}
|
||||
|
@ -1408,7 +1396,8 @@ int xen_evtchn_bind_interdomain_op(struct evtchn_bind_interdomain *interdomain)
|
|||
assign_kernel_eventfd(lp->type, xc->guest_port, xc->fd);
|
||||
}
|
||||
lp->type = EVTCHNSTAT_interdomain;
|
||||
lp->type_val = PORT_INFO_TYPEVAL_REMOTE_QEMU | interdomain->remote_port;
|
||||
lp->u.interdomain.to_qemu = 1;
|
||||
lp->u.interdomain.port = interdomain->remote_port;
|
||||
ret = 0;
|
||||
} else {
|
||||
/* Loopback */
|
||||
|
@ -1416,19 +1405,18 @@ int xen_evtchn_bind_interdomain_op(struct evtchn_bind_interdomain *interdomain)
|
|||
XenEvtchnPort *lp = &s->port_table[interdomain->local_port];
|
||||
|
||||
/*
|
||||
* The 'remote' port for loopback must be an unbound port allocated for
|
||||
* communication with the local domain (as indicated by rp->type_val
|
||||
* being zero, not PORT_INFO_TYPEVAL_REMOTE_QEMU), and must *not* be
|
||||
* the port that was just allocated for the local end.
|
||||
* The 'remote' port for loopback must be an unbound port allocated
|
||||
* for communication with the local domain, and must *not* be the
|
||||
* port that was just allocated for the local end.
|
||||
*/
|
||||
if (interdomain->local_port != interdomain->remote_port &&
|
||||
rp->type == EVTCHNSTAT_unbound && rp->type_val == 0) {
|
||||
rp->type == EVTCHNSTAT_unbound && !rp->u.interdomain.to_qemu) {
|
||||
|
||||
rp->type = EVTCHNSTAT_interdomain;
|
||||
rp->type_val = interdomain->local_port;
|
||||
rp->u.interdomain.port = interdomain->local_port;
|
||||
|
||||
lp->type = EVTCHNSTAT_interdomain;
|
||||
lp->type_val = interdomain->remote_port;
|
||||
lp->u.interdomain.port = interdomain->remote_port;
|
||||
} else {
|
||||
ret = -EINVAL;
|
||||
}
|
||||
|
@ -1447,7 +1435,6 @@ int xen_evtchn_bind_interdomain_op(struct evtchn_bind_interdomain *interdomain)
|
|||
int xen_evtchn_alloc_unbound_op(struct evtchn_alloc_unbound *alloc)
|
||||
{
|
||||
XenEvtchnState *s = xen_evtchn_singleton;
|
||||
uint16_t type_val;
|
||||
int ret;
|
||||
|
||||
if (!s) {
|
||||
|
@ -1458,18 +1445,20 @@ int xen_evtchn_alloc_unbound_op(struct evtchn_alloc_unbound *alloc)
|
|||
return -ESRCH;
|
||||
}
|
||||
|
||||
if (alloc->remote_dom == DOMID_QEMU) {
|
||||
type_val = PORT_INFO_TYPEVAL_REMOTE_QEMU;
|
||||
} else if (alloc->remote_dom == DOMID_SELF ||
|
||||
alloc->remote_dom == xen_domid) {
|
||||
type_val = 0;
|
||||
} else {
|
||||
if (alloc->remote_dom != DOMID_QEMU &&
|
||||
alloc->remote_dom != DOMID_SELF &&
|
||||
alloc->remote_dom != xen_domid) {
|
||||
return -EPERM;
|
||||
}
|
||||
|
||||
qemu_mutex_lock(&s->port_lock);
|
||||
|
||||
ret = allocate_port(s, 0, EVTCHNSTAT_unbound, type_val, &alloc->port);
|
||||
ret = allocate_port(s, 0, EVTCHNSTAT_unbound, 0, &alloc->port);
|
||||
|
||||
if (!ret && alloc->remote_dom == DOMID_QEMU) {
|
||||
XenEvtchnPort *p = &s->port_table[alloc->port];
|
||||
p->u.interdomain.to_qemu = 1;
|
||||
}
|
||||
|
||||
qemu_mutex_unlock(&s->port_lock);
|
||||
|
||||
|
@ -1496,12 +1485,12 @@ int xen_evtchn_send_op(struct evtchn_send *send)
|
|||
|
||||
switch (p->type) {
|
||||
case EVTCHNSTAT_interdomain:
|
||||
if (p->type_val & PORT_INFO_TYPEVAL_REMOTE_QEMU) {
|
||||
if (p->u.interdomain.to_qemu) {
|
||||
/*
|
||||
* This is an event from the guest to qemu itself, which is
|
||||
* serving as the driver domain.
|
||||
*/
|
||||
uint16_t be_port = p->type_val & ~PORT_INFO_TYPEVAL_REMOTE_QEMU;
|
||||
uint16_t be_port = p->u.interdomain.port;
|
||||
struct xenevtchn_handle *xc = s->be_handles[be_port];
|
||||
if (xc) {
|
||||
eventfd_write(xc->fd, 1);
|
||||
|
@ -1511,7 +1500,7 @@ int xen_evtchn_send_op(struct evtchn_send *send)
|
|||
}
|
||||
} else {
|
||||
/* Loopback interdomain ports; just a complex IPI */
|
||||
set_port_pending(s, p->type_val);
|
||||
set_port_pending(s, p->u.interdomain.port);
|
||||
}
|
||||
break;
|
||||
|
||||
|
@ -1553,8 +1542,7 @@ int xen_evtchn_set_port(uint16_t port)
|
|||
|
||||
/* QEMU has no business sending to anything but these */
|
||||
if (p->type == EVTCHNSTAT_virq ||
|
||||
(p->type == EVTCHNSTAT_interdomain &&
|
||||
(p->type_val & PORT_INFO_TYPEVAL_REMOTE_QEMU))) {
|
||||
(p->type == EVTCHNSTAT_interdomain && p->u.interdomain.to_qemu)) {
|
||||
set_port_pending(s, port);
|
||||
ret = 0;
|
||||
}
|
||||
|
@ -2064,7 +2052,7 @@ int xen_be_evtchn_bind_interdomain(struct xenevtchn_handle *xc, uint32_t domid,
|
|||
switch (gp->type) {
|
||||
case EVTCHNSTAT_interdomain:
|
||||
/* Allow rebinding after migration, preserve port # if possible */
|
||||
be_port = gp->type_val & ~PORT_INFO_TYPEVAL_REMOTE_QEMU;
|
||||
be_port = gp->u.interdomain.port;
|
||||
assert(be_port != 0);
|
||||
if (!s->be_handles[be_port]) {
|
||||
s->be_handles[be_port] = xc;
|
||||
|
@ -2085,7 +2073,8 @@ int xen_be_evtchn_bind_interdomain(struct xenevtchn_handle *xc, uint32_t domid,
|
|||
}
|
||||
|
||||
gp->type = EVTCHNSTAT_interdomain;
|
||||
gp->type_val = be_port | PORT_INFO_TYPEVAL_REMOTE_QEMU;
|
||||
gp->u.interdomain.to_qemu = 1;
|
||||
gp->u.interdomain.port = be_port;
|
||||
xc->guest_port = guest_port;
|
||||
if (kvm_xen_has_cap(EVTCHN_SEND)) {
|
||||
assign_kernel_eventfd(gp->type, guest_port, xc->fd);
|
||||
|
@ -2130,7 +2119,7 @@ int xen_be_evtchn_unbind(struct xenevtchn_handle *xc, evtchn_port_t port)
|
|||
/* This should never *not* be true */
|
||||
if (gp->type == EVTCHNSTAT_interdomain) {
|
||||
gp->type = EVTCHNSTAT_unbound;
|
||||
gp->type_val = PORT_INFO_TYPEVAL_REMOTE_QEMU;
|
||||
gp->u.interdomain.port = 0;
|
||||
}
|
||||
|
||||
if (kvm_xen_has_cap(EVTCHN_SEND)) {
|
||||
|
@ -2284,11 +2273,11 @@ EvtchnInfoList *qmp_xen_event_list(Error **errp)
|
|||
|
||||
info->type = p->type;
|
||||
if (p->type == EVTCHNSTAT_interdomain) {
|
||||
info->remote_domain = g_strdup((p->type_val & PORT_INFO_TYPEVAL_REMOTE_QEMU) ?
|
||||
info->remote_domain = g_strdup(p->u.interdomain.to_qemu ?
|
||||
"qemu" : "loopback");
|
||||
info->target = p->type_val & PORT_INFO_TYPEVAL_REMOTE_PORT_MASK;
|
||||
info->target = p->u.interdomain.port;
|
||||
} else {
|
||||
info->target = p->type_val;
|
||||
info->target = p->u.val; /* pirq# or virq# */
|
||||
}
|
||||
info->vcpu = p->vcpu;
|
||||
info->pending = test_bit(i, pending);
|
||||
|
|
|
@ -25,6 +25,7 @@
|
|||
#include "hw/xen/xen_backend_ops.h"
|
||||
#include "xen_overlay.h"
|
||||
#include "xen_gnttab.h"
|
||||
#include "xen_primary_console.h"
|
||||
|
||||
#include "sysemu/kvm.h"
|
||||
#include "sysemu/kvm_xen.h"
|
||||
|
@ -537,9 +538,13 @@ int xen_gnttab_reset(void)
|
|||
s->nr_frames = 0;
|
||||
|
||||
memset(s->entries.v1, 0, XEN_PAGE_SIZE * s->max_frames);
|
||||
|
||||
s->entries.v1[GNTTAB_RESERVED_XENSTORE].flags = GTF_permit_access;
|
||||
s->entries.v1[GNTTAB_RESERVED_XENSTORE].frame = XEN_SPECIAL_PFN(XENSTORE);
|
||||
|
||||
if (xen_primary_console_get_pfn()) {
|
||||
s->entries.v1[GNTTAB_RESERVED_CONSOLE].flags = GTF_permit_access;
|
||||
s->entries.v1[GNTTAB_RESERVED_CONSOLE].frame = XEN_SPECIAL_PFN(CONSOLE);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
|
193
hw/i386/kvm/xen_primary_console.c
Normal file
193
hw/i386/kvm/xen_primary_console.c
Normal file
|
@ -0,0 +1,193 @@
|
|||
/*
|
||||
* QEMU Xen emulation: Primary console support
|
||||
*
|
||||
* Copyright © 2023 Amazon.com, Inc. or its affiliates. All Rights Reserved.
|
||||
*
|
||||
* Authors: David Woodhouse <dwmw2@infradead.org>
|
||||
*
|
||||
* This work is licensed under the terms of the GNU GPL, version 2 or later.
|
||||
* See the COPYING file in the top-level directory.
|
||||
*/
|
||||
|
||||
#include "qemu/osdep.h"
|
||||
|
||||
#include "qapi/error.h"
|
||||
|
||||
#include "hw/sysbus.h"
|
||||
#include "hw/xen/xen.h"
|
||||
#include "hw/xen/xen_backend_ops.h"
|
||||
#include "xen_evtchn.h"
|
||||
#include "xen_overlay.h"
|
||||
#include "xen_primary_console.h"
|
||||
|
||||
#include "sysemu/kvm.h"
|
||||
#include "sysemu/kvm_xen.h"
|
||||
|
||||
#include "trace.h"
|
||||
|
||||
#include "hw/xen/interface/event_channel.h"
|
||||
#include "hw/xen/interface/grant_table.h"
|
||||
|
||||
#define TYPE_XEN_PRIMARY_CONSOLE "xen-primary-console"
|
||||
OBJECT_DECLARE_SIMPLE_TYPE(XenPrimaryConsoleState, XEN_PRIMARY_CONSOLE)
|
||||
|
||||
struct XenPrimaryConsoleState {
|
||||
/*< private >*/
|
||||
SysBusDevice busdev;
|
||||
/*< public >*/
|
||||
|
||||
MemoryRegion console_page;
|
||||
void *cp;
|
||||
|
||||
evtchn_port_t guest_port;
|
||||
evtchn_port_t be_port;
|
||||
|
||||
struct xengntdev_handle *gt;
|
||||
void *granted_xs;
|
||||
};
|
||||
|
||||
struct XenPrimaryConsoleState *xen_primary_console_singleton;
|
||||
|
||||
static void xen_primary_console_realize(DeviceState *dev, Error **errp)
|
||||
{
|
||||
XenPrimaryConsoleState *s = XEN_PRIMARY_CONSOLE(dev);
|
||||
|
||||
if (xen_mode != XEN_EMULATE) {
|
||||
error_setg(errp, "Xen primary console support is for Xen emulation");
|
||||
return;
|
||||
}
|
||||
|
||||
memory_region_init_ram(&s->console_page, OBJECT(dev), "xen:console_page",
|
||||
XEN_PAGE_SIZE, &error_abort);
|
||||
memory_region_set_enabled(&s->console_page, true);
|
||||
s->cp = memory_region_get_ram_ptr(&s->console_page);
|
||||
memset(s->cp, 0, XEN_PAGE_SIZE);
|
||||
|
||||
/* We can't map it this early as KVM isn't ready */
|
||||
xen_primary_console_singleton = s;
|
||||
}
|
||||
|
||||
static void xen_primary_console_class_init(ObjectClass *klass, void *data)
|
||||
{
|
||||
DeviceClass *dc = DEVICE_CLASS(klass);
|
||||
|
||||
dc->realize = xen_primary_console_realize;
|
||||
}
|
||||
|
||||
static const TypeInfo xen_primary_console_info = {
|
||||
.name = TYPE_XEN_PRIMARY_CONSOLE,
|
||||
.parent = TYPE_SYS_BUS_DEVICE,
|
||||
.instance_size = sizeof(XenPrimaryConsoleState),
|
||||
.class_init = xen_primary_console_class_init,
|
||||
};
|
||||
|
||||
|
||||
void xen_primary_console_create(void)
|
||||
{
|
||||
DeviceState *dev = sysbus_create_simple(TYPE_XEN_PRIMARY_CONSOLE, -1, NULL);
|
||||
|
||||
trace_xen_primary_console_create();
|
||||
|
||||
xen_primary_console_singleton = XEN_PRIMARY_CONSOLE(dev);
|
||||
|
||||
/*
|
||||
* Defer the init (xen_primary_console_reset()) until KVM is set up and the
|
||||
* overlay page can be mapped.
|
||||
*/
|
||||
}
|
||||
|
||||
static void xen_primary_console_register_types(void)
|
||||
{
|
||||
type_register_static(&xen_primary_console_info);
|
||||
}
|
||||
|
||||
type_init(xen_primary_console_register_types)
|
||||
|
||||
uint16_t xen_primary_console_get_port(void)
|
||||
{
|
||||
XenPrimaryConsoleState *s = xen_primary_console_singleton;
|
||||
if (!s) {
|
||||
return 0;
|
||||
}
|
||||
return s->guest_port;
|
||||
}
|
||||
|
||||
void xen_primary_console_set_be_port(uint16_t port)
|
||||
{
|
||||
XenPrimaryConsoleState *s = xen_primary_console_singleton;
|
||||
if (s) {
|
||||
s->be_port = port;
|
||||
}
|
||||
}
|
||||
|
||||
uint64_t xen_primary_console_get_pfn(void)
|
||||
{
|
||||
XenPrimaryConsoleState *s = xen_primary_console_singleton;
|
||||
if (!s) {
|
||||
return 0;
|
||||
}
|
||||
return XEN_SPECIAL_PFN(CONSOLE);
|
||||
}
|
||||
|
||||
void *xen_primary_console_get_map(void)
|
||||
{
|
||||
XenPrimaryConsoleState *s = xen_primary_console_singleton;
|
||||
if (!s) {
|
||||
return 0;
|
||||
}
|
||||
return s->cp;
|
||||
}
|
||||
|
||||
static void alloc_guest_port(XenPrimaryConsoleState *s)
|
||||
{
|
||||
struct evtchn_alloc_unbound alloc = {
|
||||
.dom = DOMID_SELF,
|
||||
.remote_dom = DOMID_QEMU,
|
||||
};
|
||||
|
||||
if (!xen_evtchn_alloc_unbound_op(&alloc)) {
|
||||
s->guest_port = alloc.port;
|
||||
}
|
||||
}
|
||||
|
||||
static void rebind_guest_port(XenPrimaryConsoleState *s)
|
||||
{
|
||||
struct evtchn_bind_interdomain inter = {
|
||||
.remote_dom = DOMID_QEMU,
|
||||
.remote_port = s->be_port,
|
||||
};
|
||||
|
||||
if (!xen_evtchn_bind_interdomain_op(&inter)) {
|
||||
s->guest_port = inter.local_port;
|
||||
}
|
||||
|
||||
s->be_port = 0;
|
||||
}
|
||||
|
||||
int xen_primary_console_reset(void)
|
||||
{
|
||||
XenPrimaryConsoleState *s = xen_primary_console_singleton;
|
||||
if (!s) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (!memory_region_is_mapped(&s->console_page)) {
|
||||
uint64_t gpa = XEN_SPECIAL_PFN(CONSOLE) << TARGET_PAGE_BITS;
|
||||
xen_overlay_do_map_page(&s->console_page, gpa);
|
||||
}
|
||||
|
||||
if (s->be_port) {
|
||||
rebind_guest_port(s);
|
||||
} else {
|
||||
alloc_guest_port(s);
|
||||
}
|
||||
|
||||
trace_xen_primary_console_reset(s->guest_port);
|
||||
|
||||
s->gt = qemu_xen_gnttab_open();
|
||||
uint32_t xs_gntref = GNTTAB_RESERVED_CONSOLE;
|
||||
s->granted_xs = qemu_xen_gnttab_map_refs(s->gt, 1, xen_domid, &xs_gntref,
|
||||
PROT_READ | PROT_WRITE);
|
||||
|
||||
return 0;
|
||||
}
|
23
hw/i386/kvm/xen_primary_console.h
Normal file
23
hw/i386/kvm/xen_primary_console.h
Normal file
|
@ -0,0 +1,23 @@
|
|||
/*
|
||||
* QEMU Xen emulation: Primary console support
|
||||
*
|
||||
* Copyright © 2023 Amazon.com, Inc. or its affiliates. All Rights Reserved.
|
||||
*
|
||||
* Authors: David Woodhouse <dwmw2@infradead.org>
|
||||
*
|
||||
* This work is licensed under the terms of the GNU GPL, version 2 or later.
|
||||
* See the COPYING file in the top-level directory.
|
||||
*/
|
||||
|
||||
#ifndef QEMU_XEN_PRIMARY_CONSOLE_H
|
||||
#define QEMU_XEN_PRIMARY_CONSOLE_H
|
||||
|
||||
void xen_primary_console_create(void);
|
||||
int xen_primary_console_reset(void);
|
||||
|
||||
uint16_t xen_primary_console_get_port(void);
|
||||
void xen_primary_console_set_be_port(uint16_t port);
|
||||
uint64_t xen_primary_console_get_pfn(void);
|
||||
void *xen_primary_console_get_map(void);
|
||||
|
||||
#endif /* QEMU_XEN_PRIMARY_CONSOLE_H */
|
|
@ -25,6 +25,7 @@
|
|||
#include "hw/xen/xen_backend_ops.h"
|
||||
#include "xen_overlay.h"
|
||||
#include "xen_evtchn.h"
|
||||
#include "xen_primary_console.h"
|
||||
#include "xen_xenstore.h"
|
||||
|
||||
#include "sysemu/kvm.h"
|
||||
|
@ -331,7 +332,7 @@ static void xs_error(XenXenstoreState *s, unsigned int id,
|
|||
const char *errstr = NULL;
|
||||
|
||||
for (unsigned int i = 0; i < ARRAY_SIZE(xsd_errors); i++) {
|
||||
struct xsd_errors *xsd_error = &xsd_errors[i];
|
||||
const struct xsd_errors *xsd_error = &xsd_errors[i];
|
||||
|
||||
if (xsd_error->errnum == errnum) {
|
||||
errstr = xsd_error->errstring;
|
||||
|
@ -1434,6 +1435,8 @@ static void alloc_guest_port(XenXenstoreState *s)
|
|||
int xen_xenstore_reset(void)
|
||||
{
|
||||
XenXenstoreState *s = xen_xenstore_singleton;
|
||||
int console_port;
|
||||
GList *perms;
|
||||
int err;
|
||||
|
||||
if (!s) {
|
||||
|
@ -1461,6 +1464,24 @@ int xen_xenstore_reset(void)
|
|||
}
|
||||
s->be_port = err;
|
||||
|
||||
/* Create frontend store nodes */
|
||||
perms = g_list_append(NULL, xs_perm_as_string(XS_PERM_NONE, DOMID_QEMU));
|
||||
perms = g_list_append(perms, xs_perm_as_string(XS_PERM_READ, xen_domid));
|
||||
|
||||
relpath_printf(s, perms, "store/port", "%u", s->guest_port);
|
||||
relpath_printf(s, perms, "store/ring-ref", "%lu",
|
||||
XEN_SPECIAL_PFN(XENSTORE));
|
||||
|
||||
console_port = xen_primary_console_get_port();
|
||||
if (console_port) {
|
||||
relpath_printf(s, perms, "console/ring-ref", "%lu",
|
||||
XEN_SPECIAL_PFN(CONSOLE));
|
||||
relpath_printf(s, perms, "console/port", "%u", console_port);
|
||||
relpath_printf(s, perms, "console/state", "%u", XenbusStateInitialised);
|
||||
}
|
||||
|
||||
g_list_free_full(perms, g_free);
|
||||
|
||||
/*
|
||||
* We don't actually access the guest's page through the grant, because
|
||||
* this isn't real Xen, and we can just use the page we gave it in the
|
||||
|
|
11
hw/i386/pc.c
11
hw/i386/pc.c
|
@ -1261,7 +1261,7 @@ void pc_basic_device_init(struct PCMachineState *pcms,
|
|||
if (pcms->bus) {
|
||||
pci_create_simple(pcms->bus, -1, "xen-platform");
|
||||
}
|
||||
xen_bus_init();
|
||||
pcms->xenbus = xen_bus_init();
|
||||
xen_be_init();
|
||||
}
|
||||
#endif
|
||||
|
@ -1289,7 +1289,8 @@ void pc_basic_device_init(struct PCMachineState *pcms,
|
|||
pcms->vmport != ON_OFF_AUTO_ON);
|
||||
}
|
||||
|
||||
void pc_nic_init(PCMachineClass *pcmc, ISABus *isa_bus, PCIBus *pci_bus)
|
||||
void pc_nic_init(PCMachineClass *pcmc, ISABus *isa_bus, PCIBus *pci_bus,
|
||||
BusState *xen_bus)
|
||||
{
|
||||
MachineClass *mc = MACHINE_CLASS(pcmc);
|
||||
int i;
|
||||
|
@ -1299,7 +1300,11 @@ void pc_nic_init(PCMachineClass *pcmc, ISABus *isa_bus, PCIBus *pci_bus)
|
|||
NICInfo *nd = &nd_table[i];
|
||||
const char *model = nd->model ? nd->model : mc->default_nic;
|
||||
|
||||
if (g_str_equal(model, "ne2k_isa")) {
|
||||
if (xen_bus && (!nd->model || g_str_equal(model, "xen-net-device"))) {
|
||||
DeviceState *dev = qdev_new("xen-net-device");
|
||||
qdev_set_nic_properties(dev, nd);
|
||||
qdev_realize_and_unref(dev, xen_bus, &error_fatal);
|
||||
} else if (g_str_equal(model, "ne2k_isa")) {
|
||||
pc_init_ne2k_isa(isa_bus, nd);
|
||||
} else {
|
||||
pci_nic_init_nofail(nd, pci_bus, model, NULL);
|
||||
|
|
|
@ -342,7 +342,7 @@ static void pc_init1(MachineState *machine,
|
|||
pc_basic_device_init(pcms, isa_bus, x86ms->gsi, rtc_state, true,
|
||||
0x4);
|
||||
|
||||
pc_nic_init(pcmc, isa_bus, pci_bus);
|
||||
pc_nic_init(pcmc, isa_bus, pci_bus, pcms->xenbus);
|
||||
|
||||
if (pcmc->pci_enabled) {
|
||||
pc_cmos_init(pcms, idebus[0], idebus[1], rtc_state);
|
||||
|
|
|
@ -340,7 +340,7 @@ static void pc_q35_init(MachineState *machine)
|
|||
|
||||
/* the rest devices to which pci devfn is automatically assigned */
|
||||
pc_vga_init(isa_bus, host_bus);
|
||||
pc_nic_init(pcmc, isa_bus, host_bus);
|
||||
pc_nic_init(pcmc, isa_bus, host_bus, pcms->xenbus);
|
||||
|
||||
if (machine->nvdimms_state->is_enabled) {
|
||||
nvdimm_init_acpi_state(machine->nvdimms_state, system_io,
|
||||
|
|
|
@ -140,9 +140,14 @@ static void unplug_nic(PCIBus *b, PCIDevice *d, void *o)
|
|||
/* Remove the peer of the NIC device. Normally, this would be a tap device. */
|
||||
static void del_nic_peer(NICState *nic, void *opaque)
|
||||
{
|
||||
NetClientState *nc;
|
||||
NetClientState *nc = qemu_get_queue(nic);
|
||||
ObjectClass *klass = module_object_class_by_name(nc->model);
|
||||
|
||||
/* Only delete peers of PCI NICs that we're about to delete */
|
||||
if (!klass || !object_class_dynamic_cast(klass, TYPE_PCI_DEVICE)) {
|
||||
return;
|
||||
}
|
||||
|
||||
nc = qemu_get_queue(nic);
|
||||
if (nc->peer)
|
||||
qemu_del_net_client(nc->peer);
|
||||
}
|
||||
|
@ -164,39 +169,60 @@ static void pci_unplug_nics(PCIBus *bus)
|
|||
*
|
||||
* [1] https://xenbits.xen.org/gitweb/?p=xen.git;a=blob;f=docs/misc/hvm-emulated-unplug.pandoc
|
||||
*/
|
||||
static void pci_xen_ide_unplug(PCIDevice *d, bool aux)
|
||||
struct ide_unplug_state {
|
||||
bool aux;
|
||||
int nr_unplugged;
|
||||
};
|
||||
|
||||
static int ide_dev_unplug(DeviceState *dev, void *_st)
|
||||
{
|
||||
DeviceState *dev = DEVICE(d);
|
||||
PCIIDEState *pci_ide;
|
||||
int i;
|
||||
struct ide_unplug_state *st = _st;
|
||||
IDEDevice *idedev;
|
||||
IDEBus *idebus;
|
||||
BlockBackend *blk;
|
||||
int unit;
|
||||
|
||||
pci_ide = PCI_IDE(dev);
|
||||
|
||||
for (i = aux ? 1 : 0; i < 4; i++) {
|
||||
idebus = &pci_ide->bus[i / 2];
|
||||
blk = idebus->ifs[i % 2].blk;
|
||||
|
||||
if (blk && idebus->ifs[i % 2].drive_kind != IDE_CD) {
|
||||
if (!(i % 2)) {
|
||||
idedev = idebus->master;
|
||||
} else {
|
||||
idedev = idebus->slave;
|
||||
}
|
||||
|
||||
blk_drain(blk);
|
||||
blk_flush(blk);
|
||||
|
||||
blk_detach_dev(blk, DEVICE(idedev));
|
||||
idebus->ifs[i % 2].blk = NULL;
|
||||
idedev->conf.blk = NULL;
|
||||
monitor_remove_blk(blk);
|
||||
blk_unref(blk);
|
||||
}
|
||||
idedev = IDE_DEVICE(object_dynamic_cast(OBJECT(dev), "ide-hd"));
|
||||
if (!idedev) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
idebus = IDE_BUS(qdev_get_parent_bus(dev));
|
||||
|
||||
unit = (idedev == idebus->slave);
|
||||
assert(unit || idedev == idebus->master);
|
||||
|
||||
if (st->aux && !unit && !strcmp(BUS(idebus)->name, "ide.0")) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
blk = idebus->ifs[unit].blk;
|
||||
if (blk) {
|
||||
blk_drain(blk);
|
||||
blk_flush(blk);
|
||||
|
||||
blk_detach_dev(blk, DEVICE(idedev));
|
||||
idebus->ifs[unit].blk = NULL;
|
||||
idedev->conf.blk = NULL;
|
||||
monitor_remove_blk(blk);
|
||||
blk_unref(blk);
|
||||
}
|
||||
|
||||
object_unparent(OBJECT(dev));
|
||||
st->nr_unplugged++;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void pci_xen_ide_unplug(PCIDevice *d, bool aux)
|
||||
{
|
||||
struct ide_unplug_state st = { aux, 0 };
|
||||
DeviceState *dev = DEVICE(d);
|
||||
|
||||
qdev_walk_children(dev, NULL, NULL, ide_dev_unplug, NULL, &st);
|
||||
if (st.nr_unplugged) {
|
||||
pci_device_reset(d);
|
||||
}
|
||||
pci_device_reset(d);
|
||||
}
|
||||
|
||||
static void unplug_disks(PCIBus *b, PCIDevice *d, void *opaque)
|
||||
|
@ -211,6 +237,7 @@ static void unplug_disks(PCIBus *b, PCIDevice *d, void *opaque)
|
|||
|
||||
switch (pci_get_word(d->config + PCI_CLASS_DEVICE)) {
|
||||
case PCI_CLASS_STORAGE_IDE:
|
||||
case PCI_CLASS_STORAGE_SATA:
|
||||
pci_xen_ide_unplug(d, aux);
|
||||
break;
|
||||
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
system_ss.add(when: 'CONFIG_DP8393X', if_true: files('dp8393x.c'))
|
||||
system_ss.add(when: 'CONFIG_XEN', if_true: files('xen_nic.c'))
|
||||
system_ss.add(when: 'CONFIG_XEN_BUS', if_true: files('xen_nic.c'))
|
||||
system_ss.add(when: 'CONFIG_NE2000_COMMON', if_true: files('ne2000.c'))
|
||||
|
||||
# PCI network cards
|
||||
|
|
|
@ -482,3 +482,14 @@ dp8393x_receive_oversize(int size) "oversize packet, pkt_size is %d"
|
|||
dp8393x_receive_not_netcard(void) "packet not for netcard"
|
||||
dp8393x_receive_packet(int crba) "Receive packet at 0x%"PRIx32
|
||||
dp8393x_receive_write_status(int crba) "Write status at 0x%"PRIx32
|
||||
|
||||
# xen_nic.c
|
||||
xen_netdev_realize(int dev, const char *info, const char *peer) "vif%u info '%s' peer '%s'"
|
||||
xen_netdev_unrealize(int dev) "vif%u"
|
||||
xen_netdev_create(int dev) "vif%u"
|
||||
xen_netdev_destroy(int dev) "vif%u"
|
||||
xen_netdev_disconnect(int dev) "vif%u"
|
||||
xen_netdev_connect(int dev, unsigned int tx, unsigned int rx, int port) "vif%u tx %u rx %u port %u"
|
||||
xen_netdev_frontend_changed(const char *dev, int state) "vif%s state %d"
|
||||
xen_netdev_tx(int dev, int ref, int off, int len, unsigned int flags, const char *c, const char *d, const char *m, const char *e) "vif%u ref %u off %u len %u flags 0x%x%s%s%s%s"
|
||||
xen_netdev_rx(int dev, int idx, int status, int flags) "vif%u idx %d status %d flags 0x%x"
|
||||
|
|
488
hw/net/xen_nic.c
488
hw/net/xen_nic.c
|
@ -20,6 +20,13 @@
|
|||
*/
|
||||
|
||||
#include "qemu/osdep.h"
|
||||
#include "qemu/main-loop.h"
|
||||
#include "qemu/cutils.h"
|
||||
#include "qemu/log.h"
|
||||
#include "qemu/qemu-print.h"
|
||||
#include "qapi/qmp/qdict.h"
|
||||
#include "qapi/error.h"
|
||||
|
||||
#include <sys/socket.h>
|
||||
#include <sys/ioctl.h>
|
||||
#include <sys/wait.h>
|
||||
|
@ -27,18 +34,26 @@
|
|||
#include "net/net.h"
|
||||
#include "net/checksum.h"
|
||||
#include "net/util.h"
|
||||
#include "hw/xen/xen-legacy-backend.h"
|
||||
|
||||
#include "hw/xen/xen-backend.h"
|
||||
#include "hw/xen/xen-bus-helper.h"
|
||||
#include "hw/qdev-properties.h"
|
||||
#include "hw/qdev-properties-system.h"
|
||||
|
||||
#include "hw/xen/interface/io/netif.h"
|
||||
#include "hw/xen/interface/io/xs_wire.h"
|
||||
|
||||
#include "trace.h"
|
||||
|
||||
/* ------------------------------------------------------------- */
|
||||
|
||||
struct XenNetDev {
|
||||
struct XenLegacyDevice xendev; /* must be first */
|
||||
char *mac;
|
||||
struct XenDevice xendev; /* must be first */
|
||||
XenEventChannel *event_channel;
|
||||
int dev;
|
||||
int tx_work;
|
||||
int tx_ring_ref;
|
||||
int rx_ring_ref;
|
||||
unsigned int tx_ring_ref;
|
||||
unsigned int rx_ring_ref;
|
||||
struct netif_tx_sring *txs;
|
||||
struct netif_rx_sring *rxs;
|
||||
netif_tx_back_ring_t tx_ring;
|
||||
|
@ -47,6 +62,11 @@ struct XenNetDev {
|
|||
NICState *nic;
|
||||
};
|
||||
|
||||
typedef struct XenNetDev XenNetDev;
|
||||
|
||||
#define TYPE_XEN_NET_DEVICE "xen-net-device"
|
||||
OBJECT_DECLARE_SIMPLE_TYPE(XenNetDev, XEN_NET_DEVICE)
|
||||
|
||||
/* ------------------------------------------------------------- */
|
||||
|
||||
static void net_tx_response(struct XenNetDev *netdev, netif_tx_request_t *txp, int8_t st)
|
||||
|
@ -68,7 +88,8 @@ static void net_tx_response(struct XenNetDev *netdev, netif_tx_request_t *txp, i
|
|||
netdev->tx_ring.rsp_prod_pvt = ++i;
|
||||
RING_PUSH_RESPONSES_AND_CHECK_NOTIFY(&netdev->tx_ring, notify);
|
||||
if (notify) {
|
||||
xen_pv_send_notify(&netdev->xendev);
|
||||
xen_device_notify_event_channel(XEN_DEVICE(netdev),
|
||||
netdev->event_channel, NULL);
|
||||
}
|
||||
|
||||
if (i == netdev->tx_ring.req_cons) {
|
||||
|
@ -104,13 +125,16 @@ static void net_tx_error(struct XenNetDev *netdev, netif_tx_request_t *txp, RING
|
|||
#endif
|
||||
}
|
||||
|
||||
static void net_tx_packets(struct XenNetDev *netdev)
|
||||
static bool net_tx_packets(struct XenNetDev *netdev)
|
||||
{
|
||||
bool done_something = false;
|
||||
netif_tx_request_t txreq;
|
||||
RING_IDX rc, rp;
|
||||
void *page;
|
||||
void *tmpbuf = NULL;
|
||||
|
||||
assert(qemu_mutex_iothread_locked());
|
||||
|
||||
for (;;) {
|
||||
rc = netdev->tx_ring.req_cons;
|
||||
rp = netdev->tx_ring.sring->req_prod;
|
||||
|
@ -122,49 +146,52 @@ static void net_tx_packets(struct XenNetDev *netdev)
|
|||
}
|
||||
memcpy(&txreq, RING_GET_REQUEST(&netdev->tx_ring, rc), sizeof(txreq));
|
||||
netdev->tx_ring.req_cons = ++rc;
|
||||
done_something = true;
|
||||
|
||||
#if 1
|
||||
/* should not happen in theory, we don't announce the *
|
||||
* feature-{sg,gso,whatelse} flags in xenstore (yet?) */
|
||||
if (txreq.flags & NETTXF_extra_info) {
|
||||
xen_pv_printf(&netdev->xendev, 0, "FIXME: extra info flag\n");
|
||||
qemu_log_mask(LOG_UNIMP, "vif%u: FIXME: extra info flag\n",
|
||||
netdev->dev);
|
||||
net_tx_error(netdev, &txreq, rc);
|
||||
continue;
|
||||
}
|
||||
if (txreq.flags & NETTXF_more_data) {
|
||||
xen_pv_printf(&netdev->xendev, 0, "FIXME: more data flag\n");
|
||||
qemu_log_mask(LOG_UNIMP, "vif%u: FIXME: more data flag\n",
|
||||
netdev->dev);
|
||||
net_tx_error(netdev, &txreq, rc);
|
||||
continue;
|
||||
}
|
||||
#endif
|
||||
|
||||
if (txreq.size < 14) {
|
||||
xen_pv_printf(&netdev->xendev, 0, "bad packet size: %d\n",
|
||||
txreq.size);
|
||||
qemu_log_mask(LOG_GUEST_ERROR, "vif%u: bad packet size: %d\n",
|
||||
netdev->dev, txreq.size);
|
||||
net_tx_error(netdev, &txreq, rc);
|
||||
continue;
|
||||
}
|
||||
|
||||
if ((txreq.offset + txreq.size) > XEN_PAGE_SIZE) {
|
||||
xen_pv_printf(&netdev->xendev, 0, "error: page crossing\n");
|
||||
qemu_log_mask(LOG_GUEST_ERROR, "vif%u: error: page crossing\n",
|
||||
netdev->dev);
|
||||
net_tx_error(netdev, &txreq, rc);
|
||||
continue;
|
||||
}
|
||||
|
||||
xen_pv_printf(&netdev->xendev, 3,
|
||||
"tx packet ref %d, off %d, len %d, flags 0x%x%s%s%s%s\n",
|
||||
txreq.gref, txreq.offset, txreq.size, txreq.flags,
|
||||
(txreq.flags & NETTXF_csum_blank) ? " csum_blank" : "",
|
||||
(txreq.flags & NETTXF_data_validated) ? " data_validated" : "",
|
||||
(txreq.flags & NETTXF_more_data) ? " more_data" : "",
|
||||
(txreq.flags & NETTXF_extra_info) ? " extra_info" : "");
|
||||
trace_xen_netdev_tx(netdev->dev, txreq.gref, txreq.offset,
|
||||
txreq.size, txreq.flags,
|
||||
(txreq.flags & NETTXF_csum_blank) ? " csum_blank" : "",
|
||||
(txreq.flags & NETTXF_data_validated) ? " data_validated" : "",
|
||||
(txreq.flags & NETTXF_more_data) ? " more_data" : "",
|
||||
(txreq.flags & NETTXF_extra_info) ? " extra_info" : "");
|
||||
|
||||
page = xen_be_map_grant_ref(&netdev->xendev, txreq.gref,
|
||||
PROT_READ);
|
||||
page = xen_device_map_grant_refs(&netdev->xendev, &txreq.gref, 1,
|
||||
PROT_READ, NULL);
|
||||
if (page == NULL) {
|
||||
xen_pv_printf(&netdev->xendev, 0,
|
||||
"error: tx gref dereference failed (%d)\n",
|
||||
txreq.gref);
|
||||
qemu_log_mask(LOG_GUEST_ERROR,
|
||||
"vif%u: tx gref dereference failed (%d)\n",
|
||||
netdev->dev, txreq.gref);
|
||||
net_tx_error(netdev, &txreq, rc);
|
||||
continue;
|
||||
}
|
||||
|
@ -181,7 +208,8 @@ static void net_tx_packets(struct XenNetDev *netdev)
|
|||
qemu_send_packet(qemu_get_queue(netdev->nic),
|
||||
page + txreq.offset, txreq.size);
|
||||
}
|
||||
xen_be_unmap_grant_ref(&netdev->xendev, page, txreq.gref);
|
||||
xen_device_unmap_grant_refs(&netdev->xendev, page, &txreq.gref, 1,
|
||||
NULL);
|
||||
net_tx_response(netdev, &txreq, NETIF_RSP_OKAY);
|
||||
}
|
||||
if (!netdev->tx_work) {
|
||||
|
@ -190,6 +218,7 @@ static void net_tx_packets(struct XenNetDev *netdev)
|
|||
netdev->tx_work = 0;
|
||||
}
|
||||
g_free(tmpbuf);
|
||||
return done_something;
|
||||
}
|
||||
|
||||
/* ------------------------------------------------------------- */
|
||||
|
@ -212,14 +241,13 @@ static void net_rx_response(struct XenNetDev *netdev,
|
|||
resp->status = (int16_t)st;
|
||||
}
|
||||
|
||||
xen_pv_printf(&netdev->xendev, 3,
|
||||
"rx response: idx %d, status %d, flags 0x%x\n",
|
||||
i, resp->status, resp->flags);
|
||||
trace_xen_netdev_rx(netdev->dev, i, resp->status, resp->flags);
|
||||
|
||||
netdev->rx_ring.rsp_prod_pvt = ++i;
|
||||
RING_PUSH_RESPONSES_AND_CHECK_NOTIFY(&netdev->rx_ring, notify);
|
||||
if (notify) {
|
||||
xen_pv_send_notify(&netdev->xendev);
|
||||
xen_device_notify_event_channel(XEN_DEVICE(netdev),
|
||||
netdev->event_channel, NULL);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -232,7 +260,9 @@ static ssize_t net_rx_packet(NetClientState *nc, const uint8_t *buf, size_t size
|
|||
RING_IDX rc, rp;
|
||||
void *page;
|
||||
|
||||
if (netdev->xendev.be_state != XenbusStateConnected) {
|
||||
assert(qemu_mutex_iothread_locked());
|
||||
|
||||
if (xen_device_backend_get_state(&netdev->xendev) != XenbusStateConnected) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
|
@ -244,24 +274,26 @@ static ssize_t net_rx_packet(NetClientState *nc, const uint8_t *buf, size_t size
|
|||
return 0;
|
||||
}
|
||||
if (size > XEN_PAGE_SIZE - NET_IP_ALIGN) {
|
||||
xen_pv_printf(&netdev->xendev, 0, "packet too big (%lu > %ld)",
|
||||
(unsigned long)size, XEN_PAGE_SIZE - NET_IP_ALIGN);
|
||||
qemu_log_mask(LOG_GUEST_ERROR, "vif%u: packet too big (%lu > %ld)",
|
||||
netdev->dev, (unsigned long)size,
|
||||
XEN_PAGE_SIZE - NET_IP_ALIGN);
|
||||
return -1;
|
||||
}
|
||||
|
||||
memcpy(&rxreq, RING_GET_REQUEST(&netdev->rx_ring, rc), sizeof(rxreq));
|
||||
netdev->rx_ring.req_cons = ++rc;
|
||||
|
||||
page = xen_be_map_grant_ref(&netdev->xendev, rxreq.gref, PROT_WRITE);
|
||||
page = xen_device_map_grant_refs(&netdev->xendev, &rxreq.gref, 1,
|
||||
PROT_WRITE, NULL);
|
||||
if (page == NULL) {
|
||||
xen_pv_printf(&netdev->xendev, 0,
|
||||
"error: rx gref dereference failed (%d)\n",
|
||||
rxreq.gref);
|
||||
qemu_log_mask(LOG_GUEST_ERROR,
|
||||
"vif%u: rx gref dereference failed (%d)\n",
|
||||
netdev->dev, rxreq.gref);
|
||||
net_rx_response(netdev, &rxreq, NETIF_RSP_ERROR, 0, 0, 0);
|
||||
return -1;
|
||||
}
|
||||
memcpy(page + NET_IP_ALIGN, buf, size);
|
||||
xen_be_unmap_grant_ref(&netdev->xendev, page, rxreq.gref);
|
||||
xen_device_unmap_grant_refs(&netdev->xendev, page, &rxreq.gref, 1, NULL);
|
||||
net_rx_response(netdev, &rxreq, NETIF_RSP_OKAY, NET_IP_ALIGN, size, 0);
|
||||
|
||||
return size;
|
||||
|
@ -275,139 +307,361 @@ static NetClientInfo net_xen_info = {
|
|||
.receive = net_rx_packet,
|
||||
};
|
||||
|
||||
static int net_init(struct XenLegacyDevice *xendev)
|
||||
static void xen_netdev_realize(XenDevice *xendev, Error **errp)
|
||||
{
|
||||
struct XenNetDev *netdev = container_of(xendev, struct XenNetDev, xendev);
|
||||
ERRP_GUARD();
|
||||
XenNetDev *netdev = XEN_NET_DEVICE(xendev);
|
||||
NetClientState *nc;
|
||||
|
||||
/* read xenstore entries */
|
||||
if (netdev->mac == NULL) {
|
||||
netdev->mac = xenstore_read_be_str(&netdev->xendev, "mac");
|
||||
}
|
||||
qemu_macaddr_default_if_unset(&netdev->conf.macaddr);
|
||||
|
||||
/* do we have all we need? */
|
||||
if (netdev->mac == NULL) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (net_parse_macaddr(netdev->conf.macaddr.a, netdev->mac) < 0) {
|
||||
return -1;
|
||||
}
|
||||
xen_device_frontend_printf(xendev, "mac", "%02x:%02x:%02x:%02x:%02x:%02x",
|
||||
netdev->conf.macaddr.a[0],
|
||||
netdev->conf.macaddr.a[1],
|
||||
netdev->conf.macaddr.a[2],
|
||||
netdev->conf.macaddr.a[3],
|
||||
netdev->conf.macaddr.a[4],
|
||||
netdev->conf.macaddr.a[5]);
|
||||
|
||||
netdev->nic = qemu_new_nic(&net_xen_info, &netdev->conf,
|
||||
"xen", NULL, netdev);
|
||||
object_get_typename(OBJECT(xendev)),
|
||||
DEVICE(xendev)->id, netdev);
|
||||
|
||||
qemu_set_info_str(qemu_get_queue(netdev->nic),
|
||||
"nic: xenbus vif macaddr=%s", netdev->mac);
|
||||
nc = qemu_get_queue(netdev->nic);
|
||||
qemu_format_nic_info_str(nc, netdev->conf.macaddr.a);
|
||||
|
||||
/* fill info */
|
||||
xenstore_write_be_int(&netdev->xendev, "feature-rx-copy", 1);
|
||||
xenstore_write_be_int(&netdev->xendev, "feature-rx-flip", 0);
|
||||
xen_device_backend_printf(xendev, "feature-rx-copy", "%u", 1);
|
||||
xen_device_backend_printf(xendev, "feature-rx-flip", "%u", 0);
|
||||
|
||||
return 0;
|
||||
trace_xen_netdev_realize(netdev->dev, nc->info_str, nc->peer ?
|
||||
nc->peer->name : "(none)");
|
||||
}
|
||||
|
||||
static int net_connect(struct XenLegacyDevice *xendev)
|
||||
static bool net_event(void *_xendev)
|
||||
{
|
||||
struct XenNetDev *netdev = container_of(xendev, struct XenNetDev, xendev);
|
||||
int rx_copy;
|
||||
XenNetDev *netdev = XEN_NET_DEVICE(_xendev);
|
||||
bool done_something;
|
||||
|
||||
if (xenstore_read_fe_int(&netdev->xendev, "tx-ring-ref",
|
||||
&netdev->tx_ring_ref) == -1) {
|
||||
return -1;
|
||||
}
|
||||
if (xenstore_read_fe_int(&netdev->xendev, "rx-ring-ref",
|
||||
&netdev->rx_ring_ref) == -1) {
|
||||
return 1;
|
||||
}
|
||||
if (xenstore_read_fe_int(&netdev->xendev, "event-channel",
|
||||
&netdev->xendev.remote_port) == -1) {
|
||||
return -1;
|
||||
done_something = net_tx_packets(netdev);
|
||||
qemu_flush_queued_packets(qemu_get_queue(netdev->nic));
|
||||
return done_something;
|
||||
}
|
||||
|
||||
static bool xen_netdev_connect(XenDevice *xendev, Error **errp)
|
||||
{
|
||||
XenNetDev *netdev = XEN_NET_DEVICE(xendev);
|
||||
unsigned int port, rx_copy;
|
||||
|
||||
assert(qemu_mutex_iothread_locked());
|
||||
|
||||
if (xen_device_frontend_scanf(xendev, "tx-ring-ref", "%u",
|
||||
&netdev->tx_ring_ref) != 1) {
|
||||
error_setg(errp, "failed to read tx-ring-ref");
|
||||
return false;
|
||||
}
|
||||
|
||||
if (xenstore_read_fe_int(&netdev->xendev, "request-rx-copy", &rx_copy) == -1) {
|
||||
if (xen_device_frontend_scanf(xendev, "rx-ring-ref", "%u",
|
||||
&netdev->rx_ring_ref) != 1) {
|
||||
error_setg(errp, "failed to read rx-ring-ref");
|
||||
return false;
|
||||
}
|
||||
|
||||
if (xen_device_frontend_scanf(xendev, "event-channel", "%u",
|
||||
&port) != 1) {
|
||||
error_setg(errp, "failed to read event-channel");
|
||||
return false;
|
||||
}
|
||||
|
||||
if (xen_device_frontend_scanf(xendev, "request-rx-copy", "%u",
|
||||
&rx_copy) != 1) {
|
||||
rx_copy = 0;
|
||||
}
|
||||
if (rx_copy == 0) {
|
||||
xen_pv_printf(&netdev->xendev, 0,
|
||||
"frontend doesn't support rx-copy.\n");
|
||||
return -1;
|
||||
error_setg(errp, "frontend doesn't support rx-copy");
|
||||
return false;
|
||||
}
|
||||
|
||||
netdev->txs = xen_be_map_grant_ref(&netdev->xendev,
|
||||
netdev->tx_ring_ref,
|
||||
PROT_READ | PROT_WRITE);
|
||||
netdev->txs = xen_device_map_grant_refs(xendev,
|
||||
&netdev->tx_ring_ref, 1,
|
||||
PROT_READ | PROT_WRITE,
|
||||
errp);
|
||||
if (!netdev->txs) {
|
||||
return -1;
|
||||
error_prepend(errp, "failed to map tx grant ref: ");
|
||||
return false;
|
||||
}
|
||||
netdev->rxs = xen_be_map_grant_ref(&netdev->xendev,
|
||||
netdev->rx_ring_ref,
|
||||
PROT_READ | PROT_WRITE);
|
||||
|
||||
netdev->rxs = xen_device_map_grant_refs(xendev,
|
||||
&netdev->rx_ring_ref, 1,
|
||||
PROT_READ | PROT_WRITE,
|
||||
errp);
|
||||
if (!netdev->rxs) {
|
||||
xen_be_unmap_grant_ref(&netdev->xendev, netdev->txs,
|
||||
netdev->tx_ring_ref);
|
||||
netdev->txs = NULL;
|
||||
return -1;
|
||||
error_prepend(errp, "failed to map rx grant ref: ");
|
||||
return false;
|
||||
}
|
||||
|
||||
BACK_RING_INIT(&netdev->tx_ring, netdev->txs, XEN_PAGE_SIZE);
|
||||
BACK_RING_INIT(&netdev->rx_ring, netdev->rxs, XEN_PAGE_SIZE);
|
||||
|
||||
xen_be_bind_evtchn(&netdev->xendev);
|
||||
netdev->event_channel = xen_device_bind_event_channel(xendev, port,
|
||||
net_event,
|
||||
netdev,
|
||||
errp);
|
||||
if (!netdev->event_channel) {
|
||||
return false;
|
||||
}
|
||||
|
||||
xen_pv_printf(&netdev->xendev, 1, "ok: tx-ring-ref %d, rx-ring-ref %d, "
|
||||
"remote port %d, local port %d\n",
|
||||
netdev->tx_ring_ref, netdev->rx_ring_ref,
|
||||
netdev->xendev.remote_port, netdev->xendev.local_port);
|
||||
trace_xen_netdev_connect(netdev->dev, netdev->tx_ring_ref,
|
||||
netdev->rx_ring_ref, port);
|
||||
|
||||
net_tx_packets(netdev);
|
||||
return 0;
|
||||
return true;
|
||||
}
|
||||
|
||||
static void net_disconnect(struct XenLegacyDevice *xendev)
|
||||
static void xen_netdev_disconnect(XenDevice *xendev, Error **errp)
|
||||
{
|
||||
struct XenNetDev *netdev = container_of(xendev, struct XenNetDev, xendev);
|
||||
XenNetDev *netdev = XEN_NET_DEVICE(xendev);
|
||||
|
||||
xen_pv_unbind_evtchn(&netdev->xendev);
|
||||
trace_xen_netdev_disconnect(netdev->dev);
|
||||
|
||||
assert(qemu_mutex_iothread_locked());
|
||||
|
||||
netdev->tx_ring.sring = NULL;
|
||||
netdev->rx_ring.sring = NULL;
|
||||
|
||||
if (netdev->event_channel) {
|
||||
xen_device_unbind_event_channel(xendev, netdev->event_channel,
|
||||
errp);
|
||||
netdev->event_channel = NULL;
|
||||
}
|
||||
if (netdev->txs) {
|
||||
xen_be_unmap_grant_ref(&netdev->xendev, netdev->txs,
|
||||
netdev->tx_ring_ref);
|
||||
xen_device_unmap_grant_refs(xendev, netdev->txs,
|
||||
&netdev->tx_ring_ref, 1, errp);
|
||||
netdev->txs = NULL;
|
||||
}
|
||||
if (netdev->rxs) {
|
||||
xen_be_unmap_grant_ref(&netdev->xendev, netdev->rxs,
|
||||
netdev->rx_ring_ref);
|
||||
xen_device_unmap_grant_refs(xendev, netdev->rxs,
|
||||
&netdev->rx_ring_ref, 1, errp);
|
||||
netdev->rxs = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
static void net_event(struct XenLegacyDevice *xendev)
|
||||
/* -------------------------------------------------------------------- */
|
||||
|
||||
|
||||
static void xen_netdev_frontend_changed(XenDevice *xendev,
|
||||
enum xenbus_state frontend_state,
|
||||
Error **errp)
|
||||
{
|
||||
struct XenNetDev *netdev = container_of(xendev, struct XenNetDev, xendev);
|
||||
net_tx_packets(netdev);
|
||||
qemu_flush_queued_packets(qemu_get_queue(netdev->nic));
|
||||
ERRP_GUARD();
|
||||
enum xenbus_state backend_state = xen_device_backend_get_state(xendev);
|
||||
|
||||
trace_xen_netdev_frontend_changed(xendev->name, frontend_state);
|
||||
|
||||
switch (frontend_state) {
|
||||
case XenbusStateConnected:
|
||||
if (backend_state == XenbusStateConnected) {
|
||||
break;
|
||||
}
|
||||
|
||||
xen_netdev_disconnect(xendev, errp);
|
||||
if (*errp) {
|
||||
break;
|
||||
}
|
||||
|
||||
if (!xen_netdev_connect(xendev, errp)) {
|
||||
xen_netdev_disconnect(xendev, NULL);
|
||||
xen_device_backend_set_state(xendev, XenbusStateClosing);
|
||||
break;
|
||||
}
|
||||
|
||||
xen_device_backend_set_state(xendev, XenbusStateConnected);
|
||||
break;
|
||||
|
||||
case XenbusStateClosing:
|
||||
xen_device_backend_set_state(xendev, XenbusStateClosing);
|
||||
break;
|
||||
|
||||
case XenbusStateClosed:
|
||||
case XenbusStateUnknown:
|
||||
xen_netdev_disconnect(xendev, errp);
|
||||
if (*errp) {
|
||||
break;
|
||||
}
|
||||
|
||||
xen_device_backend_set_state(xendev, XenbusStateClosed);
|
||||
break;
|
||||
|
||||
case XenbusStateInitialised:
|
||||
/*
|
||||
* Linux netback does nothing on the frontend going (back) to
|
||||
* XenbusStateInitialised, so do the same here.
|
||||
*/
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
static int net_free(struct XenLegacyDevice *xendev)
|
||||
static char *xen_netdev_get_name(XenDevice *xendev, Error **errp)
|
||||
{
|
||||
struct XenNetDev *netdev = container_of(xendev, struct XenNetDev, xendev);
|
||||
XenNetDev *netdev = XEN_NET_DEVICE(xendev);
|
||||
|
||||
if (netdev->dev == -1) {
|
||||
XenBus *xenbus = XEN_BUS(qdev_get_parent_bus(DEVICE(xendev)));
|
||||
char fe_path[XENSTORE_ABS_PATH_MAX + 1];
|
||||
int idx = (xen_mode == XEN_EMULATE) ? 0 : 1;
|
||||
char *value;
|
||||
|
||||
/* Theoretically we could go up to INT_MAX here but that's overkill */
|
||||
while (idx < 100) {
|
||||
snprintf(fe_path, sizeof(fe_path),
|
||||
"/local/domain/%u/device/vif/%u",
|
||||
xendev->frontend_id, idx);
|
||||
value = qemu_xen_xs_read(xenbus->xsh, XBT_NULL, fe_path, NULL);
|
||||
if (!value) {
|
||||
if (errno == ENOENT) {
|
||||
netdev->dev = idx;
|
||||
goto found;
|
||||
}
|
||||
error_setg(errp, "cannot read %s: %s", fe_path,
|
||||
strerror(errno));
|
||||
return NULL;
|
||||
}
|
||||
free(value);
|
||||
idx++;
|
||||
}
|
||||
error_setg(errp, "cannot find device index for netdev device");
|
||||
return NULL;
|
||||
}
|
||||
found:
|
||||
return g_strdup_printf("%u", netdev->dev);
|
||||
}
|
||||
|
||||
static void xen_netdev_unrealize(XenDevice *xendev)
|
||||
{
|
||||
XenNetDev *netdev = XEN_NET_DEVICE(xendev);
|
||||
|
||||
trace_xen_netdev_unrealize(netdev->dev);
|
||||
|
||||
/* Disconnect from the frontend in case this has not already happened */
|
||||
xen_netdev_disconnect(xendev, NULL);
|
||||
|
||||
if (netdev->nic) {
|
||||
qemu_del_nic(netdev->nic);
|
||||
netdev->nic = NULL;
|
||||
}
|
||||
g_free(netdev->mac);
|
||||
netdev->mac = NULL;
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* ------------------------------------------------------------- */
|
||||
|
||||
struct XenDevOps xen_netdev_ops = {
|
||||
.size = sizeof(struct XenNetDev),
|
||||
.flags = DEVOPS_FLAG_NEED_GNTDEV,
|
||||
.init = net_init,
|
||||
.initialise = net_connect,
|
||||
.event = net_event,
|
||||
.disconnect = net_disconnect,
|
||||
.free = net_free,
|
||||
static Property xen_netdev_properties[] = {
|
||||
DEFINE_NIC_PROPERTIES(XenNetDev, conf),
|
||||
DEFINE_PROP_INT32("idx", XenNetDev, dev, -1),
|
||||
DEFINE_PROP_END_OF_LIST(),
|
||||
};
|
||||
|
||||
static void xen_netdev_class_init(ObjectClass *class, void *data)
|
||||
{
|
||||
DeviceClass *dev_class = DEVICE_CLASS(class);
|
||||
XenDeviceClass *xendev_class = XEN_DEVICE_CLASS(class);
|
||||
|
||||
xendev_class->backend = "qnic";
|
||||
xendev_class->device = "vif";
|
||||
xendev_class->get_name = xen_netdev_get_name;
|
||||
xendev_class->realize = xen_netdev_realize;
|
||||
xendev_class->frontend_changed = xen_netdev_frontend_changed;
|
||||
xendev_class->unrealize = xen_netdev_unrealize;
|
||||
set_bit(DEVICE_CATEGORY_NETWORK, dev_class->categories);
|
||||
dev_class->user_creatable = true;
|
||||
|
||||
device_class_set_props(dev_class, xen_netdev_properties);
|
||||
}
|
||||
|
||||
static const TypeInfo xen_net_type_info = {
|
||||
.name = TYPE_XEN_NET_DEVICE,
|
||||
.parent = TYPE_XEN_DEVICE,
|
||||
.instance_size = sizeof(XenNetDev),
|
||||
.class_init = xen_netdev_class_init,
|
||||
};
|
||||
|
||||
static void xen_net_register_types(void)
|
||||
{
|
||||
type_register_static(&xen_net_type_info);
|
||||
}
|
||||
|
||||
type_init(xen_net_register_types)
|
||||
|
||||
/* Called to instantiate a XenNetDev when the backend is detected. */
|
||||
static void xen_net_device_create(XenBackendInstance *backend,
|
||||
QDict *opts, Error **errp)
|
||||
{
|
||||
ERRP_GUARD();
|
||||
XenBus *xenbus = xen_backend_get_bus(backend);
|
||||
const char *name = xen_backend_get_name(backend);
|
||||
XenDevice *xendev = NULL;
|
||||
unsigned long number;
|
||||
const char *macstr;
|
||||
XenNetDev *net;
|
||||
MACAddr mac;
|
||||
|
||||
if (qemu_strtoul(name, NULL, 10, &number) || number >= INT_MAX) {
|
||||
error_setg(errp, "failed to parse name '%s'", name);
|
||||
goto fail;
|
||||
}
|
||||
|
||||
trace_xen_netdev_create(number);
|
||||
|
||||
macstr = qdict_get_try_str(opts, "mac");
|
||||
if (macstr == NULL) {
|
||||
error_setg(errp, "no MAC address found");
|
||||
goto fail;
|
||||
}
|
||||
|
||||
if (net_parse_macaddr(mac.a, macstr) < 0) {
|
||||
error_setg(errp, "failed to parse MAC address");
|
||||
goto fail;
|
||||
}
|
||||
|
||||
xendev = XEN_DEVICE(qdev_new(TYPE_XEN_NET_DEVICE));
|
||||
net = XEN_NET_DEVICE(xendev);
|
||||
|
||||
net->dev = number;
|
||||
memcpy(&net->conf.macaddr, &mac, sizeof(mac));
|
||||
|
||||
if (qdev_realize_and_unref(DEVICE(xendev), BUS(xenbus), errp)) {
|
||||
xen_backend_set_device(backend, xendev);
|
||||
return;
|
||||
}
|
||||
|
||||
error_prepend(errp, "realization of net device %lu failed: ",
|
||||
number);
|
||||
|
||||
fail:
|
||||
if (xendev) {
|
||||
object_unparent(OBJECT(xendev));
|
||||
}
|
||||
}
|
||||
|
||||
static void xen_net_device_destroy(XenBackendInstance *backend,
|
||||
Error **errp)
|
||||
{
|
||||
ERRP_GUARD();
|
||||
XenDevice *xendev = xen_backend_get_device(backend);
|
||||
XenNetDev *netdev = XEN_NET_DEVICE(xendev);
|
||||
|
||||
trace_xen_netdev_destroy(netdev->dev);
|
||||
|
||||
object_unparent(OBJECT(xendev));
|
||||
}
|
||||
|
||||
static const XenBackendInfo xen_net_backend_info = {
|
||||
.type = "qnic",
|
||||
.create = xen_net_device_create,
|
||||
.destroy = xen_net_device_destroy,
|
||||
};
|
||||
|
||||
static void xen_net_register_backend(void)
|
||||
{
|
||||
xen_backend_register(&xen_net_backend_info);
|
||||
}
|
||||
|
||||
xen_backend_init(xen_net_register_backend);
|
||||
|
|
|
@ -101,6 +101,24 @@ static XenBackendInstance *xen_backend_list_find(XenDevice *xendev)
|
|||
return NULL;
|
||||
}
|
||||
|
||||
bool xen_backend_exists(const char *type, const char *name)
|
||||
{
|
||||
const XenBackendImpl *impl = xen_backend_table_lookup(type);
|
||||
XenBackendInstance *backend;
|
||||
|
||||
if (!impl) {
|
||||
return false;
|
||||
}
|
||||
|
||||
QLIST_FOREACH(backend, &backend_list, entry) {
|
||||
if (backend->impl == impl && !strcmp(backend->name, name)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
static void xen_backend_list_remove(XenBackendInstance *backend)
|
||||
{
|
||||
QLIST_REMOVE(backend, entry);
|
||||
|
@ -122,11 +140,6 @@ void xen_backend_device_create(XenBus *xenbus, const char *type,
|
|||
backend->name = g_strdup(name);
|
||||
|
||||
impl->create(backend, opts, errp);
|
||||
if (*errp) {
|
||||
g_free(backend->name);
|
||||
g_free(backend);
|
||||
return;
|
||||
}
|
||||
|
||||
backend->impl = impl;
|
||||
xen_backend_list_add(backend);
|
||||
|
@ -165,7 +178,9 @@ bool xen_backend_try_device_destroy(XenDevice *xendev, Error **errp)
|
|||
}
|
||||
|
||||
impl = backend->impl;
|
||||
impl->destroy(backend, errp);
|
||||
if (backend->xendev) {
|
||||
impl->destroy(backend, errp);
|
||||
}
|
||||
|
||||
xen_backend_list_remove(backend);
|
||||
g_free(backend->name);
|
||||
|
|
|
@ -209,7 +209,8 @@ static void xen_bus_type_enumerate(XenBus *xenbus, const char *type)
|
|||
NULL, "%u", &online) != 1)
|
||||
online = 0;
|
||||
|
||||
if (online && state == XenbusStateInitialising) {
|
||||
if (online && state == XenbusStateInitialising &&
|
||||
!xen_backend_exists(type, backend[i])) {
|
||||
Error *local_err = NULL;
|
||||
|
||||
xen_bus_backend_create(xenbus, type, backend[i], backend_path,
|
||||
|
@ -711,8 +712,17 @@ static void xen_device_frontend_create(XenDevice *xendev, Error **errp)
|
|||
{
|
||||
ERRP_GUARD();
|
||||
XenBus *xenbus = XEN_BUS(qdev_get_parent_bus(DEVICE(xendev)));
|
||||
XenDeviceClass *xendev_class = XEN_DEVICE_GET_CLASS(xendev);
|
||||
|
||||
xendev->frontend_path = xen_device_get_frontend_path(xendev);
|
||||
if (xendev_class->get_frontend_path) {
|
||||
xendev->frontend_path = xendev_class->get_frontend_path(xendev, errp);
|
||||
if (!xendev->frontend_path) {
|
||||
error_prepend(errp, "failed to create frontend: ");
|
||||
return;
|
||||
}
|
||||
} else {
|
||||
xendev->frontend_path = xen_device_get_frontend_path(xendev);
|
||||
}
|
||||
|
||||
/*
|
||||
* The frontend area may have already been created by a legacy
|
||||
|
@ -912,6 +922,11 @@ void xen_device_notify_event_channel(XenDevice *xendev,
|
|||
}
|
||||
}
|
||||
|
||||
unsigned int xen_event_channel_get_local_port(XenEventChannel *channel)
|
||||
{
|
||||
return channel->local_port;
|
||||
}
|
||||
|
||||
void xen_device_unbind_event_channel(XenDevice *xendev,
|
||||
XenEventChannel *channel,
|
||||
Error **errp)
|
||||
|
@ -1118,11 +1133,13 @@ static void xen_register_types(void)
|
|||
|
||||
type_init(xen_register_types)
|
||||
|
||||
void xen_bus_init(void)
|
||||
BusState *xen_bus_init(void)
|
||||
{
|
||||
DeviceState *dev = qdev_new(TYPE_XEN_BRIDGE);
|
||||
BusState *bus = qbus_new(TYPE_XEN_BUS, dev, NULL);
|
||||
|
||||
sysbus_realize_and_unref(SYS_BUS_DEVICE(dev), &error_fatal);
|
||||
qbus_set_bus_hotplug_handler(bus);
|
||||
|
||||
return bus;
|
||||
}
|
||||
|
|
|
@ -623,7 +623,6 @@ void xen_be_init(void)
|
|||
|
||||
xen_set_dynamic_sysbus();
|
||||
|
||||
xen_be_register("console", &xen_console_ops);
|
||||
xen_be_register("vkbd", &xen_kbdmouse_ops);
|
||||
#ifdef CONFIG_VIRTFS
|
||||
xen_be_register("9pfs", &xen_9pfs_ops);
|
||||
|
|
|
@ -46,34 +46,6 @@ static int xen_config_dev_all(char *fe, char *be)
|
|||
|
||||
/* ------------------------------------------------------------- */
|
||||
|
||||
int xen_config_dev_blk(DriveInfo *disk)
|
||||
{
|
||||
char fe[256], be[256], device_name[32];
|
||||
int vdev = 202 * 256 + 16 * disk->unit;
|
||||
int cdrom = disk->media_cd;
|
||||
const char *devtype = cdrom ? "cdrom" : "disk";
|
||||
const char *mode = cdrom ? "r" : "w";
|
||||
const char *filename = qemu_opt_get(disk->opts, "file");
|
||||
|
||||
snprintf(device_name, sizeof(device_name), "xvd%c", 'a' + disk->unit);
|
||||
xen_pv_printf(NULL, 1, "config disk %d [%s]: %s\n",
|
||||
disk->unit, device_name, filename);
|
||||
xen_config_dev_dirs("vbd", "qdisk", vdev, fe, be, sizeof(fe));
|
||||
|
||||
/* frontend */
|
||||
xenstore_write_int(fe, "virtual-device", vdev);
|
||||
xenstore_write_str(fe, "device-type", devtype);
|
||||
|
||||
/* backend */
|
||||
xenstore_write_str(be, "dev", device_name);
|
||||
xenstore_write_str(be, "type", "file");
|
||||
xenstore_write_str(be, "params", filename);
|
||||
xenstore_write_str(be, "mode", mode);
|
||||
|
||||
/* common stuff */
|
||||
return xen_config_dev_all(fe, be);
|
||||
}
|
||||
|
||||
int xen_config_dev_nic(NICInfo *nic)
|
||||
{
|
||||
char fe[256], be[256];
|
||||
|
|
|
@ -32,7 +32,6 @@
|
|||
|
||||
static void xen_init_pv(MachineState *machine)
|
||||
{
|
||||
DriveInfo *dinfo;
|
||||
int i;
|
||||
|
||||
setup_xen_backend_ops();
|
||||
|
@ -55,7 +54,6 @@ static void xen_init_pv(MachineState *machine)
|
|||
}
|
||||
|
||||
xen_be_register("vfb", &xen_framebuffer_ops);
|
||||
xen_be_register("qnic", &xen_netdev_ops);
|
||||
|
||||
/* configure framebuffer */
|
||||
if (vga_interface_type == VGA_XENFB) {
|
||||
|
@ -64,14 +62,6 @@ static void xen_init_pv(MachineState *machine)
|
|||
vga_interface_created = true;
|
||||
}
|
||||
|
||||
/* configure disks */
|
||||
for (i = 0; i < 16; i++) {
|
||||
dinfo = drive_get(IF_XEN, 0, i);
|
||||
if (!dinfo)
|
||||
continue;
|
||||
xen_config_dev_blk(dinfo);
|
||||
}
|
||||
|
||||
/* configure nics */
|
||||
for (i = 0; i < nb_nics; i++) {
|
||||
if (!nd_table[i].model || 0 != strcmp(nd_table[i].model, "xen"))
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue