From 53e51d85ef1fdd295c8f09792b8e7490c148f4b3 Mon Sep 17 00:00:00 2001 From: Markus Armbruster Date: Thu, 16 Jun 2011 18:45:36 +0200 Subject: [PATCH 001/230] Fix automatically assigned network names for netdev If a network client doesn't have a name, we make one up, with assign_name(). assign_name() creates a name MODEL.NUM, where MODEL is the client's model, and NUM is the number of MODELs that already exist. Bug: it misses clients that are not on a VLAN, i.e. netdevs and the NICs using them: $ qemu-system-x86_64 -nodefaults -vnc :0 -S -monitor stdio -netdev user,id=hostnet0 -net nic,netdev=hostnet0 -netdev user,id=hostnet1 -net nic,netdev=hostnet1 QEMU 0.14.50 monitor - type 'help' for more information (qemu) info network Devices not on any VLAN: hostnet0: net=10.0.2.0, restricted=n peer=e1000.0 hostnet1: net=10.0.2.0, restricted=n peer=e1000.0 e1000.0: model=e1000,macaddr=52:54:00:12:34:56 peer=hostnet0 e1000.0: model=e1000,macaddr=52:54:00:12:34:57 peer=hostnet1 Fix that. Signed-off-by: Markus Armbruster Signed-off-by: Michael S. Tsirkin --- net.c | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/net.c b/net.c index 66123ad409..55c73c592d 100644 --- a/net.c +++ b/net.c @@ -150,12 +150,11 @@ void qemu_macaddr_default_if_unset(MACAddr *macaddr) static char *assign_name(VLANClientState *vc1, const char *model) { VLANState *vlan; + VLANClientState *vc; char buf[256]; int id = 0; QTAILQ_FOREACH(vlan, &vlans, next) { - VLANClientState *vc; - QTAILQ_FOREACH(vc, &vlan->clients, next) { if (vc != vc1 && strcmp(vc->model, model) == 0) { id++; @@ -163,6 +162,12 @@ static char *assign_name(VLANClientState *vc1, const char *model) } } + QTAILQ_FOREACH(vc, &non_vlan_clients, next) { + if (vc != vc1 && strcmp(vc->model, model) == 0) { + id++; + } + } + snprintf(buf, sizeof(buf), "%s.%d", model, id); return qemu_strdup(buf); From 85dde9a90b9d26273ef531d344b2cdfee9a6683d Mon Sep 17 00:00:00 2001 From: Markus Armbruster Date: Thu, 16 Jun 2011 18:45:37 +0200 Subject: [PATCH 002/230] Fix netdev name lookup in -device, device_add, netdev_del qemu_find_netdev() looks up members of non_vlan_clients by name. It happily returns the first match. Trouble is the names need not be unique. non_vlan_clients contains host parts (netdevs) and guest parts (NICs). Netdevs have unique names: a netdev's name is a (mandatory) qemu_netdev_opts ID, and these are unique. NIC names are not unique. If a NIC has a qdev ID (which is unique), that's its name. Else, we make up a name. The made-up names are unique, but they can clash with qdev IDs. Even if NICs had unique names, they could still clash with netdev names. Callers of qemu_find_netdev(): * net_init_nic() wants a netdev. It happens to work because it runs before NICs get added to non_vlan_clients. * do_netdev_del() wants a netdev. If it gets a NIC, it complains and fails. Bug: a netdev with the same name that comes later in non_vlan_clients can't be deleted: $ qemu-system-x86_64 -nodefaults -vnc :0 -S -monitor stdio -netdev user,id=hostnet0 -device virtio-net-pci,netdev=hostnet0,id=virtio1 [...] (qemu) netdev_add user,id=virtio1 (qemu) info network Devices not on any VLAN: hostnet0: net=10.0.2.0, restricted=n peer=virtio1 virtio1: model=virtio-net-pci,macaddr=52:54:00:12:34:56 peer=hostnet0 virtio1: net=10.0.2.0, restricted=n (qemu) netdev_del virtio1 Device 'virtio1' not found * parse_netdev() wants a netdev. If it gets a NIC, it gets confused. With the test setup above: (qemu) device_add virtio-net-pci,netdev=virtio1 Property 'virtio-net-pci.netdev' can't take value 'virtio1', it's in use You can even connect two NICs to each other: $ qemu-system-x86_64 -nodefaults -vnc :0 -S -monitor stdio -device virtio-net-pci,id=virtio1 -device e1000,netdev=virtio1 [...] Devices not on any VLAN: virtio1: model=virtio-net-pci,macaddr=52:54:00:12:34:56 peer=e1000.0 e1000.0: model=e1000,macaddr=52:54:00:12:34:57 peer=virtio1 (qemu) q Segmentation fault (core dumped) * do_set_link() works fine for both netdevs and NICs. Whether it really makes sense for netdevs is debatable, but that's outside this patch's scope. Change qemu_find_netdev() to return only netdevs. This fixes the netdev_del and device_add/-device bugs demonstrated above. To avoid changing set_link, make do_set_link() search non_vlan_clients by hand instead of calling qemu_find_netdev(). Signed-off-by: Markus Armbruster Signed-off-by: Michael S. Tsirkin --- net.c | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/net.c b/net.c index 55c73c592d..e329c693ea 100644 --- a/net.c +++ b/net.c @@ -658,6 +658,8 @@ VLANClientState *qemu_find_netdev(const char *id) VLANClientState *vc; QTAILQ_FOREACH(vc, &non_vlan_clients, next) { + if (vc->info->type == NET_CLIENT_TYPE_NIC) + continue; if (!strcmp(vc->name, id)) { return vc; } @@ -1217,7 +1219,7 @@ int do_netdev_del(Monitor *mon, const QDict *qdict, QObject **ret_data) VLANClientState *vc; vc = qemu_find_netdev(id); - if (!vc || vc->info->type == NET_CLIENT_TYPE_NIC) { + if (!vc) { qerror_report(QERR_DEVICE_NOT_FOUND, id); return -1; } @@ -1262,7 +1264,11 @@ int do_set_link(Monitor *mon, const QDict *qdict, QObject **ret_data) } } } - vc = qemu_find_netdev(name); + QTAILQ_FOREACH(vc, &non_vlan_clients, next) { + if (!strcmp(vc->name, name)) { + goto done; + } + } done: if (!vc) { From 2837c8ea1f10c281c9ff68f397405f3596f8ce0b Mon Sep 17 00:00:00 2001 From: Gerd Hoffmann Date: Fri, 8 Jul 2011 10:44:35 +0200 Subject: [PATCH 003/230] vmstate: add no_migrate flag to VMStateDescription This allows to easily tag devices as non-migratable, so any attempt to migrate a virtual machine with the device in question active will make migration fail. Signed-off-by: Gerd Hoffmann --- hw/hw.h | 1 + savevm.c | 1 + 2 files changed, 2 insertions(+) diff --git a/hw/hw.h b/hw/hw.h index 9dd7096fc2..df6ca65058 100644 --- a/hw/hw.h +++ b/hw/hw.h @@ -324,6 +324,7 @@ typedef struct VMStateSubsection { struct VMStateDescription { const char *name; + int unmigratable; int version_id; int minimum_version_id; int minimum_version_id_old; diff --git a/savevm.c b/savevm.c index 8139bc7e29..1c5abe2476 100644 --- a/savevm.c +++ b/savevm.c @@ -1234,6 +1234,7 @@ int vmstate_register_with_alias_id(DeviceState *dev, int instance_id, se->opaque = opaque; se->vmsd = vmsd; se->alias_id = alias_id; + se->no_migrate = vmsd->unmigratable; if (dev && dev->parent_bus && dev->parent_bus->info->get_dev_path) { char *id = dev->parent_bus->info->get_dev_path(dev); From b7ce1b27f652630e6bc201497ea451b67ad549fa Mon Sep 17 00:00:00 2001 From: Gerd Hoffmann Date: Fri, 8 Jul 2011 10:48:37 +0200 Subject: [PATCH 004/230] ahci doesn't support migration Signed-off-by: Gerd Hoffmann --- hw/ide/ich.c | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/hw/ide/ich.c b/hw/ide/ich.c index 054e0734e4..d241ea8005 100644 --- a/hw/ide/ich.c +++ b/hw/ide/ich.c @@ -72,6 +72,11 @@ #include #include +static const VMStateDescription vmstate_ahci = { + .name = "ahci", + .unmigratable = 1, +}; + static int pci_ich9_ahci_init(PCIDevice *dev) { struct AHCIPCIState *d; @@ -123,6 +128,7 @@ static PCIDeviceInfo ich_ahci_info[] = { .qdev.name = "ich9-ahci", .qdev.alias = "ahci", .qdev.size = sizeof(AHCIPCIState), + .qdev.vmsd = &vmstate_ahci, .init = pci_ich9_ahci_init, .exit = pci_ich9_uninit, .config_write = pci_ich9_write_config, From 9490fb0624e67bce90297444fb960c2d9476239e Mon Sep 17 00:00:00 2001 From: Gerd Hoffmann Date: Fri, 8 Jul 2011 10:48:46 +0200 Subject: [PATCH 005/230] ehci doesn't support migration Signed-off-by: Gerd Hoffmann --- hw/usb-ehci.c | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/hw/usb-ehci.c b/hw/usb-ehci.c index a4758f976e..8b0dcc335d 100644 --- a/hw/usb-ehci.c +++ b/hw/usb-ehci.c @@ -2244,6 +2244,11 @@ static USBBusOps ehci_bus_ops = { .register_companion = ehci_register_companion, }; +static const VMStateDescription vmstate_ehci = { + .name = "ehci", + .unmigratable = 1, +}; + static Property ehci_properties[] = { DEFINE_PROP_UINT32("freq", EHCIState, freq, FRAME_TIMER_FREQ), DEFINE_PROP_UINT32("maxframes", EHCIState, maxframes, 128), @@ -2254,6 +2259,7 @@ static PCIDeviceInfo ehci_info[] = { { .qdev.name = "usb-ehci", .qdev.size = sizeof(EHCIState), + .qdev.vmsd = &vmstate_ehci, .init = usb_ehci_initfn, .vendor_id = PCI_VENDOR_ID_INTEL, .device_id = PCI_DEVICE_ID_INTEL_82801D, /* ich4 */ @@ -2263,6 +2269,7 @@ static PCIDeviceInfo ehci_info[] = { },{ .qdev.name = "ich9-usb-ehci1", .qdev.size = sizeof(EHCIState), + .qdev.vmsd = &vmstate_ehci, .init = usb_ehci_initfn, .vendor_id = PCI_VENDOR_ID_INTEL, .device_id = PCI_DEVICE_ID_INTEL_82801I_EHCI1, From f54b65630385d7dc7cf3442eb459d1a5b3d1a9c6 Mon Sep 17 00:00:00 2001 From: Gerd Hoffmann Date: Fri, 10 Dec 2010 14:58:41 +0100 Subject: [PATCH 006/230] usb storage: first migration support bits. Tag vmstate as unmigratable for the time being, to be removed when mgration support is finished. Signed-off-by: Gerd Hoffmann --- hw/usb-msd.c | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/hw/usb-msd.c b/hw/usb-msd.c index 86582cc723..8ed85943b8 100644 --- a/hw/usb-msd.c +++ b/hw/usb-msd.c @@ -623,11 +623,23 @@ static USBDevice *usb_msd_init(const char *filename) return dev; } +static const VMStateDescription vmstate_usb_msd = { + .name = "usb-storage", + .unmigratable = 1, /* FIXME: handle transactions which are in flight */ + .version_id = 1, + .minimum_version_id = 1, + .fields = (VMStateField []) { + VMSTATE_USB_DEVICE(dev, MSDState), + VMSTATE_END_OF_LIST() + } +}; + static struct USBDeviceInfo msd_info = { .product_desc = "QEMU USB MSD", .qdev.name = "usb-storage", .qdev.fw_name = "storage", .qdev.size = sizeof(MSDState), + .qdev.vmsd = &vmstate_usb_msd, .usb_desc = &desc, .init = usb_msd_initfn, .handle_packet = usb_generic_handle_packet, From ccce9fd205317f60dd30a986084eec39addbd09b Mon Sep 17 00:00:00 2001 From: Gerd Hoffmann Date: Wed, 20 Jul 2011 10:00:51 +0200 Subject: [PATCH 007/230] usb-wacom doesn't support migration Signed-off-by: Gerd Hoffmann --- hw/usb-wacom.c | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/hw/usb-wacom.c b/hw/usb-wacom.c index 9d348e170e..d76ee97e49 100644 --- a/hw/usb-wacom.c +++ b/hw/usb-wacom.c @@ -349,6 +349,11 @@ static int usb_wacom_initfn(USBDevice *dev) return 0; } +static const VMStateDescription vmstate_usb_wacom = { + .name = "usb-wacom", + .unmigratable = 1, +}; + static struct USBDeviceInfo wacom_info = { .product_desc = "QEMU PenPartner Tablet", .qdev.name = "usb-wacom-tablet", @@ -356,6 +361,7 @@ static struct USBDeviceInfo wacom_info = { .usbdevice_name = "wacom-tablet", .usb_desc = &desc_wacom, .qdev.size = sizeof(USBWacomState), + .qdev.vmsd = &vmstate_usb_wacom, .init = usb_wacom_initfn, .handle_packet = usb_generic_handle_packet, .handle_reset = usb_wacom_handle_reset, From 2474e5052b3987cbd7f41f6a2992ce691dc8cc0c Mon Sep 17 00:00:00 2001 From: Gerd Hoffmann Date: Wed, 20 Jul 2011 10:02:40 +0200 Subject: [PATCH 008/230] usb-bt doesn't support migration Signed-off-by: Gerd Hoffmann --- hw/usb-bt.c | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/hw/usb-bt.c b/hw/usb-bt.c index e364513a01..4557802bbc 100644 --- a/hw/usb-bt.c +++ b/hw/usb-bt.c @@ -548,10 +548,16 @@ USBDevice *usb_bt_init(HCIInfo *hci) return dev; } +static const VMStateDescription vmstate_usb_bt = { + .name = "usb-bt", + .unmigratable = 1, +}; + static struct USBDeviceInfo bt_info = { .product_desc = "QEMU BT dongle", .qdev.name = "usb-bt-dongle", .qdev.size = sizeof(struct USBBtState), + .qdev.vmsd = &vmstate_usb_bt, .usb_desc = &desc_bluetooth, .init = usb_bt_initfn, .handle_packet = usb_generic_handle_packet, From 4ab0ba9e26d52a272cadd5635437a341a4e7ff36 Mon Sep 17 00:00:00 2001 From: Gerd Hoffmann Date: Wed, 20 Jul 2011 10:06:07 +0200 Subject: [PATCH 009/230] usb-net doesn't support migration Signed-off-by: Gerd Hoffmann --- hw/usb-net.c | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/hw/usb-net.c b/hw/usb-net.c index 9be709f7cf..4212e5b3c5 100644 --- a/hw/usb-net.c +++ b/hw/usb-net.c @@ -1414,11 +1414,17 @@ static USBDevice *usb_net_init(const char *cmdline) return dev; } +static const VMStateDescription vmstate_usb_net = { + .name = "usb-net", + .unmigratable = 1, +}; + static struct USBDeviceInfo net_info = { .product_desc = "QEMU USB Network Interface", .qdev.name = "usb-net", .qdev.fw_name = "network", .qdev.size = sizeof(USBNetState), + .qdev.vmsd = &vmstate_usb_net, .usb_desc = &desc_net, .init = usb_net_initfn, .handle_packet = usb_generic_handle_packet, From 98e51ec92e678cf0e501b5ef013753ec8710e222 Mon Sep 17 00:00:00 2001 From: Gerd Hoffmann Date: Wed, 20 Jul 2011 10:06:18 +0200 Subject: [PATCH 010/230] usb-serial doesn't support migration Signed-off-by: Gerd Hoffmann --- hw/usb-serial.c | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/hw/usb-serial.c b/hw/usb-serial.c index 59cb0fb2f7..70d694d3cb 100644 --- a/hw/usb-serial.c +++ b/hw/usb-serial.c @@ -566,10 +566,16 @@ static USBDevice *usb_braille_init(const char *unused) return dev; } +static const VMStateDescription vmstate_usb_serial = { + .name = "usb-serial", + .unmigratable = 1, +}; + static struct USBDeviceInfo serial_info = { .product_desc = "QEMU USB Serial", .qdev.name = "usb-serial", .qdev.size = sizeof(USBSerialState), + .qdev.vmsd = &vmstate_usb_serial, .usb_desc = &desc_serial, .init = usb_serial_initfn, .handle_packet = usb_generic_handle_packet, @@ -589,6 +595,7 @@ static struct USBDeviceInfo braille_info = { .product_desc = "QEMU USB Braille", .qdev.name = "usb-braille", .qdev.size = sizeof(USBSerialState), + .qdev.vmsd = &vmstate_usb_serial, .usb_desc = &desc_braille, .init = usb_serial_initfn, .handle_packet = usb_generic_handle_packet, From 010debef6111c6776cda7ad8d00737f2d1dbb164 Mon Sep 17 00:00:00 2001 From: Robert Relyea Date: Tue, 28 Jun 2011 17:28:22 +0200 Subject: [PATCH 011/230] libcacard/vcard_emul_nss: support cards lying about CKM_RSA_X_509 support Some tokens claim to do CKM_RSA_X_509, but then choke when they try to do the actual operations. Try to detect those cases and treat them as if the token didn't claim support for X_509. Signed-off-by: Robert Relyea --- libcacard/vcard_emul_nss.c | 133 +++++++++++++++++++++++++++++++++++-- 1 file changed, 127 insertions(+), 6 deletions(-) diff --git a/libcacard/vcard_emul_nss.c b/libcacard/vcard_emul_nss.c index f3db657d74..0f5095405f 100644 --- a/libcacard/vcard_emul_nss.c +++ b/libcacard/vcard_emul_nss.c @@ -33,10 +33,17 @@ #include "vreader.h" #include "vevent.h" +typedef enum { + VCardEmulUnknown = -1, + VCardEmulFalse = 0, + VCardEmulTrue = 1 +} VCardEmulTriState; + struct VCardKeyStruct { CERTCertificate *cert; PK11SlotInfo *slot; SECKEYPrivateKey *key; + VCardEmulTriState failedX509; }; @@ -140,6 +147,7 @@ vcard_emul_make_key(PK11SlotInfo *slot, CERTCertificate *cert) /* NOTE: the cert is a temp cert, not necessarily the cert in the token, * use the DER version of this function */ key->key = PK11_FindKeyByDERCert(slot, cert, NULL); + key->failedX509 = VCardEmulUnknown; return key; } @@ -208,13 +216,23 @@ vcard_emul_rsa_op(VCard *card, VCardKey *key, { SECKEYPrivateKey *priv_key; unsigned signature_len; + PK11SlotInfo *slot; SECStatus rv; + unsigned char buf[2048]; + unsigned char *bp = NULL; + int pad_len; + vcard_7816_status_t ret = VCARD7816_STATUS_SUCCESS; if ((!nss_emul_init) || (key == NULL)) { /* couldn't get the key, indicate that we aren't logged in */ return VCARD7816_STATUS_ERROR_CONDITION_NOT_SATISFIED; } priv_key = vcard_emul_get_nss_key(key); + if (priv_key == NULL) { + /* couldn't get the key, indicate that we aren't logged in */ + return VCARD7816_STATUS_ERROR_CONDITION_NOT_SATISFIED; + } + slot = vcard_emul_card_get_slot(card); /* * this is only true of the rsa signature @@ -223,13 +241,116 @@ vcard_emul_rsa_op(VCard *card, VCardKey *key, if (buffer_size != signature_len) { return VCARD7816_STATUS_ERROR_DATA_INVALID; } - rv = PK11_PrivDecryptRaw(priv_key, buffer, &signature_len, signature_len, - buffer, buffer_size); - if (rv != SECSuccess) { - return vcard_emul_map_error(PORT_GetError()); + /* be able to handle larger keys if necessariy */ + bp = &buf[0]; + if (sizeof(buf) < signature_len) { + bp = qemu_malloc(signature_len); } - assert(buffer_size == signature_len); - return VCARD7816_STATUS_SUCCESS; + + /* + * do the raw operations. Some tokens claim to do CKM_RSA_X_509, but then + * choke when they try to do the actual operations. Try to detect + * those cases and treat them as if the token didn't claim support for + * X_509. + */ + if (key->failedX509 != VCardEmulTrue + && PK11_DoesMechanism(slot, CKM_RSA_X_509)) { + rv = PK11_PrivDecryptRaw(priv_key, bp, &signature_len, signature_len, + buffer, buffer_size); + if (rv == SECSuccess) { + assert(buffer_size == signature_len); + memcpy(buffer, bp, signature_len); + key->failedX509 = VCardEmulFalse; + goto cleanup; + } + /* + * we've had a successful X509 operation, this failure must be + * somethine else + */ + if (key->failedX509 == VCardEmulFalse) { + ret = vcard_emul_map_error(PORT_GetError()); + goto cleanup; + } + /* + * key->failedX509 must be Unknown at this point, try the + * non-x_509 case + */ + } + /* token does not support CKM_RSA_X509, emulate that with CKM_RSA_PKCS */ + /* is this a PKCS #1 formatted signature? */ + if ((buffer[0] == 0) && (buffer[1] == 1)) { + int i; + + for (i = 2; i < buffer_size; i++) { + /* rsa signature pad */ + if (buffer[i] != 0xff) { + break; + } + } + if ((i < buffer_size) && (buffer[i] == 0)) { + /* yes, we have a properly formated PKCS #1 signature */ + /* + * NOTE: even if we accidentally got an encrypt buffer, which + * through shear luck started with 00, 01, ff, 00, it won't matter + * because the resulting Sign operation will effectively decrypt + * the real buffer. + */ + SECItem signature; + SECItem hash; + + i++; + hash.data = &buffer[i]; + hash.len = buffer_size - i; + signature.data = bp; + signature.len = signature_len; + rv = PK11_Sign(priv_key, &signature, &hash); + if (rv != SECSuccess) { + ret = vcard_emul_map_error(PORT_GetError()); + goto cleanup; + } + assert(buffer_size == signature.len); + memcpy(buffer, bp, signature.len); + /* + * we got here because either the X509 attempt failed, or the + * token couldn't do the X509 operation, in either case stay + * with the PKCS version for future operations on this key + */ + key->failedX509 = VCardEmulTrue; + goto cleanup; + } + } + pad_len = buffer_size - signature_len; + assert(pad_len < 4); + /* + * OK now we've decrypted the payload, package it up in PKCS #1 for the + * upper layer. + */ + buffer[0] = 0; + buffer[1] = 2; /* RSA_encrypt */ + pad_len -= 3; /* format is 0 || 2 || pad || 0 || data */ + /* + * padding for PKCS #1 encrypted data is a string of random bytes. The + * random butes protect against potential decryption attacks against RSA. + * Since PrivDecrypt has already stripped those bytes, we can't reconstruct + * them. This shouldn't matter to the upper level code which should just + * strip this code out anyway, so We'll pad with a constant 3. + */ + memset(&buffer[2], 0x03, pad_len); + pad_len += 2; /* index to the end of the pad */ + buffer[pad_len] = 0; + pad_len++; /* index to the start of the data */ + memcpy(&buffer[pad_len], bp, signature_len); + /* + * we got here because either the X509 attempt failed, or the + * token couldn't do the X509 operation, in either case stay + * with the PKCS version for future operations on this key + */ + key->failedX509 = VCardEmulTrue; +cleanup: + if (bp != buf) { + qemu_free(bp); + } + return ret; } /* From ee83d41466ab393d82d9abf57b9ec24d4e6633be Mon Sep 17 00:00:00 2001 From: Christophe Fergeau Date: Mon, 4 Jul 2011 18:10:43 +0200 Subject: [PATCH 012/230] libcacard: don't leak vcard_emul_alloc_arrays mem vcard_emul_mirror_card and vcard_emul_init use vcard_emul_alloc_arrays to allocate memory for temporary arrays which will contain elements that in the end will be used one by one in cac_card_init. The arrays themselves are never stored anywhere, they are only used as temporary containers. Hence the memory that was allocated for these arrays should be freed after use or they will be leaked. --- libcacard/vcard_emul_nss.c | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/libcacard/vcard_emul_nss.c b/libcacard/vcard_emul_nss.c index 0f5095405f..f1763f55f7 100644 --- a/libcacard/vcard_emul_nss.c +++ b/libcacard/vcard_emul_nss.c @@ -597,6 +597,7 @@ vcard_emul_mirror_card(VReader *vreader) VCardKey **keys; PK11SlotInfo *slot; PRBool ret; + VCard *card; slot = vcard_emul_reader_get_slot(vreader); if (slot == NULL) { @@ -656,7 +657,12 @@ vcard_emul_mirror_card(VReader *vreader) } /* now create the card */ - return vcard_emul_make_card(vreader, certs, cert_len, keys, cert_count); + card = vcard_emul_make_card(vreader, certs, cert_len, keys, cert_count); + qemu_free(certs); + qemu_free(cert_len); + qemu_free(keys); + + return card; } static VCardEmulType default_card_type = VCARD_EMUL_NONE; @@ -941,6 +947,9 @@ vcard_emul_init(const VCardEmulOptions *options) vreader_free(vreader); has_readers = PR_TRUE; } + qemu_free(certs); + qemu_free(cert_len); + qemu_free(keys); } /* if we aren't suppose to use hw, skip looking up hardware tokens */ From 009651675afc775fc77018273a47fb36c28a8100 Mon Sep 17 00:00:00 2001 From: Christophe Fergeau Date: Fri, 22 Jul 2011 13:42:17 +0200 Subject: [PATCH 013/230] libcacard: s/strip(args++)/strip(args+1) vcard_emul_options used args = strip(args++) a few times, which was not returning the expected result since the rest of the code expected args to be increased by at least 1, which is not the case if *args is not a blank space when this function is called. Replace these calls by "strip(args+1)" which will do what we expect. Signed-off-by: Christophe Fergeau Reviewed-by: Alon Levy --- libcacard/vcard_emul_nss.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/libcacard/vcard_emul_nss.c b/libcacard/vcard_emul_nss.c index f1763f55f7..8c59eff81d 100644 --- a/libcacard/vcard_emul_nss.c +++ b/libcacard/vcard_emul_nss.c @@ -1171,7 +1171,7 @@ vcard_emul_options(const char *args) args++; continue; } - args = strip(args++); + args = strip(args+1); type_params = args; args = strpbrk(args + 1, ",)"); if (*args == 0) { @@ -1182,7 +1182,7 @@ vcard_emul_options(const char *args) continue; } type_params_length = args - name; - args = strip(args++); + args = strip(args+1); if (*args == 0) { break; } From a5aa842a0597ef9a24e80966b02ca01f287fb334 Mon Sep 17 00:00:00 2001 From: Christophe Fergeau Date: Fri, 22 Jul 2011 13:42:18 +0200 Subject: [PATCH 014/230] libcacard: fix soft=... parsing in vcard_emul_options The previous parser had copy and paste errors when computing vname_length and type_params_length, "name" was used instead of respectively vname and type_params. This led to length that could be bigger than the input string, and to access out of the array bounds when trying to copy these strings. valgrind rightfully complained about this. It also didn't handle empty fields correctly, Signed-off-by: Christophe Fergeau Reviewed-by: Alon Levy --- libcacard/vcard_emul_nss.c | 81 ++++++++++++++++++++++---------------- 1 file changed, 46 insertions(+), 35 deletions(-) diff --git a/libcacard/vcard_emul_nss.c b/libcacard/vcard_emul_nss.c index 8c59eff81d..3360f6c22f 100644 --- a/libcacard/vcard_emul_nss.c +++ b/libcacard/vcard_emul_nss.c @@ -1110,8 +1110,6 @@ vcard_emul_options(const char *args) { int reader_count = 0; VCardEmulOptions *opts; - char type_str[100]; - int type_len; /* Allow the future use of allocating the options structure on the fly */ memcpy(&options, &default_options, sizeof(options)); @@ -1126,43 +1124,23 @@ vcard_emul_options(const char *args) * cert_2,cert_3...) */ if (strncmp(args, "soft=", 5) == 0) { const char *name; + size_t name_length; const char *vname; + size_t vname_length; const char *type_params; + size_t type_params_length; + char type_str[100]; VCardEmulType type; - int name_length, vname_length, type_params_length, count, i; + int count, i; VirtualReaderOptions *vreaderOpt = NULL; args = strip(args + 5); if (*args != '(') { continue; } + args = strip(args+1); + name = args; - args = strpbrk(args + 1, ",)"); - if (*args == 0) { - break; - } - if (*args == ')') { - args++; - continue; - } - args = strip(args+1); - name_length = args - name - 2; - vname = args; - args = strpbrk(args + 1, ",)"); - if (*args == 0) { - break; - } - if (*args == ')') { - args++; - continue; - } - vname_length = args - name - 2; - args = strip(args+1); - type_len = strpbrk(args, ",)") - args; - assert(sizeof(type_str) > type_len); - strncpy(type_str, args, type_len); - type_str[type_len] = 0; - type = vcard_emul_type_from_string(type_str); args = strpbrk(args, ",)"); if (*args == 0) { break; @@ -1171,9 +1149,11 @@ vcard_emul_options(const char *args) args++; continue; } + name_length = args - name; args = strip(args+1); - type_params = args; - args = strpbrk(args + 1, ",)"); + + vname = args; + args = strpbrk(args, ",)"); if (*args == 0) { break; } @@ -1181,8 +1161,38 @@ vcard_emul_options(const char *args) args++; continue; } - type_params_length = args - name; + vname_length = args - vname; args = strip(args+1); + + type_params = args; + args = strpbrk(args, ",)"); + if (*args == 0) { + break; + } + if (*args == ')') { + args++; + continue; + } + type_params_length = args - type_params; + args = strip(args+1); + + type_params_length = MIN(type_params_length, sizeof(type_str)-1); + strncpy(type_str, type_params, type_params_length); + type_str[type_params_length] = 0; + type = vcard_emul_type_from_string(type_str); + + type_params = args; + args = strpbrk(args, ",)"); + if (*args == 0) { + break; + } + if (*args == ')') { + args++; + continue; + } + type_params_length = args - type_params; + args = strip(args+1); + if (*args == 0) { break; } @@ -1202,13 +1212,14 @@ vcard_emul_options(const char *args) vreaderOpt->card_type = type; vreaderOpt->type_params = copy_string(type_params, type_params_length); - count = count_tokens(args, ',', ')'); + count = count_tokens(args, ',', ')') + 1; vreaderOpt->cert_count = count; vreaderOpt->cert_name = (char **)qemu_malloc(count*sizeof(char *)); for (i = 0; i < count; i++) { - const char *cert = args + 1; - args = strpbrk(args + 1, ",)"); + const char *cert = args; + args = strpbrk(args, ",)"); vreaderOpt->cert_name[i] = copy_string(cert, args - cert); + args = strip(args+1); } if (*args == ')') { args++; From d246b3cfd5378e45895b0834a8b8762733c0148f Mon Sep 17 00:00:00 2001 From: Christophe Fergeau Date: Fri, 22 Jul 2011 13:42:19 +0200 Subject: [PATCH 015/230] libcacard: introduce NEXT_TOKEN macro vcard_emul_options now has repetitive code to read the current token and advance to the next. After the previous changes, this repetitive code can be moved in a NEXT_TOKEN macro to avoid having this code duplicated. Signed-off-by: Christophe Fergeau Reviewed-by: Alon Levy --- libcacard/vcard_emul_nss.c | 71 +++++++++++++------------------------- 1 file changed, 24 insertions(+), 47 deletions(-) diff --git a/libcacard/vcard_emul_nss.c b/libcacard/vcard_emul_nss.c index 3360f6c22f..1a24acf645 100644 --- a/libcacard/vcard_emul_nss.c +++ b/libcacard/vcard_emul_nss.c @@ -1105,6 +1105,26 @@ find_blank(const char *str) static VCardEmulOptions options; #define READER_STEP 4 +/* Expects "args" to be at the beginning of a token (ie right after the ',' + * ending the previous token), and puts the next token start in "token", + * and its length in "token_length". "token" will not be nul-terminated. + * After calling the macro, "args" will be advanced to the beginning of + * the next token. + * This macro may call continue or break. + */ +#define NEXT_TOKEN(token) \ + (token) = args; \ + args = strpbrk(args, ",)"); \ + if (*args == 0) { \ + break; \ + } \ + if (*args == ')') { \ + args++; \ + continue; \ + } \ + (token##_length) = args - (token); \ + args = strip(args+1); + VCardEmulOptions * vcard_emul_options(const char *args) { @@ -1140,58 +1160,15 @@ vcard_emul_options(const char *args) } args = strip(args+1); - name = args; - args = strpbrk(args, ",)"); - if (*args == 0) { - break; - } - if (*args == ')') { - args++; - continue; - } - name_length = args - name; - args = strip(args+1); - - vname = args; - args = strpbrk(args, ",)"); - if (*args == 0) { - break; - } - if (*args == ')') { - args++; - continue; - } - vname_length = args - vname; - args = strip(args+1); - - type_params = args; - args = strpbrk(args, ",)"); - if (*args == 0) { - break; - } - if (*args == ')') { - args++; - continue; - } - type_params_length = args - type_params; - args = strip(args+1); - + NEXT_TOKEN(name) + NEXT_TOKEN(vname) + NEXT_TOKEN(type_params) type_params_length = MIN(type_params_length, sizeof(type_str)-1); strncpy(type_str, type_params, type_params_length); type_str[type_params_length] = 0; type = vcard_emul_type_from_string(type_str); - type_params = args; - args = strpbrk(args, ",)"); - if (*args == 0) { - break; - } - if (*args == ')') { - args++; - continue; - } - type_params_length = args - type_params; - args = strip(args+1); + NEXT_TOKEN(type_params) if (*args == 0) { break; From 2b56cb87e46fbfc5dca31ed13ffc9a5422e1e405 Mon Sep 17 00:00:00 2001 From: Christophe Fergeau Date: Fri, 22 Jul 2011 13:42:20 +0200 Subject: [PATCH 016/230] libcacard: replace copy_string with strndup copy_string reimplements strndup, this commit removes it and replaces all copy_string uses with strndup. Signed-off-by: Christophe Fergeau Reviewed-by: Alon Levy --- libcacard/vcard_emul_nss.c | 23 ++++++----------------- 1 file changed, 6 insertions(+), 17 deletions(-) diff --git a/libcacard/vcard_emul_nss.c b/libcacard/vcard_emul_nss.c index 1a24acf645..84fc49026f 100644 --- a/libcacard/vcard_emul_nss.c +++ b/libcacard/vcard_emul_nss.c @@ -1055,17 +1055,6 @@ vcard_emul_replay_insertion_events(void) /* * Silly little functions to help parsing our argument string */ -static char * -copy_string(const char *str, int str_len) -{ - char *new_str; - - new_str = qemu_malloc(str_len+1); - memcpy(new_str, str, str_len); - new_str[str_len] = 0; - return new_str; -} - static int count_tokens(const char *str, char token, char token_end) { @@ -1184,18 +1173,18 @@ vcard_emul_options(const char *args) } opts->vreader = vreaderOpt; vreaderOpt = &vreaderOpt[opts->vreader_count]; - vreaderOpt->name = copy_string(name, name_length); - vreaderOpt->vname = copy_string(vname, vname_length); + vreaderOpt->name = qemu_strndup(name, name_length); + vreaderOpt->vname = qemu_strndup(vname, vname_length); vreaderOpt->card_type = type; vreaderOpt->type_params = - copy_string(type_params, type_params_length); + qemu_strndup(type_params, type_params_length); count = count_tokens(args, ',', ')') + 1; vreaderOpt->cert_count = count; vreaderOpt->cert_name = (char **)qemu_malloc(count*sizeof(char *)); for (i = 0; i < count; i++) { const char *cert = args; args = strpbrk(args, ",)"); - vreaderOpt->cert_name[i] = copy_string(cert, args - cert); + vreaderOpt->cert_name[i] = qemu_strndup(cert, args - cert); args = strip(args+1); } if (*args == ')') { @@ -1222,7 +1211,7 @@ vcard_emul_options(const char *args) args = strip(args+10); params = args; args = find_blank(args); - opts->hw_type_params = copy_string(params, args-params); + opts->hw_type_params = qemu_strndup(params, args-params); /* db="/data/base/path" */ } else if (strncmp(args, "db=", 3) == 0) { const char *db; @@ -1233,7 +1222,7 @@ vcard_emul_options(const char *args) args++; db = args; args = strpbrk(args, "\"\n"); - opts->nss_db = copy_string(db, args-db); + opts->nss_db = qemu_strndup(db, args-db); if (*args != 0) { args++; } From 9052ea6bf4962b1342aa56d4341bb55176ed9e45 Mon Sep 17 00:00:00 2001 From: Jan Kiszka Date: Sat, 23 Jul 2011 12:38:37 +0200 Subject: [PATCH 017/230] Generalize -machine command line option -machine somehow suggests that it selects the machine, but it doesn't. Fix that before this command is set in stone. Actually, -machine should supersede -M and allow to introduce arbitrary per-machine options to the command line. That will change the internal realization again, but we will be able to keep the user interface stable. Tested-by: Ian Campbell Signed-off-by: Jan Kiszka Signed-off-by: Jan Kiszka Signed-off-by: Anthony Liguori --- qemu-config.c | 5 +++++ qemu-options.hx | 20 +++++++++++++++----- vl.c | 43 ++++++++++++++++++++++++++----------------- 3 files changed, 46 insertions(+), 22 deletions(-) diff --git a/qemu-config.c b/qemu-config.c index 93d20c6cf8..b2ec40bd66 100644 --- a/qemu-config.c +++ b/qemu-config.c @@ -464,9 +464,14 @@ QemuOptsList qemu_option_rom_opts = { static QemuOptsList qemu_machine_opts = { .name = "machine", + .implied_opt_name = "type", .head = QTAILQ_HEAD_INITIALIZER(qemu_machine_opts.head), .desc = { { + .name = "type", + .type = QEMU_OPT_STRING, + .help = "emulated machine" + }, { .name = "accel", .type = QEMU_OPT_STRING, .help = "accelerator list", diff --git a/qemu-options.hx b/qemu-options.hx index 64114dd448..195943bf89 100644 --- a/qemu-options.hx +++ b/qemu-options.hx @@ -2075,13 +2075,23 @@ if KVM support is enabled when compiling. ETEXI DEF("machine", HAS_ARG, QEMU_OPTION_machine, \ - "-machine accel=accel1[:accel2] use an accelerator (kvm,xen,tcg), default is tcg\n", QEMU_ARCH_ALL) + "-machine [type=]name[,prop[=value][,...]]\n" + " selects emulated machine (-machine ? for list)\n" + " property accel=accel1[:accel2[:...]] selects accelerator\n" + " supported accelerators are kvm, xen, tcg (default: tcg)\n", + QEMU_ARCH_ALL) STEXI -@item -machine accel=@var{accels} +@item -machine [type=]@var{name}[,prop=@var{value}[,...]] @findex -machine -This is use to enable an accelerator, in kvm,xen,tcg. -By default, it use only tcg. If there a more than one accelerator -specified, the next one is used if the first don't work. +Select the emulated machine by @var{name}. Use @code{-machine ?} to list +available machines. Supported machine properties are: +@table @option +@item accel=@var{accels1}[:@var{accels2}[:...]] +This is used to enable an accelerator. Depending on the target architecture, +kvm, xen, or tcg can be available. By default, tcg is used. If there is more +than one accelerator specified, the next one is used if the previous one fails +to initialize. +@end table ETEXI DEF("xen-domid", HAS_ARG, QEMU_OPTION_xen_domid, diff --git a/vl.c b/vl.c index fcd7395dd8..acfff858a1 100644 --- a/vl.c +++ b/vl.c @@ -1899,6 +1899,27 @@ static int debugcon_parse(const char *devname) return 0; } +static QEMUMachine *machine_parse(const char *name) +{ + QEMUMachine *m, *machine = NULL; + + if (name) { + machine = find_machine(name); + } + if (machine) { + return machine; + } + printf("Supported machines are:\n"); + for (m = first_machine; m != NULL; m = m->next) { + if (m->alias) { + printf("%-10s %s (alias of %s)\n", m->alias, m->desc, m->name); + } + printf("%-10s %s%s\n", m->name, m->desc, + m->is_default ? " (default)" : ""); + } + exit(!name || *name != '?'); +} + static int tcg_init(void) { return 0; @@ -2155,20 +2176,7 @@ int main(int argc, char **argv, char **envp) } switch(popt->index) { case QEMU_OPTION_M: - machine = find_machine(optarg); - if (!machine) { - QEMUMachine *m; - printf("Supported machines are:\n"); - for(m = first_machine; m != NULL; m = m->next) { - if (m->alias) - printf("%-10s %s (alias of %s)\n", - m->alias, m->desc, m->name); - printf("%-10s %s%s\n", - m->name, m->desc, - m->is_default ? " (default)" : ""); - } - exit(*optarg != '?'); - } + machine = machine_parse(optarg); break; case QEMU_OPTION_cpu: /* hw initialization will check this */ @@ -2698,11 +2706,12 @@ int main(int argc, char **argv, char **envp) case QEMU_OPTION_machine: olist = qemu_find_opts("machine"); qemu_opts_reset(olist); - opts = qemu_opts_parse(olist, optarg, 0); + opts = qemu_opts_parse(olist, optarg, 1); if (!opts) { fprintf(stderr, "parse error: %s\n", optarg); exit(1); } + machine = machine_parse(qemu_opt_get(opts, "type")); break; case QEMU_OPTION_usb: usb_enabled = 1; @@ -2976,8 +2985,8 @@ int main(int argc, char **argv, char **envp) p = qemu_opt_get(QTAILQ_FIRST(&list->head), "accel"); } if (p == NULL) { - opts = qemu_opts_parse(qemu_find_opts("machine"), - machine->default_machine_opts, 0); + qemu_opts_reset(list); + opts = qemu_opts_parse(list, machine->default_machine_opts, 0); if (!opts) { fprintf(stderr, "parse error for machine %s: %s\n", machine->name, machine->default_machine_opts); From 80f52a6694423da7a40e2ec39e14a5817184c7ef Mon Sep 17 00:00:00 2001 From: Jan Kiszka Date: Sat, 23 Jul 2011 12:39:46 +0200 Subject: [PATCH 018/230] Deprecate -M command line options Superseded by -machine. Therefore, this patch removes -M from the help list and pushes -machine at the same place in the output. Signed-off-by: Jan Kiszka Signed-off-by: Anthony Liguori --- qemu-options.hx | 45 ++++++++++++++++++++------------------------- 1 file changed, 20 insertions(+), 25 deletions(-) diff --git a/qemu-options.hx b/qemu-options.hx index 195943bf89..1233f834df 100644 --- a/qemu-options.hx +++ b/qemu-options.hx @@ -27,14 +27,29 @@ STEXI Display version information and exit ETEXI -DEF("M", HAS_ARG, QEMU_OPTION_M, - "-M machine select emulated machine (-M ? for list)\n", QEMU_ARCH_ALL) +DEF("machine", HAS_ARG, QEMU_OPTION_machine, \ + "-machine [type=]name[,prop[=value][,...]]\n" + " selects emulated machine (-machine ? for list)\n" + " property accel=accel1[:accel2[:...]] selects accelerator\n" + " supported accelerators are kvm, xen, tcg (default: tcg)\n", + QEMU_ARCH_ALL) STEXI -@item -M @var{machine} -@findex -M -Select the emulated @var{machine} (@code{-M ?} for list) +@item -machine [type=]@var{name}[,prop=@var{value}[,...]] +@findex -machine +Select the emulated machine by @var{name}. Use @code{-machine ?} to list +available machines. Supported machine properties are: +@table @option +@item accel=@var{accels1}[:@var{accels2}[:...]] +This is used to enable an accelerator. Depending on the target architecture, +kvm, xen, or tcg can be available. By default, tcg is used. If there is more +than one accelerator specified, the next one is used if the previous one fails +to initialize. +@end table ETEXI +HXCOMM Deprecated by -machine +DEF("M", HAS_ARG, QEMU_OPTION_M, "", QEMU_ARCH_ALL) + DEF("cpu", HAS_ARG, QEMU_OPTION_cpu, "-cpu cpu select CPU (-cpu ? for list)\n", QEMU_ARCH_ALL) STEXI @@ -2074,26 +2089,6 @@ Enable KVM full virtualization support. This option is only available if KVM support is enabled when compiling. ETEXI -DEF("machine", HAS_ARG, QEMU_OPTION_machine, \ - "-machine [type=]name[,prop[=value][,...]]\n" - " selects emulated machine (-machine ? for list)\n" - " property accel=accel1[:accel2[:...]] selects accelerator\n" - " supported accelerators are kvm, xen, tcg (default: tcg)\n", - QEMU_ARCH_ALL) -STEXI -@item -machine [type=]@var{name}[,prop=@var{value}[,...]] -@findex -machine -Select the emulated machine by @var{name}. Use @code{-machine ?} to list -available machines. Supported machine properties are: -@table @option -@item accel=@var{accels1}[:@var{accels2}[:...]] -This is used to enable an accelerator. Depending on the target architecture, -kvm, xen, or tcg can be available. By default, tcg is used. If there is more -than one accelerator specified, the next one is used if the previous one fails -to initialize. -@end table -ETEXI - DEF("xen-domid", HAS_ARG, QEMU_OPTION_xen_domid, "-xen-domid id specify xen guest domain id\n", QEMU_ARCH_ALL) DEF("xen-create", 0, QEMU_OPTION_xen_create, From 12b513d837c9da5277390ddaf98ca0058339977a Mon Sep 17 00:00:00 2001 From: Jan Kiszka Date: Wed, 20 Jul 2011 12:20:13 +0200 Subject: [PATCH 019/230] slirp: Fix restricted mode This aligns the code to what the documentation claims: Allow everything but requests that would have to be routed outside of the virtual LAN. So we need to drop the unneeded IP-level filter, allow TFTP requests, and add the missing protocol-level filter to ICMP. CC: Gleb Natapov Signed-off-by: Jan Kiszka Signed-off-by: Anthony Liguori --- slirp/ip_icmp.c | 2 ++ slirp/ip_input.c | 21 --------------------- slirp/udp.c | 8 ++++---- 3 files changed, 6 insertions(+), 25 deletions(-) diff --git a/slirp/ip_icmp.c b/slirp/ip_icmp.c index 751a8e249a..0cd129cc97 100644 --- a/slirp/ip_icmp.c +++ b/slirp/ip_icmp.c @@ -101,6 +101,8 @@ icmp_input(struct mbuf *m, int hlen) ip->ip_len += hlen; /* since ip_input subtracts this */ if (ip->ip_dst.s_addr == slirp->vhost_addr.s_addr) { icmp_reflect(m); + } else if (slirp->restricted) { + goto freeit; } else { struct socket *so; struct sockaddr_in addr; diff --git a/slirp/ip_input.c b/slirp/ip_input.c index 768ab0cd49..2ff6adbc4c 100644 --- a/slirp/ip_input.c +++ b/slirp/ip_input.c @@ -118,27 +118,6 @@ ip_input(struct mbuf *m) goto bad; } - if (slirp->restricted) { - if ((ip->ip_dst.s_addr & slirp->vnetwork_mask.s_addr) == - slirp->vnetwork_addr.s_addr) { - if (ip->ip_dst.s_addr == 0xffffffff && ip->ip_p != IPPROTO_UDP) - goto bad; - } else { - uint32_t inv_mask = ~slirp->vnetwork_mask.s_addr; - struct ex_list *ex_ptr; - - if ((ip->ip_dst.s_addr & inv_mask) == inv_mask) { - goto bad; - } - for (ex_ptr = slirp->exec_list; ex_ptr; ex_ptr = ex_ptr->ex_next) - if (ex_ptr->ex_addr.s_addr == ip->ip_dst.s_addr) - break; - - if (!ex_ptr) - goto bad; - } - } - /* Should drop packet if mbuf too long? hmmm... */ if (m->m_len > ip->ip_len) m_adj(m, ip->ip_len - m->m_len); diff --git a/slirp/udp.c b/slirp/udp.c index 02b3793e9f..f1a9a10948 100644 --- a/slirp/udp.c +++ b/slirp/udp.c @@ -125,10 +125,6 @@ udp_input(register struct mbuf *m, int iphlen) goto bad; } - if (slirp->restricted) { - goto bad; - } - /* * handle TFTP */ @@ -137,6 +133,10 @@ udp_input(register struct mbuf *m, int iphlen) goto bad; } + if (slirp->restricted) { + goto bad; + } + /* * Locate pcb for datagram. */ From c54ed5bcdd8ed29f9cdfcfc0e456b6ec1f25d2c3 Mon Sep 17 00:00:00 2001 From: Jan Kiszka Date: Wed, 20 Jul 2011 12:20:14 +0200 Subject: [PATCH 020/230] slirp: Canonicalize restrict syntax All other boolean arguments accept on|off - except for slirp's restrict. Fix that while still accepting the formerly allowed yes|y|no|n, but reject everything else. This avoids accidentally allowing external connections because syntax errors were so far interpreted as 'restrict=no'. CC: Gleb Natapov Signed-off-by: Jan Kiszka Signed-off-by: Anthony Liguori --- net/slirp.c | 21 +++++++++++++++------ qemu-options.hx | 4 ++-- 2 files changed, 17 insertions(+), 8 deletions(-) diff --git a/net/slirp.c b/net/slirp.c index e057a14ce9..71e2577b6f 100644 --- a/net/slirp.c +++ b/net/slirp.c @@ -240,7 +240,8 @@ static int net_slirp_init(VLANState *vlan, const char *model, nc = qemu_new_net_client(&net_slirp_info, vlan, NULL, model, name); snprintf(nc->info_str, sizeof(nc->info_str), - "net=%s, restricted=%c", inet_ntoa(net), restricted ? 'y' : 'n'); + "net=%s,restrict=%s", inet_ntoa(net), + restricted ? "on" : "off"); s = DO_UPCAST(SlirpState, nc, nc); @@ -689,6 +690,7 @@ int net_init_slirp(QemuOpts *opts, const char *bootfile; const char *smb_export; const char *vsmbsrv; + const char *restrict_opt; char *vnet = NULL; int restricted = 0; int ret; @@ -702,6 +704,18 @@ int net_init_slirp(QemuOpts *opts, smb_export = qemu_opt_get(opts, "smb"); vsmbsrv = qemu_opt_get(opts, "smbserver"); + restrict_opt = qemu_opt_get(opts, "restrict"); + if (restrict_opt) { + if (!strcmp(restrict_opt, "on") || + !strcmp(restrict_opt, "yes") || !strcmp(restrict_opt, "y")) { + restricted = 1; + } else if (strcmp(restrict_opt, "off") && + strcmp(restrict_opt, "no") && strcmp(restrict_opt, "n")) { + error_report("invalid option: 'restrict=%s'", restrict_opt); + return -1; + } + } + if (qemu_opt_get(opts, "ip")) { const char *ip = qemu_opt_get(opts, "ip"); int l = strlen(ip) + strlen("/24") + 1; @@ -720,11 +734,6 @@ int net_init_slirp(QemuOpts *opts, vnet = qemu_strdup(qemu_opt_get(opts, "net")); } - if (qemu_opt_get(opts, "restrict") && - qemu_opt_get(opts, "restrict")[0] == 'y') { - restricted = 1; - } - qemu_opt_foreach(opts, net_init_slirp_configs, NULL, 0); ret = net_slirp_init(vlan, "user", name, restricted, vnet, vhost, diff --git a/qemu-options.hx b/qemu-options.hx index 1233f834df..1d57f64888 100644 --- a/qemu-options.hx +++ b/qemu-options.hx @@ -1115,7 +1115,7 @@ DEF("net", HAS_ARG, QEMU_OPTION_net, "-net nic[,vlan=n][,macaddr=mac][,model=type][,name=str][,addr=str][,vectors=v]\n" " create a new Network Interface Card and connect it to VLAN 'n'\n" #ifdef CONFIG_SLIRP - "-net user[,vlan=n][,name=str][,net=addr[/mask]][,host=addr][,restrict=y|n]\n" + "-net user[,vlan=n][,name=str][,net=addr[/mask]][,host=addr][,restrict=on|off]\n" " [,hostname=host][,dhcpstart=addr][,dns=addr][,tftp=dir][,bootfile=f]\n" " [,hostfwd=rule][,guestfwd=rule]" #ifndef _WIN32 @@ -1208,7 +1208,7 @@ either in the form a.b.c.d or as number of valid top-most bits. Default is Specify the guest-visible address of the host. Default is the 2nd IP in the guest network, i.e. x.x.x.2. -@item restrict=y|yes|n|no +@item restrict=on|off If this option is enabled, the guest will be isolated, i.e. it will not be able to contact the host and no guest IP packets will be routed over the host to the outside. This option does not affect any explicitly set forwarding rules. From 5a82362ad0bf06bba3377d63ca0ecd05fb74f322 Mon Sep 17 00:00:00 2001 From: Jan Kiszka Date: Wed, 20 Jul 2011 12:20:15 +0200 Subject: [PATCH 021/230] slirp: Strictly associate DHCP/BOOTP and TFTP with virtual host Instead of accepting every DHCP/BOOTP and TFTP packet, only invoke the built-in servers if the target is the virtual host. Signed-off-by: Jan Kiszka Signed-off-by: Anthony Liguori --- slirp/udp.c | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-) diff --git a/slirp/udp.c b/slirp/udp.c index f1a9a10948..cefd50b792 100644 --- a/slirp/udp.c +++ b/slirp/udp.c @@ -120,15 +120,18 @@ udp_input(register struct mbuf *m, int iphlen) /* * handle DHCP/BOOTP */ - if (ntohs(uh->uh_dport) == BOOTP_SERVER) { - bootp_input(m); - goto bad; - } + if (ntohs(uh->uh_dport) == BOOTP_SERVER && + (ip->ip_dst.s_addr == slirp->vhost_addr.s_addr || + ip->ip_dst.s_addr == 0xffffffff)) { + bootp_input(m); + goto bad; + } /* * handle TFTP */ - if (ntohs(uh->uh_dport) == TFTP_SERVER) { + if (ntohs(uh->uh_dport) == TFTP_SERVER && + ip->ip_dst.s_addr == slirp->vhost_addr.s_addr) { tftp_input(m); goto bad; } From 3acccfc67d3aa4611142e2171337c7c494b52efb Mon Sep 17 00:00:00 2001 From: Jan Kiszka Date: Wed, 20 Jul 2011 12:20:16 +0200 Subject: [PATCH 022/230] slirp: Replace m_freem with m_free Remove this pointless wrapping. Signed-off-by: Jan Kiszka Signed-off-by: Anthony Liguori --- slirp/ip_icmp.c | 6 +++--- slirp/ip_input.c | 8 ++++---- slirp/ip_output.c | 4 ++-- slirp/mbuf.h | 3 --- slirp/tcp_input.c | 10 +++++----- slirp/tcp_subr.c | 2 +- slirp/udp.c | 2 +- 7 files changed, 16 insertions(+), 19 deletions(-) diff --git a/slirp/ip_icmp.c b/slirp/ip_icmp.c index 0cd129cc97..4f10826b3f 100644 --- a/slirp/ip_icmp.c +++ b/slirp/ip_icmp.c @@ -81,7 +81,7 @@ icmp_input(struct mbuf *m, int hlen) */ if (icmplen < ICMP_MINLEN) { /* min 8 bytes payload */ freeit: - m_freem(m); + m_free(m); goto end_error; } @@ -155,11 +155,11 @@ icmp_input(struct mbuf *m, int hlen) case ICMP_TSTAMP: case ICMP_MASKREQ: case ICMP_REDIRECT: - m_freem(m); + m_free(m); break; default: - m_freem(m); + m_free(m); } /* swith */ end_error: diff --git a/slirp/ip_input.c b/slirp/ip_input.c index 2ff6adbc4c..46c60b0f82 100644 --- a/slirp/ip_input.c +++ b/slirp/ip_input.c @@ -204,7 +204,7 @@ ip_input(struct mbuf *m) } return; bad: - m_freem(m); + m_free(m); return; } @@ -297,7 +297,7 @@ ip_reass(Slirp *slirp, struct ip *ip, struct ipq *fp) break; } q = q->ipf_next; - m_freem(dtom(slirp, q->ipf_prev)); + m_free(dtom(slirp, q->ipf_prev)); ip_deq(q->ipf_prev); } @@ -363,7 +363,7 @@ insert: return ip; dropfrag: - m_freem(m); + m_free(m); return NULL; } @@ -379,7 +379,7 @@ ip_freef(Slirp *slirp, struct ipq *fp) for (q = fp->frag_link.next; q != (struct ipasfrag*)&fp->frag_link; q = p) { p = q->ipf_next; ip_deq(q); - m_freem(dtom(slirp, q)); + m_free(dtom(slirp, q)); } remque(&fp->ip_link); (void) m_free(dtom(slirp, fp)); diff --git a/slirp/ip_output.c b/slirp/ip_output.c index 542f3180be..c82830fe7d 100644 --- a/slirp/ip_output.c +++ b/slirp/ip_output.c @@ -159,7 +159,7 @@ sendorfree: if (error == 0) if_output(so, m); else - m_freem(m); + m_free(m); } } @@ -167,6 +167,6 @@ done: return (error); bad: - m_freem(m0); + m_free(m0); goto done; } diff --git a/slirp/mbuf.h b/slirp/mbuf.h index 97729e24b0..b74544b42b 100644 --- a/slirp/mbuf.h +++ b/slirp/mbuf.h @@ -33,9 +33,6 @@ #ifndef _MBUF_H_ #define _MBUF_H_ -#define m_freem m_free - - #define MINCSIZE 4096 /* Amount to increase mbuf if too small */ /* diff --git a/slirp/tcp_input.c b/slirp/tcp_input.c index e4a77310d0..c1214c0659 100644 --- a/slirp/tcp_input.c +++ b/slirp/tcp_input.c @@ -136,7 +136,7 @@ tcp_reass(register struct tcpcb *tp, register struct tcpiphdr *ti, i = q->ti_seq + q->ti_len - ti->ti_seq; if (i > 0) { if (i >= ti->ti_len) { - m_freem(m); + m_free(m); /* * Try to present any queued data * at the left window edge to the user. @@ -170,7 +170,7 @@ tcp_reass(register struct tcpcb *tp, register struct tcpiphdr *ti, q = tcpiphdr_next(q); m = tcpiphdr_prev(q)->ti_mbuf; remque(tcpiphdr2qlink(tcpiphdr_prev(q))); - m_freem(m); + m_free(m); } /* @@ -197,7 +197,7 @@ present: m = ti->ti_mbuf; ti = tcpiphdr_next(ti); if (so->so_state & SS_FCANTSENDMORE) - m_freem(m); + m_free(m); else { if (so->so_emu) { if (tcp_emu(so,m)) sbappend(so, m); @@ -451,7 +451,7 @@ findso: acked = ti->ti_ack - tp->snd_una; sbdrop(&so->so_snd, acked); tp->snd_una = ti->ti_ack; - m_freem(m); + m_free(m); /* * If all outstanding data are acked, stop @@ -1260,7 +1260,7 @@ dropafterack: */ if (tiflags & TH_RST) goto drop; - m_freem(m); + m_free(m); tp->t_flags |= TF_ACKNOW; (void) tcp_output(tp); return; diff --git a/slirp/tcp_subr.c b/slirp/tcp_subr.c index b661d2623c..61079b1b2d 100644 --- a/slirp/tcp_subr.c +++ b/slirp/tcp_subr.c @@ -250,7 +250,7 @@ tcp_close(struct tcpcb *tp) t = tcpiphdr_next(t); m = tcpiphdr_prev(t)->ti_mbuf; remque(tcpiphdr2qlink(tcpiphdr_prev(t))); - m_freem(m); + m_free(m); } free(tp); so->so_tcpcb = NULL; diff --git a/slirp/udp.c b/slirp/udp.c index cefd50b792..5b060f397b 100644 --- a/slirp/udp.c +++ b/slirp/udp.c @@ -222,7 +222,7 @@ udp_input(register struct mbuf *m, int iphlen) return; bad: - m_freem(m); + m_free(m); return; } From 565465fcae755fbdb11c2f65ec5f0ae62c78db3a Mon Sep 17 00:00:00 2001 From: Jan Kiszka Date: Wed, 20 Jul 2011 12:20:17 +0200 Subject: [PATCH 023/230] slirp: Put forked exec into separate process group Recent smb daemons tend to terminate themselves via a process group SIGTERM. If the daemon is still in qemu's group by that time, qemu will die as well. Avoid this by always pushing fork_exec processes into a group of their own, not just (unused) type 2 execs. Signed-off-by: Jan Kiszka Signed-off-by: Anthony Liguori --- slirp/misc.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/slirp/misc.c b/slirp/misc.c index 08eba6adc0..34179e26a8 100644 --- a/slirp/misc.c +++ b/slirp/misc.c @@ -153,11 +153,12 @@ fork_exec(struct socket *so, const char *ex, int do_pty) return 0; case 0: + setsid(); + /* Set the DISPLAY */ if (do_pty == 2) { (void) close(master); #ifdef TIOCSCTTY /* XXXXX */ - (void) setsid(); ioctl(s, TIOCSCTTY, (char *)NULL); #endif } else { From e6d43cfb1f937898dc031c7b38a23e5ccad8bd9a Mon Sep 17 00:00:00 2001 From: Jan Kiszka Date: Wed, 20 Jul 2011 12:20:18 +0200 Subject: [PATCH 024/230] slirp: Forward ICMP echo requests via unprivileged sockets Linux 3.0 gained support for unprivileged ICMP ping sockets. Use this feature to forward guest pings to the outer world. The host admin has to set the ping_group_range in order to grant access to those sockets. To allow ping for the users group (GID 100): echo 100 100 > /proc/sys/net/ipv4/ping_group_range Signed-off-by: Jan Kiszka Signed-off-by: Anthony Liguori --- slirp/ip_icmp.c | 87 +++++++++++++++++++++++++++++++++++++++++++++++- slirp/ip_icmp.h | 3 ++ slirp/ip_input.c | 1 + slirp/misc.c | 13 ++++++++ slirp/slirp.c | 37 ++++++++++++++++++++ slirp/slirp.h | 5 +++ slirp/socket.c | 2 ++ 7 files changed, 147 insertions(+), 1 deletion(-) diff --git a/slirp/ip_icmp.c b/slirp/ip_icmp.c index 4f10826b3f..14a5312c84 100644 --- a/slirp/ip_icmp.c +++ b/slirp/ip_icmp.c @@ -60,6 +60,52 @@ static const int icmp_flush[19] = { /* ADDR MASK REPLY (18) */ 0 }; +void icmp_init(Slirp *slirp) +{ + slirp->icmp.so_next = slirp->icmp.so_prev = &slirp->icmp; + slirp->icmp_last_so = &slirp->icmp; +} + +static int icmp_send(struct socket *so, struct mbuf *m, int hlen) +{ + struct ip *ip = mtod(m, struct ip *); + struct sockaddr_in addr; + + so->s = qemu_socket(AF_INET, SOCK_DGRAM, IPPROTO_ICMP); + if (so->s == -1) { + return -1; + } + + so->so_m = m; + so->so_faddr = ip->ip_dst; + so->so_laddr = ip->ip_src; + so->so_iptos = ip->ip_tos; + so->so_type = IPPROTO_ICMP; + so->so_state = SS_ISFCONNECTED; + so->so_expire = curtime + SO_EXPIRE; + + addr.sin_family = AF_INET; + addr.sin_addr = so->so_faddr; + + insque(so, &so->slirp->icmp); + + if (sendto(so->s, m->m_data + hlen, m->m_len - hlen, 0, + (struct sockaddr *)&addr, sizeof(addr)) == -1) { + DEBUG_MISC((dfd, "icmp_input icmp sendto tx errno = %d-%s\n", + errno, strerror(errno))); + icmp_error(m, ICMP_UNREACH, ICMP_UNREACH_NET, 0, strerror(errno)); + icmp_detach(so); + } + + return 0; +} + +void icmp_detach(struct socket *so) +{ + closesocket(so->s); + sofree(so); +} + /* * Process a received ICMP message. */ @@ -97,7 +143,6 @@ icmp_input(struct mbuf *m, int hlen) DEBUG_ARG("icmp_type = %d", icp->icmp_type); switch (icp->icmp_type) { case ICMP_ECHO: - icp->icmp_type = ICMP_ECHOREPLY; ip->ip_len += hlen; /* since ip_input subtracts this */ if (ip->ip_dst.s_addr == slirp->vhost_addr.s_addr) { icmp_reflect(m); @@ -107,6 +152,9 @@ icmp_input(struct mbuf *m, int hlen) struct socket *so; struct sockaddr_in addr; if ((so = socreate(slirp)) == NULL) goto freeit; + if (icmp_send(so, m, hlen) == 0) { + return; + } if(udp_attach(so) == -1) { DEBUG_MISC((dfd,"icmp_input udp_attach errno = %d-%s\n", errno,strerror(errno))); @@ -321,6 +369,7 @@ icmp_reflect(struct mbuf *m) m->m_len -= hlen; icp = mtod(m, struct icmp *); + icp->icmp_type = ICMP_ECHOREPLY; icp->icmp_cksum = 0; icp->icmp_cksum = cksum(m, ip->ip_len - hlen); @@ -351,3 +400,39 @@ icmp_reflect(struct mbuf *m) (void ) ip_output((struct socket *)NULL, m); } + +void icmp_receive(struct socket *so) +{ + struct mbuf *m = so->so_m; + struct ip *ip = mtod(m, struct ip *); + int hlen = ip->ip_hl << 2; + u_char error_code; + struct icmp *icp; + int id, len; + + m->m_data += hlen; + m->m_len -= hlen; + icp = mtod(m, struct icmp *); + + id = icp->icmp_id; + len = recv(so->s, icp, m->m_len, 0); + icp->icmp_id = id; + + m->m_data -= hlen; + m->m_len += hlen; + + if (len == -1 || len == 0) { + if (errno == ENETUNREACH) { + error_code = ICMP_UNREACH_NET; + } else { + error_code = ICMP_UNREACH_HOST; + } + DEBUG_MISC((dfd, " udp icmp rx errno = %d-%s\n", errno, + strerror(errno))); + icmp_error(so->so_m, ICMP_UNREACH, error_code, 0, strerror(errno)); + } else { + icmp_reflect(so->so_m); + so->so_m = NULL; /* Don't m_free() it again! */ + } + icmp_detach(so); +} diff --git a/slirp/ip_icmp.h b/slirp/ip_icmp.h index 2692822f87..b3da1f2697 100644 --- a/slirp/ip_icmp.h +++ b/slirp/ip_icmp.h @@ -153,9 +153,12 @@ struct icmp { (type) == ICMP_IREQ || (type) == ICMP_IREQREPLY || \ (type) == ICMP_MASKREQ || (type) == ICMP_MASKREPLY) +void icmp_init(Slirp *slirp); void icmp_input(struct mbuf *, int); void icmp_error(struct mbuf *msrc, u_char type, u_char code, int minsize, const char *message); void icmp_reflect(struct mbuf *); +void icmp_receive(struct socket *so); +void icmp_detach(struct socket *so); #endif diff --git a/slirp/ip_input.c b/slirp/ip_input.c index 46c60b0f82..5e67631ab4 100644 --- a/slirp/ip_input.c +++ b/slirp/ip_input.c @@ -58,6 +58,7 @@ ip_init(Slirp *slirp) slirp->ipq.ip_link.next = slirp->ipq.ip_link.prev = &slirp->ipq.ip_link; udp_init(slirp); tcp_init(slirp); + icmp_init(slirp); } /* diff --git a/slirp/misc.c b/slirp/misc.c index 34179e26a8..6002550361 100644 --- a/slirp/misc.c +++ b/slirp/misc.c @@ -407,4 +407,17 @@ void slirp_connection_info(Slirp *slirp, Monitor *mon) inet_ntoa(dst_addr), ntohs(dst_port), so->so_rcv.sb_cc, so->so_snd.sb_cc); } + + for (so = slirp->icmp.so_next; so != &slirp->icmp; so = so->so_next) { + n = snprintf(buf, sizeof(buf), " ICMP[%d sec]", + (so->so_expire - curtime) / 1000); + src.sin_addr = so->so_laddr; + dst_addr = so->so_faddr; + memset(&buf[n], ' ', 19 - n); + buf[19] = 0; + monitor_printf(mon, "%s %3d %15s - ", buf, so->s, + src.sin_addr.s_addr ? inet_ntoa(src.sin_addr) : "*"); + monitor_printf(mon, "%15s - %5d %5d\n", inet_ntoa(dst_addr), + so->so_rcv.sb_cc, so->so_snd.sb_cc); + } } diff --git a/slirp/slirp.c b/slirp/slirp.c index 1593be177e..faaa2f36c7 100644 --- a/slirp/slirp.c +++ b/slirp/slirp.c @@ -373,6 +373,31 @@ void slirp_select_fill(int *pnfds, UPD_NFDS(so->s); } } + + /* + * ICMP sockets + */ + for (so = slirp->icmp.so_next; so != &slirp->icmp; + so = so_next) { + so_next = so->so_next; + + /* + * See if it's timed out + */ + if (so->so_expire) { + if (so->so_expire <= curtime) { + icmp_detach(so); + continue; + } else { + do_slowtimo = 1; /* Let socket expire */ + } + } + + if (so->so_state & SS_ISFCONNECTED) { + FD_SET(so->s, readfds); + UPD_NFDS(so->s); + } + } } *pnfds = nfds; @@ -542,6 +567,18 @@ void slirp_select_poll(fd_set *readfds, fd_set *writefds, fd_set *xfds, sorecvfrom(so); } } + + /* + * Check incoming ICMP relies. + */ + for (so = slirp->icmp.so_next; so != &slirp->icmp; + so = so_next) { + so_next = so->so_next; + + if (so->s != -1 && FD_ISSET(so->s, readfds)) { + icmp_receive(so); + } + } } /* diff --git a/slirp/slirp.h b/slirp/slirp.h index 954289a8c8..16bb6bae45 100644 --- a/slirp/slirp.h +++ b/slirp/slirp.h @@ -152,6 +152,7 @@ int inet_aton(const char *cp, struct in_addr *ia); #include "tcp_var.h" #include "tcpip.h" #include "udp.h" +#include "ip_icmp.h" #include "mbuf.h" #include "sbuf.h" #include "socket.h" @@ -218,6 +219,10 @@ struct Slirp { struct socket udb; struct socket *udp_last_so; + /* icmp states */ + struct socket icmp; + struct socket *icmp_last_so; + /* tftp states */ char *tftp_prefix; struct tftp_session tftp_sessions[TFTP_SESSIONS_MAX]; diff --git a/slirp/socket.c b/slirp/socket.c index 611923424c..9b8ae13f5e 100644 --- a/slirp/socket.c +++ b/slirp/socket.c @@ -71,6 +71,8 @@ sofree(struct socket *so) slirp->tcp_last_so = &slirp->tcb; } else if (so == slirp->udp_last_so) { slirp->udp_last_so = &slirp->udb; + } else if (so == slirp->icmp_last_so) { + slirp->icmp_last_so = &slirp->icmp; } m_free(so->so_m); From 19061e63c04810cb24769e9d92d943079206297a Mon Sep 17 00:00:00 2001 From: Jan Kiszka Date: Wed, 20 Jul 2011 12:20:19 +0200 Subject: [PATCH 025/230] net: Improve layout of 'info network' Improve the layout when listing non-vlan clients via 'info network'. The result looks like this: (qemu) info network Devices not on any VLAN: orphan: net=10.0.2.0, restricted=n virtio-net-pci.0: model=virtio-net-pci,macaddr=52:54:00:12:34:56 \ network2: fd=5 e1000.0: model=e1000,macaddr=52:54:00:12:34:57 \ network1: net=10.0.2.0, restricted=n rtl8139.0: model=rtl8139,macaddr=52:54:00:12:34:58 ie. peers are grouped, orphans are listed as before. CC: Markus Armbruster Signed-off-by: Jan Kiszka Signed-off-by: Anthony Liguori --- net.c | 14 +++++++++----- 1 file changed, 9 insertions(+), 5 deletions(-) diff --git a/net.c b/net.c index 66123ad409..43627ad8c8 100644 --- a/net.c +++ b/net.c @@ -1224,7 +1224,8 @@ int do_netdev_del(Monitor *mon, const QDict *qdict, QObject **ret_data) void do_info_network(Monitor *mon) { VLANState *vlan; - VLANClientState *vc; + VLANClientState *vc, *peer; + net_client_type type; QTAILQ_FOREACH(vlan, &vlans, next) { monitor_printf(mon, "VLAN %d devices:\n", vlan->id); @@ -1235,11 +1236,14 @@ void do_info_network(Monitor *mon) } monitor_printf(mon, "Devices not on any VLAN:\n"); QTAILQ_FOREACH(vc, &non_vlan_clients, next) { - monitor_printf(mon, " %s: %s", vc->name, vc->info_str); - if (vc->peer) { - monitor_printf(mon, " peer=%s", vc->peer->name); + peer = vc->peer; + type = vc->info->type; + if (!peer || type == NET_CLIENT_TYPE_NIC) { + monitor_printf(mon, " %s: %s\n", vc->name, vc->info_str); + } /* else it's a netdev connected to a NIC, printed with the NIC */ + if (peer && type == NET_CLIENT_TYPE_NIC) { + monitor_printf(mon, " \\ %s: %s\n", peer->name, peer->info_str); } - monitor_printf(mon, "\n"); } } From 6f7b3b1be229e3a686a4507d6c11dfcf5f12cef4 Mon Sep 17 00:00:00 2001 From: Jan Kiszka Date: Wed, 20 Jul 2011 12:20:20 +0200 Subject: [PATCH 026/230] net: Refactor net_client_types Position entries of net_client_types according to the corresponding values of NET_CLIENT_TYPE_*. The array size is now defined by NET_CLIENT_TYPE_MAX. This will allow to obtain entries based on type value in later patches. At this chance rename NET_CLIENT_TYPE_SLIRP to NET_CLIENT_TYPE_USER for the sake of consistency. CC: Markus Armbruster Signed-off-by: Jan Kiszka Signed-off-by: Anthony Liguori --- net.c | 30 ++++++++++++++++++------------ net.h | 6 ++++-- net/slirp.c | 2 +- 3 files changed, 23 insertions(+), 15 deletions(-) diff --git a/net.c b/net.c index 43627ad8c8..05fc685fec 100644 --- a/net.c +++ b/net.c @@ -830,14 +830,15 @@ static const struct { const char *type; net_client_init_func init; QemuOptDesc desc[NET_MAX_DESC]; -} net_client_types[] = { - { +} net_client_types[NET_CLIENT_TYPE_MAX] = { + [NET_CLIENT_TYPE_NONE] = { .type = "none", .desc = { NET_COMMON_PARAMS_DESC, { /* end of list */ } }, - }, { + }, + [NET_CLIENT_TYPE_NIC] = { .type = "nic", .init = net_init_nic, .desc = { @@ -866,8 +867,9 @@ static const struct { }, { /* end of list */ } }, + }, #ifdef CONFIG_SLIRP - }, { + [NET_CLIENT_TYPE_USER] = { .type = "user", .init = net_init_slirp, .desc = { @@ -927,8 +929,9 @@ static const struct { }, { /* end of list */ } }, + }, #endif - }, { + [NET_CLIENT_TYPE_TAP] = { .type = "tap", .init = net_init_tap, .desc = { @@ -975,7 +978,8 @@ static const struct { #endif /* _WIN32 */ { /* end of list */ } }, - }, { + }, + [NET_CLIENT_TYPE_SOCKET] = { .type = "socket", .init = net_init_socket, .desc = { @@ -1003,8 +1007,9 @@ static const struct { }, { /* end of list */ } }, + }, #ifdef CONFIG_VDE - }, { + [NET_CLIENT_TYPE_VDE] = { .type = "vde", .init = net_init_vde, .desc = { @@ -1028,8 +1033,9 @@ static const struct { }, { /* end of list */ } }, + }, #endif - }, { + [NET_CLIENT_TYPE_DUMP] = { .type = "dump", .init = net_init_dump, .desc = { @@ -1046,7 +1052,6 @@ static const struct { { /* end of list */ } }, }, - { /* end of list */ } }; int net_client_init(Monitor *mon, QemuOpts *opts, int is_netdev) @@ -1094,8 +1099,9 @@ int net_client_init(Monitor *mon, QemuOpts *opts, int is_netdev) name = qemu_opt_get(opts, "name"); } - for (i = 0; net_client_types[i].type != NULL; i++) { - if (!strcmp(net_client_types[i].type, type)) { + for (i = 0; i < NET_CLIENT_TYPE_MAX; i++) { + if (net_client_types[i].type != NULL && + !strcmp(net_client_types[i].type, type)) { VLANState *vlan = NULL; int ret; @@ -1330,7 +1336,7 @@ void net_check_clients(void) case NET_CLIENT_TYPE_NIC: has_nic = 1; break; - case NET_CLIENT_TYPE_SLIRP: + case NET_CLIENT_TYPE_USER: case NET_CLIENT_TYPE_TAP: case NET_CLIENT_TYPE_SOCKET: case NET_CLIENT_TYPE_VDE: diff --git a/net.h b/net.h index 5b883a96ba..4fdd9420bc 100644 --- a/net.h +++ b/net.h @@ -31,11 +31,13 @@ typedef struct NICConf { typedef enum { NET_CLIENT_TYPE_NONE, NET_CLIENT_TYPE_NIC, - NET_CLIENT_TYPE_SLIRP, + NET_CLIENT_TYPE_USER, NET_CLIENT_TYPE_TAP, NET_CLIENT_TYPE_SOCKET, NET_CLIENT_TYPE_VDE, - NET_CLIENT_TYPE_DUMP + NET_CLIENT_TYPE_DUMP, + + NET_CLIENT_TYPE_MAX } net_client_type; typedef void (NetPoll)(VLANClientState *, bool enable); diff --git a/net/slirp.c b/net/slirp.c index 71e2577b6f..157b80a9f6 100644 --- a/net/slirp.c +++ b/net/slirp.c @@ -128,7 +128,7 @@ static void net_slirp_cleanup(VLANClientState *nc) } static NetClientInfo net_slirp_info = { - .type = NET_CLIENT_TYPE_SLIRP, + .type = NET_CLIENT_TYPE_USER, .size = sizeof(SlirpState), .receive = net_slirp_receive, .cleanup = net_slirp_cleanup, From 44e798d3959e6face1adc0b2fd1873b25f66a5e7 Mon Sep 17 00:00:00 2001 From: Jan Kiszka Date: Wed, 20 Jul 2011 12:20:21 +0200 Subject: [PATCH 027/230] net: Dump client type 'info network' Include the client type name into the output of 'info network'. The result looks like this: (qemu) info network VLAN 0 devices: rtl8139.0: type=nic,model=rtl8139,macaddr=52:54:00:12:34:57 Devices not on any VLAN: virtio-net-pci.0: type=nic,model=virtio-net-pci,macaddr=52:54:00:12:34:56 \ network1: type=tap,fd=5 CC: Markus Armbruster Signed-off-by: Jan Kiszka Signed-off-by: Anthony Liguori --- net.c | 15 ++++++++++++--- 1 file changed, 12 insertions(+), 3 deletions(-) diff --git a/net.c b/net.c index 05fc685fec..12701af253 100644 --- a/net.c +++ b/net.c @@ -1227,6 +1227,12 @@ int do_netdev_del(Monitor *mon, const QDict *qdict, QObject **ret_data) return 0; } +static void print_net_client(Monitor *mon, VLANClientState *vc) +{ + monitor_printf(mon, "%s: type=%s,%s\n", vc->name, + net_client_types[vc->info->type].type, vc->info_str); +} + void do_info_network(Monitor *mon) { VLANState *vlan; @@ -1237,7 +1243,8 @@ void do_info_network(Monitor *mon) monitor_printf(mon, "VLAN %d devices:\n", vlan->id); QTAILQ_FOREACH(vc, &vlan->clients, next) { - monitor_printf(mon, " %s: %s\n", vc->name, vc->info_str); + monitor_printf(mon, " "); + print_net_client(mon, vc); } } monitor_printf(mon, "Devices not on any VLAN:\n"); @@ -1245,10 +1252,12 @@ void do_info_network(Monitor *mon) peer = vc->peer; type = vc->info->type; if (!peer || type == NET_CLIENT_TYPE_NIC) { - monitor_printf(mon, " %s: %s\n", vc->name, vc->info_str); + monitor_printf(mon, " "); + print_net_client(mon, vc); } /* else it's a netdev connected to a NIC, printed with the NIC */ if (peer && type == NET_CLIENT_TYPE_NIC) { - monitor_printf(mon, " \\ %s: %s\n", peer->name, peer->info_str); + monitor_printf(mon, " \\ "); + print_net_client(mon, peer); } } } From 6eed18568d985f5e091e96205f5ebf50fb823f4e Mon Sep 17 00:00:00 2001 From: Jan Kiszka Date: Wed, 20 Jul 2011 12:20:22 +0200 Subject: [PATCH 028/230] net: Consistently use qemu_macaddr_default_if_unset Drop the open-coded MAC assignment from net_init_nic and replace it with standard qemu_macaddr_default_if_unset which is also used by qdev. That avoid creating colliding MACs when instantiating NICs via different mechanisms. This change requires to store the MAC as MACAddr in NICInfo, and the remaining nd_table users need to be updated. Based on suggestion by Peter Maydell. CC: Markus Armbruster CC: Peter Maydell Signed-off-by: Jan Kiszka Signed-off-by: Anthony Liguori --- hw/dp8393x.c | 2 +- hw/etraxfs_eth.c | 2 +- hw/mcf_fec.c | 2 +- hw/mipsnet.c | 2 +- hw/qdev.c | 2 +- hw/stellaris.c | 2 +- hw/xen_devconfig.c | 4 ++-- net.c | 10 ++-------- net.h | 2 +- 9 files changed, 11 insertions(+), 17 deletions(-) diff --git a/hw/dp8393x.c b/hw/dp8393x.c index c332dd59d2..1bcd8eeba9 100644 --- a/hw/dp8393x.c +++ b/hw/dp8393x.c @@ -898,7 +898,7 @@ void dp83932_init(NICInfo *nd, target_phys_addr_t base, int it_shift, s->watchdog = qemu_new_timer_ns(vm_clock, dp8393x_watchdog, s); s->regs[SONIC_SR] = 0x0004; /* only revision recognized by Linux */ - memcpy(s->conf.macaddr.a, nd->macaddr, sizeof(s->conf.macaddr)); + s->conf.macaddr = nd->macaddr; s->conf.vlan = nd->vlan; s->conf.peer = nd->netdev; diff --git a/hw/etraxfs_eth.c b/hw/etraxfs_eth.c index 6aa4007203..dff5f55f33 100644 --- a/hw/etraxfs_eth.c +++ b/hw/etraxfs_eth.c @@ -602,7 +602,7 @@ void *etraxfs_eth_init(NICInfo *nd, target_phys_addr_t base, int phyaddr) DEVICE_NATIVE_ENDIAN); cpu_register_physical_memory (base, 0x5c, eth->ethregs); - memcpy(eth->conf.macaddr.a, nd->macaddr, sizeof(nd->macaddr)); + eth->conf.macaddr = nd->macaddr; eth->conf.vlan = nd->vlan; eth->conf.peer = nd->netdev; diff --git a/hw/mcf_fec.c b/hw/mcf_fec.c index 21035da345..5477e0e159 100644 --- a/hw/mcf_fec.c +++ b/hw/mcf_fec.c @@ -471,7 +471,7 @@ void mcf_fec_init(NICInfo *nd, target_phys_addr_t base, qemu_irq *irq) DEVICE_NATIVE_ENDIAN); cpu_register_physical_memory(base, 0x400, s->mmio_index); - memcpy(s->conf.macaddr.a, nd->macaddr, sizeof(nd->macaddr)); + s->conf.macaddr = nd->macaddr; s->conf.vlan = nd->vlan; s->conf.peer = nd->netdev; diff --git a/hw/mipsnet.c b/hw/mipsnet.c index 26aad51eab..0db3ba7a89 100644 --- a/hw/mipsnet.c +++ b/hw/mipsnet.c @@ -258,7 +258,7 @@ void mipsnet_init (int base, qemu_irq irq, NICInfo *nd) s->irq = irq; if (nd) { - memcpy(s->conf.macaddr.a, nd->macaddr, sizeof(nd->macaddr)); + s->conf.macaddr = nd->macaddr; s->conf.vlan = nd->vlan; s->conf.peer = nd->netdev; diff --git a/hw/qdev.c b/hw/qdev.c index 292b52f8c5..a0fcd06094 100644 --- a/hw/qdev.c +++ b/hw/qdev.c @@ -459,7 +459,7 @@ void qdev_connect_gpio_out(DeviceState * dev, int n, qemu_irq pin) void qdev_set_nic_properties(DeviceState *dev, NICInfo *nd) { - qdev_prop_set_macaddr(dev, "mac", nd->macaddr); + qdev_prop_set_macaddr(dev, "mac", nd->macaddr.a); if (nd->vlan) qdev_prop_set_vlan(dev, "vlan", nd->vlan); if (nd->netdev) diff --git a/hw/stellaris.c b/hw/stellaris.c index ac9fcc1f38..b8a7cebd8c 100644 --- a/hw/stellaris.c +++ b/hw/stellaris.c @@ -1230,7 +1230,7 @@ static void stellaris_init(const char *kernel_filename, const char *cpu_model, } } - stellaris_sys_init(0x400fe000, pic[28], board, nd_table[0].macaddr); + stellaris_sys_init(0x400fe000, pic[28], board, nd_table[0].macaddr.a); for (i = 0; i < 7; i++) { if (board->dc4 & (1 << i)) { diff --git a/hw/xen_devconfig.c b/hw/xen_devconfig.c index 3a9215566d..6926c54f4f 100644 --- a/hw/xen_devconfig.c +++ b/hw/xen_devconfig.c @@ -126,8 +126,8 @@ int xen_config_dev_nic(NICInfo *nic) char mac[20]; snprintf(mac, sizeof(mac), "%02x:%02x:%02x:%02x:%02x:%02x", - nic->macaddr[0], nic->macaddr[1], nic->macaddr[2], - nic->macaddr[3], nic->macaddr[4], nic->macaddr[5]); + nic->macaddr.a[0], nic->macaddr.a[1], nic->macaddr.a[2], + nic->macaddr.a[3], nic->macaddr.a[4], nic->macaddr.a[5]); xen_be_printf(NULL, 1, "config nic %d: mac=\"%s\"\n", nic->vlan->id, mac); xen_config_dev_dirs("vif", "qnic", nic->vlan->id, fe, be, sizeof(fe)); diff --git a/net.c b/net.c index 12701af253..31c23389c8 100644 --- a/net.c +++ b/net.c @@ -776,18 +776,12 @@ static int net_init_nic(QemuOpts *opts, nd->devaddr = qemu_strdup(qemu_opt_get(opts, "addr")); } - nd->macaddr[0] = 0x52; - nd->macaddr[1] = 0x54; - nd->macaddr[2] = 0x00; - nd->macaddr[3] = 0x12; - nd->macaddr[4] = 0x34; - nd->macaddr[5] = 0x56 + idx; - if (qemu_opt_get(opts, "macaddr") && - net_parse_macaddr(nd->macaddr, qemu_opt_get(opts, "macaddr")) < 0) { + net_parse_macaddr(nd->macaddr.a, qemu_opt_get(opts, "macaddr")) < 0) { error_report("invalid syntax for ethernet address"); return -1; } + qemu_macaddr_default_if_unset(&nd->macaddr); nd->nvectors = qemu_opt_get_number(opts, "vectors", DEV_NVECTORS_UNSPECIFIED); diff --git a/net.h b/net.h index 4fdd9420bc..5a7881cf67 100644 --- a/net.h +++ b/net.h @@ -129,7 +129,7 @@ int do_set_link(Monitor *mon, const QDict *qdict, QObject **ret_data); #define MAX_NICS 8 struct NICInfo { - uint8_t macaddr[6]; + MACAddr macaddr; char *model; char *name; char *devaddr; From 9af99f1daf8e9bf0fecddcde35273383ec02cd45 Mon Sep 17 00:00:00 2001 From: Michael Roth Date: Fri, 22 Jul 2011 16:42:00 -0500 Subject: [PATCH 029/230] guest agent: use QERR_UNSUPPORTED for disabled RPCs Signed-off-by: Anthony Liguori --- qga/guest-agent-commands.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/qga/guest-agent-commands.c b/qga/guest-agent-commands.c index e215bd3c5f..624972e84c 100644 --- a/qga/guest-agent-commands.c +++ b/qga/guest-agent-commands.c @@ -521,7 +521,7 @@ static void guest_fsfreeze_cleanup(void) */ GuestFsfreezeStatus qmp_guest_fsfreeze_status(Error **err) { - error_set(err, QERR_COMMAND_NOT_FOUND, "guest_fsfreeze_status"); + error_set(err, QERR_UNSUPPORTED); return 0; } @@ -532,7 +532,7 @@ GuestFsfreezeStatus qmp_guest_fsfreeze_status(Error **err) */ int64_t qmp_guest_fsfreeze_freeze(Error **err) { - error_set(err, QERR_COMMAND_NOT_FOUND, "guest_fsfreeze_freeze"); + error_set(err, QERR_UNSUPPORTED); return 0; } @@ -542,7 +542,7 @@ int64_t qmp_guest_fsfreeze_freeze(Error **err) */ int64_t qmp_guest_fsfreeze_thaw(Error **err) { - error_set(err, QERR_COMMAND_NOT_FOUND, "guest_fsfreeze_thaw"); + error_set(err, QERR_UNSUPPORTED); return 0; } From 5bda29da18eb5104718cad9810b625a0105cb0d2 Mon Sep 17 00:00:00 2001 From: Alexandre Raymond Date: Sat, 23 Jul 2011 01:41:57 -0400 Subject: [PATCH 030/230] .gitignore: ignore qemu-ga and qapi-generated Add a new binary and generation directory to the gitignore file Signed-off-by: Alexandre Raymond Signed-off-by: Anthony Liguori --- .gitignore | 2 ++ 1 file changed, 2 insertions(+) diff --git a/.gitignore b/.gitignore index 08013fc57b..54835bcb97 100644 --- a/.gitignore +++ b/.gitignore @@ -15,6 +15,7 @@ libdis* libhw32 libhw64 libuser +qapi-generated qemu-doc.html qemu-tech.html qemu-doc.info @@ -32,6 +33,7 @@ qemu-options.texi qemu-img-cmds.texi qemu-img-cmds.h qemu-io +qemu-ga qemu-monitor.texi QMP/qmp-commands.txt .gdbinit From 6141dbfe0a595076310f690ec8db84ad5be2cde5 Mon Sep 17 00:00:00 2001 From: Paolo Bonzini Date: Fri, 15 Jul 2011 17:10:15 +0200 Subject: [PATCH 031/230] report serial devices created with -device in the PIIX4 config space MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Serial and parallel devices created with -device are not reported in the PIIX4 configuration space, and are hence not picked up by the DSDT. This upsets Windows, which hides them altogether from the guest. To avoid this, check at the end of machine initialization whether the corresponding I/O ports have been registered. The new function in ioport.c does this; this also requires a tweak to isa_unassign_ioport. I left the comment in piix4_pm_initfn since the registers I moved do seem to match the 82371AB datasheet. There are some quirks though. We are setting this bit: "Device 8 EIO Enable (EIO_EN_DEV8)—R/W. 1=Enable PCI access to the device 8 enabled I/O ranges to be claimed by PIIX4 and forwarded to the ISA/EIO bus. 0=Disable. The LPT_MON_EN must be set to enable the decode." but not LPT_MON_EN (bit 18 at 50h): LPT Port Enable (LPT_MON_EN)—R/W. 1=Enable accesses to parallel port address range (LPT_DEC_SEL) to generate a device 8 (parallel port) decode event. 0=Disable. We're also setting the LPT_DEC_SEL field (that's the 0x60 written to 63h) to 11, which means reserved, rather than to 01 (378h-37Fh). Likewise we're not setting SA_MON_EN, SB_MON_EN (respectively bit 14 and bit 16 at address 50h) for the serial ports. However, we're setting COMA_DEC_SEL and COMB_DEC_SEL correctly, unlike the corresponding register for the parallel port. All these fields are left as they are, since they are probably only meant to be used in the DSDT. Signed-off-by: Paolo Bonzini Signed-off-by: Anthony Liguori --- hw/acpi_piix4.c | 22 +++++++++++++++++----- ioport.c | 19 +++++++++++++------ ioport.h | 2 +- 3 files changed, 31 insertions(+), 12 deletions(-) diff --git a/hw/acpi_piix4.c b/hw/acpi_piix4.c index 350558b859..03bd768366 100644 --- a/hw/acpi_piix4.c +++ b/hw/acpi_piix4.c @@ -23,6 +23,7 @@ #include "acpi.h" #include "sysemu.h" #include "range.h" +#include "ioport.h" //#define DEBUG @@ -63,6 +64,7 @@ typedef struct PIIX4PMState { qemu_irq irq; qemu_irq smi_irq; int kvm_enabled; + Notifier machine_ready; /* for pci hotplug */ ACPIGPE gpe; @@ -311,6 +313,19 @@ static void piix4_powerdown(void *opaque, int irq, int power_failing) acpi_pm1_evt_power_down(pm1a, tmr); } +static void piix4_pm_machine_ready(struct Notifier* n) +{ + PIIX4PMState *s = container_of(n, PIIX4PMState, machine_ready); + uint8_t *pci_conf; + + pci_conf = s->dev.config; + pci_conf[0x5f] = (isa_is_ioport_assigned(0x378) ? 0x80 : 0) | 0x10; + pci_conf[0x63] = 0x60; + pci_conf[0x67] = (isa_is_ioport_assigned(0x3f8) ? 0x08 : 0) | + (isa_is_ioport_assigned(0x2f8) ? 0x90 : 0); + +} + static int piix4_pm_initfn(PCIDevice *dev) { PIIX4PMState *s = DO_UPCAST(PIIX4PMState, dev, dev); @@ -337,11 +352,6 @@ static int piix4_pm_initfn(PCIDevice *dev) /* XXX: which specification is used ? The i82731AB has different mappings */ - pci_conf[0x5f] = (parallel_hds[0] != NULL ? 0x80 : 0) | 0x10; - pci_conf[0x63] = 0x60; - pci_conf[0x67] = (serial_hds[0] != NULL ? 0x08 : 0) | - (serial_hds[1] != NULL ? 0x90 : 0); - pci_conf[0x90] = s->smb_io_base | 1; pci_conf[0x91] = s->smb_io_base >> 8; pci_conf[0xd2] = 0x09; @@ -354,6 +364,8 @@ static int piix4_pm_initfn(PCIDevice *dev) qemu_system_powerdown = *qemu_allocate_irqs(piix4_powerdown, s, 1); pm_smbus_init(&s->dev.qdev, &s->smb); + s->machine_ready.notify = piix4_pm_machine_ready; + qemu_add_machine_init_done_notifier(&s->machine_ready); qemu_register_reset(piix4_reset, s); piix4_acpi_system_hot_add_init(dev->bus, s); diff --git a/ioport.c b/ioport.c index 2e971fa3e5..0d2611d142 100644 --- a/ioport.c +++ b/ioport.c @@ -245,18 +245,25 @@ void isa_unassign_ioport(pio_addr_t start, int length) int i; for(i = start; i < start + length; i++) { - ioport_read_table[0][i] = default_ioport_readb; - ioport_read_table[1][i] = default_ioport_readw; - ioport_read_table[2][i] = default_ioport_readl; + ioport_read_table[0][i] = NULL; + ioport_read_table[1][i] = NULL; + ioport_read_table[2][i] = NULL; - ioport_write_table[0][i] = default_ioport_writeb; - ioport_write_table[1][i] = default_ioport_writew; - ioport_write_table[2][i] = default_ioport_writel; + ioport_write_table[0][i] = NULL; + ioport_write_table[1][i] = NULL; + ioport_write_table[2][i] = NULL; ioport_opaque[i] = NULL; } } +bool isa_is_ioport_assigned(pio_addr_t start) +{ + return (ioport_read_table[0][start] || ioport_write_table[0][start] || + ioport_read_table[1][start] || ioport_write_table[1][start] || + ioport_read_table[2][start] || ioport_write_table[2][start]); +} + /***********************************************************/ void cpu_outb(pio_addr_t addr, uint8_t val) diff --git a/ioport.h b/ioport.h index 5ae62a3a2c..82ffd9d81a 100644 --- a/ioport.h +++ b/ioport.h @@ -43,7 +43,7 @@ int register_ioport_read(pio_addr_t start, int length, int size, int register_ioport_write(pio_addr_t start, int length, int size, IOPortWriteFunc *func, void *opaque); void isa_unassign_ioport(pio_addr_t start, int length); - +bool isa_is_ioport_assigned(pio_addr_t start); void cpu_outb(pio_addr_t addr, uint8_t val); void cpu_outw(pio_addr_t addr, uint16_t val); From 49e40b6627ea92c246b3903d171c88480b782512 Mon Sep 17 00:00:00 2001 From: Adam Lackorzynski Date: Wed, 6 Jul 2011 10:03:57 +0200 Subject: [PATCH 032/230] multiboot: Support commas in module parameters Support commas in the parameter list of multiboot modules as well as for the kernel command line, by using double commas (via get_opt_value()). Signed-off-by: Adam Lackorzynski Reviewed-by: Kevin Wolf Signed-off-by: Anthony Liguori --- hw/multiboot.c | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/hw/multiboot.c b/hw/multiboot.c index 6e6cfb9531..2426e84833 100644 --- a/hw/multiboot.c +++ b/hw/multiboot.c @@ -97,11 +97,11 @@ typedef struct { static uint32_t mb_add_cmdline(MultibootState *s, const char *cmdline) { - int len = strlen(cmdline) + 1; target_phys_addr_t p = s->offset_cmdlines; + char *b = (char *)s->mb_buf + p; - pstrcpy((char *)s->mb_buf + p, len, cmdline); - s->offset_cmdlines += len; + get_opt_value(b, strlen(cmdline) + 1, cmdline); + s->offset_cmdlines += strlen(b) + 1; return s->mb_buf_phys + p; } @@ -238,7 +238,7 @@ int load_multiboot(void *fw_cfg, const char *r = initrd_filename; mbs.mb_buf_size += strlen(r) + 1; mbs.mb_mods_avail = 1; - while ((r = strchr(r, ','))) { + while (*(r = get_opt_value(NULL, 0, r))) { mbs.mb_mods_avail++; r++; } @@ -252,7 +252,7 @@ int load_multiboot(void *fw_cfg, mbs.offset_cmdlines = mbs.offset_mbinfo + mbs.mb_mods_avail * MB_MOD_SIZE; if (initrd_filename) { - char *next_initrd; + char *next_initrd, not_last; mbs.offset_mods = mbs.mb_buf_size; @@ -261,9 +261,9 @@ int load_multiboot(void *fw_cfg, int mb_mod_length; uint32_t offs = mbs.mb_buf_size; - next_initrd = strchr(initrd_filename, ','); - if (next_initrd) - *next_initrd = '\0'; + next_initrd = (char *)get_opt_value(NULL, 0, initrd_filename); + not_last = *next_initrd; + *next_initrd = '\0'; /* if a space comes after the module filename, treat everything after that as parameters */ target_phys_addr_t c = mb_add_cmdline(&mbs, initrd_filename); @@ -287,7 +287,7 @@ int load_multiboot(void *fw_cfg, (char *)mbs.mb_buf + offs, (char *)mbs.mb_buf + offs + mb_mod_length, c); initrd_filename = next_initrd+1; - } while (next_initrd); + } while (not_last); } /* Commandline support */ From 46daff13c854769bfa8c51e77719325ea0f47b1b Mon Sep 17 00:00:00 2001 From: Paolo Bonzini Date: Thu, 9 Jun 2011 13:10:24 +0200 Subject: [PATCH 033/230] iothread: replace fair_mutex with a condition variable This conveys the intention better, and scales to more than >1 threads contending the mutex with the iothread (as long as all of them have a "quiescent point" like the TCG thread has). Also, on Mac OS X the fair_mutex somehow didn't work as intended and deadlocked. Signed-off-by: Paolo Bonzini Tested-by: Alexander Graf Signed-off-by: Anthony Liguori --- cpus.c | 24 +++++++++--------------- 1 file changed, 9 insertions(+), 15 deletions(-) diff --git a/cpus.c b/cpus.c index 3035314486..6bf4e3f005 100644 --- a/cpus.c +++ b/cpus.c @@ -636,7 +636,8 @@ void vm_stop(int reason) #else /* CONFIG_IOTHREAD */ QemuMutex qemu_global_mutex; -static QemuMutex qemu_fair_mutex; +static QemuCond qemu_io_proceeded_cond; +static bool iothread_requesting_mutex; static QemuThread io_thread; @@ -672,7 +673,7 @@ int qemu_init_main_loop(void) qemu_cond_init(&qemu_system_cond); qemu_cond_init(&qemu_pause_cond); qemu_cond_init(&qemu_work_cond); - qemu_mutex_init(&qemu_fair_mutex); + qemu_cond_init(&qemu_io_proceeded_cond); qemu_mutex_init(&qemu_global_mutex); qemu_mutex_lock(&qemu_global_mutex); @@ -755,17 +756,9 @@ static void qemu_tcg_wait_io_event(void) qemu_cond_wait(tcg_halt_cond, &qemu_global_mutex); } - qemu_mutex_unlock(&qemu_global_mutex); - - /* - * Users of qemu_global_mutex can be starved, having no chance - * to acquire it since this path will get to it first. - * So use another lock to provide fairness. - */ - qemu_mutex_lock(&qemu_fair_mutex); - qemu_mutex_unlock(&qemu_fair_mutex); - - qemu_mutex_lock(&qemu_global_mutex); + while (iothread_requesting_mutex) { + qemu_cond_wait(&qemu_io_proceeded_cond, &qemu_global_mutex); + } for (env = first_cpu; env != NULL; env = env->next_cpu) { qemu_wait_io_event_common(env); @@ -908,12 +901,13 @@ void qemu_mutex_lock_iothread(void) if (kvm_enabled()) { qemu_mutex_lock(&qemu_global_mutex); } else { - qemu_mutex_lock(&qemu_fair_mutex); + iothread_requesting_mutex = true; if (qemu_mutex_trylock(&qemu_global_mutex)) { qemu_cpu_kick_thread(first_cpu); qemu_mutex_lock(&qemu_global_mutex); } - qemu_mutex_unlock(&qemu_fair_mutex); + iothread_requesting_mutex = false; + qemu_cond_broadcast(&qemu_io_proceeded_cond); } } From 84682834eb8f654da5e03a92930d80b8ae0d3065 Mon Sep 17 00:00:00 2001 From: Paolo Bonzini Date: Thu, 9 Jun 2011 13:10:25 +0200 Subject: [PATCH 034/230] qemu-timer: change unix timer to dynticks A timer that wakes up every millisecond puts a lot of stress on the iothread. The large amount of IPIs causes very high context switch activity, making emulation slow and the UI unusable. This is by the way the same reason why the Windows timers were switched to dynticks. Signed-off-by: Paolo Bonzini Tested-by: Alexander Graf Signed-off-by: Anthony Liguori --- qemu-timer.c | 40 ++++++++++++++++++++++++++++------------ 1 file changed, 28 insertions(+), 12 deletions(-) diff --git a/qemu-timer.c b/qemu-timer.c index 72066c7c50..67c2974959 100644 --- a/qemu-timer.c +++ b/qemu-timer.c @@ -218,6 +218,7 @@ static void win32_rearm_timer(struct qemu_alarm_timer *t); static int unix_start_timer(struct qemu_alarm_timer *t); static void unix_stop_timer(struct qemu_alarm_timer *t); +static void unix_rearm_timer(struct qemu_alarm_timer *t); #ifdef __linux__ @@ -290,7 +291,7 @@ static struct qemu_alarm_timer alarm_timers[] = { {"dynticks", dynticks_start_timer, dynticks_stop_timer, dynticks_rearm_timer}, #endif - {"unix", unix_start_timer, unix_stop_timer, NULL}, + {"unix", unix_start_timer, unix_stop_timer, unix_rearm_timer}, #else {"mmtimer", mm_start_timer, mm_stop_timer, NULL}, {"mmtimer2", mm_start_timer, mm_stop_timer, mm_rearm_timer}, @@ -890,8 +891,6 @@ static void dynticks_rearm_timer(struct qemu_alarm_timer *t) static int unix_start_timer(struct qemu_alarm_timer *t) { struct sigaction act; - struct itimerval itv; - int err; /* timer signal */ sigfillset(&act.sa_mask); @@ -899,18 +898,35 @@ static int unix_start_timer(struct qemu_alarm_timer *t) act.sa_handler = host_alarm_handler; sigaction(SIGALRM, &act, NULL); + return 0; +} + +static void unix_rearm_timer(struct qemu_alarm_timer *t) +{ + struct itimerval itv; + int64_t nearest_delta_ns = INT64_MAX; + int err; + + assert(alarm_has_dynticks(t)); + if (!active_timers[QEMU_CLOCK_REALTIME] && + !active_timers[QEMU_CLOCK_VIRTUAL] && + !active_timers[QEMU_CLOCK_HOST]) + return; + + nearest_delta_ns = qemu_next_alarm_deadline(); + if (nearest_delta_ns < MIN_TIMER_REARM_NS) + nearest_delta_ns = MIN_TIMER_REARM_NS; itv.it_interval.tv_sec = 0; - /* for i386 kernel 2.6 to get 1 ms */ - itv.it_interval.tv_usec = 999; - itv.it_value.tv_sec = 0; - itv.it_value.tv_usec = 10 * 1000; - + itv.it_interval.tv_usec = 0; /* 0 for one-shot timer */ + itv.it_value.tv_sec = nearest_delta_ns / 1000000000; + itv.it_value.tv_usec = (nearest_delta_ns % 1000000000) / 1000; err = setitimer(ITIMER_REAL, &itv, NULL); - if (err) - return -1; - - return 0; + if (err) { + perror("setitimer"); + fprintf(stderr, "Internal timer error: aborting\n"); + exit(1); + } } static void unix_stop_timer(struct qemu_alarm_timer *t) From 6e1db57b2ac9025c2443c665a0d9e78748637b26 Mon Sep 17 00:00:00 2001 From: Kevin Wolf Date: Wed, 1 Jun 2011 13:29:11 +0200 Subject: [PATCH 035/230] qemu-char: Print strerror message on failure The only way for chardev drivers to communicate an error was to return a NULL pointer, which resulted in an error message that said _that_ something went wrong, but not _why_. This patch changes the interface to return 0/-errno and updates qemu_chr_open_opts to use strerror to display a more helpful error message. Signed-off-by: Kevin Wolf Signed-off-by: Anthony Liguori --- console.c | 8 ++- console.h | 2 +- hw/baum.c | 7 +- hw/msmouse.c | 5 +- hw/msmouse.h | 2 +- qemu-char.c | 165 +++++++++++++++++++++++++++------------------- spice-qemu-char.c | 9 +-- ui/qemu-spice.h | 2 +- 8 files changed, 117 insertions(+), 83 deletions(-) diff --git a/console.c b/console.c index acd8ca162c..242086cf42 100644 --- a/console.c +++ b/console.c @@ -1514,7 +1514,7 @@ static void text_console_do_init(CharDriverState *chr, DisplayState *ds) chr->init(chr); } -CharDriverState *text_console_init(QemuOpts *opts) +int text_console_init(QemuOpts *opts, CharDriverState **_chr) { CharDriverState *chr; TextConsole *s; @@ -1546,7 +1546,7 @@ CharDriverState *text_console_init(QemuOpts *opts) if (!s) { free(chr); - return NULL; + return -EBUSY; } s->chr = chr; @@ -1554,7 +1554,9 @@ CharDriverState *text_console_init(QemuOpts *opts) s->g_height = height; chr->opaque = s; chr->chr_set_echo = text_console_set_echo; - return chr; + + *_chr = chr; + return 0; } void text_consoles_set_display(DisplayState *ds) diff --git a/console.h b/console.h index 64d1f090ed..c09537b861 100644 --- a/console.h +++ b/console.h @@ -354,7 +354,7 @@ void vga_hw_text_update(console_ch_t *chardata); int is_graphic_console(void); int is_fixedsize_console(void); -CharDriverState *text_console_init(QemuOpts *opts); +int text_console_init(QemuOpts *opts, CharDriverState **_chr); void text_consoles_set_display(DisplayState *ds); void console_select(unsigned int index); void console_color_init(DisplayState *ds); diff --git a/hw/baum.c b/hw/baum.c index 2aaf5ffe9d..33a22a73d9 100644 --- a/hw/baum.c +++ b/hw/baum.c @@ -576,7 +576,7 @@ static void baum_close(struct CharDriverState *chr) qemu_free(baum); } -CharDriverState *chr_baum_init(QemuOpts *opts) +int chr_baum_init(QemuOpts *opts, CharDriverState **_chr) { BaumDriverState *baum; CharDriverState *chr; @@ -629,7 +629,8 @@ CharDriverState *chr_baum_init(QemuOpts *opts) qemu_chr_generic_open(chr); - return chr; + *_chr = chr; + return 0; fail: qemu_free_timer(baum->cellCount_timer); @@ -638,5 +639,5 @@ fail_handle: qemu_free(handle); qemu_free(chr); qemu_free(baum); - return NULL; + return -EIO; } diff --git a/hw/msmouse.c b/hw/msmouse.c index 05f893ca93..67c6cd43e0 100644 --- a/hw/msmouse.c +++ b/hw/msmouse.c @@ -64,7 +64,7 @@ static void msmouse_chr_close (struct CharDriverState *chr) qemu_free (chr); } -CharDriverState *qemu_chr_open_msmouse(QemuOpts *opts) +int qemu_chr_open_msmouse(QemuOpts *opts, CharDriverState **_chr) { CharDriverState *chr; @@ -74,5 +74,6 @@ CharDriverState *qemu_chr_open_msmouse(QemuOpts *opts) qemu_add_mouse_event_handler(msmouse_event, chr, 0, "QEMU Microsoft Mouse"); - return chr; + *_chr = chr; + return 0; } diff --git a/hw/msmouse.h b/hw/msmouse.h index 456cb21424..8b853b35bf 100644 --- a/hw/msmouse.h +++ b/hw/msmouse.h @@ -1,2 +1,2 @@ /* msmouse.c */ -CharDriverState *qemu_chr_open_msmouse(QemuOpts *opts); +int qemu_chr_open_msmouse(QemuOpts *opts, CharDriverState **_chr); diff --git a/qemu-char.c b/qemu-char.c index fb13b28454..926987b98a 100644 --- a/qemu-char.c +++ b/qemu-char.c @@ -219,13 +219,15 @@ static int null_chr_write(CharDriverState *chr, const uint8_t *buf, int len) return len; } -static CharDriverState *qemu_chr_open_null(QemuOpts *opts) +static int qemu_chr_open_null(QemuOpts *opts, CharDriverState **_chr) { CharDriverState *chr; chr = qemu_mallocz(sizeof(CharDriverState)); chr->chr_write = null_chr_write; - return chr; + + *_chr= chr; + return 0; } /* MUX driver for serial I/O splitting */ @@ -634,18 +636,21 @@ static CharDriverState *qemu_chr_open_fd(int fd_in, int fd_out) return chr; } -static CharDriverState *qemu_chr_open_file_out(QemuOpts *opts) +static int qemu_chr_open_file_out(QemuOpts *opts, CharDriverState **_chr) { int fd_out; TFR(fd_out = qemu_open(qemu_opt_get(opts, "path"), O_WRONLY | O_TRUNC | O_CREAT | O_BINARY, 0666)); - if (fd_out < 0) - return NULL; - return qemu_chr_open_fd(-1, fd_out); + if (fd_out < 0) { + return -errno; + } + + *_chr = qemu_chr_open_fd(-1, fd_out); + return 0; } -static CharDriverState *qemu_chr_open_pipe(QemuOpts *opts) +static int qemu_chr_open_pipe(QemuOpts *opts, CharDriverState **_chr) { int fd_in, fd_out; char filename_in[256], filename_out[256]; @@ -653,7 +658,7 @@ static CharDriverState *qemu_chr_open_pipe(QemuOpts *opts) if (filename == NULL) { fprintf(stderr, "chardev: pipe: no filename given\n"); - return NULL; + return -EINVAL; } snprintf(filename_in, 256, "%s.in", filename); @@ -665,11 +670,14 @@ static CharDriverState *qemu_chr_open_pipe(QemuOpts *opts) close(fd_in); if (fd_out >= 0) close(fd_out); - TFR(fd_in = fd_out = open(filename, O_RDWR | O_BINARY)); - if (fd_in < 0) - return NULL; + TFR(fd_in = fd_out = qemu_open(filename, O_RDWR | O_BINARY)); + if (fd_in < 0) { + return -errno; + } } - return qemu_chr_open_fd(fd_in, fd_out); + + *_chr = qemu_chr_open_fd(fd_in, fd_out); + return 0; } @@ -760,12 +768,14 @@ static void qemu_chr_close_stdio(struct CharDriverState *chr) fd_chr_close(chr); } -static CharDriverState *qemu_chr_open_stdio(QemuOpts *opts) +static int qemu_chr_open_stdio(QemuOpts *opts, CharDriverState **_chr) { CharDriverState *chr; - if (stdio_nb_clients >= STDIO_MAX_CLIENTS) - return NULL; + if (stdio_nb_clients >= STDIO_MAX_CLIENTS) { + return -EBUSY; + } + if (stdio_nb_clients == 0) { old_fd0_flags = fcntl(0, F_GETFL); tcgetattr (0, &oldtty); @@ -782,7 +792,8 @@ static CharDriverState *qemu_chr_open_stdio(QemuOpts *opts) display_type != DT_NOGRAPHIC); qemu_chr_set_echo(chr, false); - return chr; + *_chr = chr; + return 0; } #ifdef __sun__ @@ -969,7 +980,7 @@ static void pty_chr_close(struct CharDriverState *chr) qemu_chr_event(chr, CHR_EVENT_CLOSED); } -static CharDriverState *qemu_chr_open_pty(QemuOpts *opts) +static int qemu_chr_open_pty(QemuOpts *opts, CharDriverState **_chr) { CharDriverState *chr; PtyCharDriver *s; @@ -987,7 +998,7 @@ static CharDriverState *qemu_chr_open_pty(QemuOpts *opts) s = qemu_mallocz(sizeof(PtyCharDriver)); if (openpty(&s->fd, &slave_fd, pty_name, NULL, NULL) < 0) { - return NULL; + return -errno; } /* Set raw attributes on the pty. */ @@ -1009,7 +1020,8 @@ static CharDriverState *qemu_chr_open_pty(QemuOpts *opts) s->timer = qemu_new_timer_ms(rt_clock, pty_chr_timer, chr); - return chr; + *_chr = chr; + return 0; } static void tty_serial_init(int fd, int speed, @@ -1210,30 +1222,28 @@ static void qemu_chr_close_tty(CharDriverState *chr) } } -static CharDriverState *qemu_chr_open_tty(QemuOpts *opts) +static int qemu_chr_open_tty(QemuOpts *opts, CharDriverState **_chr) { const char *filename = qemu_opt_get(opts, "path"); CharDriverState *chr; int fd; - TFR(fd = open(filename, O_RDWR | O_NONBLOCK)); + TFR(fd = qemu_open(filename, O_RDWR | O_NONBLOCK)); if (fd < 0) { - return NULL; + return -errno; } tty_serial_init(fd, 115200, 'N', 8, 1); chr = qemu_chr_open_fd(fd, fd); - if (!chr) { - close(fd); - return NULL; - } chr->chr_ioctl = tty_serial_ioctl; chr->chr_close = qemu_chr_close_tty; - return chr; + + *_chr = chr; + return 0; } #else /* ! __linux__ && ! __sun__ */ -static CharDriverState *qemu_chr_open_pty(QemuOpts *opts) +static int qemu_chr_open_pty(QemuOpts *opts, CharDriverState **_chr) { - return NULL; + return -ENOTSUP; } #endif /* __linux__ || __sun__ */ @@ -1347,7 +1357,7 @@ static void pp_close(CharDriverState *chr) qemu_chr_event(chr, CHR_EVENT_CLOSED); } -static CharDriverState *qemu_chr_open_pp(QemuOpts *opts) +static int qemu_chr_open_pp(QemuOpts *opts, CharDriverState **_chr) { const char *filename = qemu_opt_get(opts, "path"); CharDriverState *chr; @@ -1355,12 +1365,13 @@ static CharDriverState *qemu_chr_open_pp(QemuOpts *opts) int fd; TFR(fd = open(filename, O_RDWR)); - if (fd < 0) - return NULL; + if (fd < 0) { + return -errno; + } if (ioctl(fd, PPCLAIM) < 0) { close(fd); - return NULL; + return -errno; } drv = qemu_mallocz(sizeof(ParallelCharDriver)); @@ -1375,7 +1386,8 @@ static CharDriverState *qemu_chr_open_pp(QemuOpts *opts) qemu_chr_generic_open(chr); - return chr; + *_chr = chr; + return 0; } #endif /* __linux__ */ @@ -1417,21 +1429,24 @@ static int pp_ioctl(CharDriverState *chr, int cmd, void *arg) return 0; } -static CharDriverState *qemu_chr_open_pp(QemuOpts *opts) +static int qemu_chr_open_pp(QemuOpts *opts, CharDriverState **_chr) { const char *filename = qemu_opt_get(opts, "path"); CharDriverState *chr; int fd; - fd = open(filename, O_RDWR); - if (fd < 0) - return NULL; + fd = qemu_open(filename, O_RDWR); + if (fd < 0) { + return -errno; + } chr = qemu_mallocz(sizeof(CharDriverState)); chr->opaque = (void *)(intptr_t)fd; chr->chr_write = null_chr_write; chr->chr_ioctl = pp_ioctl; - return chr; + + *_chr = chr; + return 0; } #endif @@ -1637,7 +1652,7 @@ static int win_chr_poll(void *opaque) return 0; } -static CharDriverState *qemu_chr_open_win(QemuOpts *opts) +static int qemu_chr_open_win(QemuOpts *opts, CharDriverState **_chr) { const char *filename = qemu_opt_get(opts, "path"); CharDriverState *chr; @@ -1652,10 +1667,12 @@ static CharDriverState *qemu_chr_open_win(QemuOpts *opts) if (win_chr_init(chr, filename) < 0) { free(s); free(chr); - return NULL; + return -EIO; } qemu_chr_generic_open(chr); - return chr; + + *_chr = chr; + return 0; } static int win_chr_pipe_poll(void *opaque) @@ -1737,7 +1754,7 @@ static int win_chr_pipe_init(CharDriverState *chr, const char *filename) } -static CharDriverState *qemu_chr_open_win_pipe(QemuOpts *opts) +static int qemu_chr_open_win_pipe(QemuOpts *opts, CharDriverState **_chr) { const char *filename = qemu_opt_get(opts, "path"); CharDriverState *chr; @@ -1752,10 +1769,12 @@ static CharDriverState *qemu_chr_open_win_pipe(QemuOpts *opts) if (win_chr_pipe_init(chr, filename) < 0) { free(s); free(chr); - return NULL; + return -EIO; } qemu_chr_generic_open(chr); - return chr; + + *_chr = chr; + return 0; } static CharDriverState *qemu_chr_open_win_file(HANDLE fd_out) @@ -1772,22 +1791,23 @@ static CharDriverState *qemu_chr_open_win_file(HANDLE fd_out) return chr; } -static CharDriverState *qemu_chr_open_win_con(QemuOpts *opts) +static int qemu_chr_open_win_con(QemuOpts *opts, CharDriverState **_chr) { - return qemu_chr_open_win_file(GetStdHandle(STD_OUTPUT_HANDLE)); + return qemu_chr_open_win_file(GetStdHandle(STD_OUTPUT_HANDLE), chr); } -static CharDriverState *qemu_chr_open_win_file_out(QemuOpts *opts) +static int qemu_chr_open_win_file_out(QemuOpts *opts, CharDriverState **_chr) { const char *file_out = qemu_opt_get(opts, "path"); HANDLE fd_out; fd_out = CreateFile(file_out, GENERIC_WRITE, FILE_SHARE_READ, NULL, OPEN_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL); - if (fd_out == INVALID_HANDLE_VALUE) - return NULL; + if (fd_out == INVALID_HANDLE_VALUE) { + return -EIO; + } - return qemu_chr_open_win_file(fd_out); + return qemu_chr_open_win_file(fd_out, _chr); } #endif /* !_WIN32 */ @@ -1868,11 +1888,12 @@ static void udp_chr_close(CharDriverState *chr) qemu_chr_event(chr, CHR_EVENT_CLOSED); } -static CharDriverState *qemu_chr_open_udp(QemuOpts *opts) +static int qemu_chr_open_udp(QemuOpts *opts, CharDriverState **_chr) { CharDriverState *chr = NULL; NetCharDriver *s = NULL; int fd = -1; + int ret; chr = qemu_mallocz(sizeof(CharDriverState)); s = qemu_mallocz(sizeof(NetCharDriver)); @@ -1880,6 +1901,7 @@ static CharDriverState *qemu_chr_open_udp(QemuOpts *opts) fd = inet_dgram_opts(opts); if (fd < 0) { fprintf(stderr, "inet_dgram_opts failed\n"); + ret = -errno; goto return_err; } @@ -1890,16 +1912,17 @@ static CharDriverState *qemu_chr_open_udp(QemuOpts *opts) chr->chr_write = udp_chr_write; chr->chr_update_read_handler = udp_chr_update_read_handler; chr->chr_close = udp_chr_close; - return chr; + + *_chr = chr; + return 0; return_err: - if (chr) - free(chr); - if (s) - free(s); - if (fd >= 0) + qemu_free(chr); + qemu_free(s); + if (fd >= 0) { closesocket(fd); - return NULL; + } + return ret; } /***********************************************************/ @@ -2178,7 +2201,7 @@ static void tcp_chr_close(CharDriverState *chr) qemu_chr_event(chr, CHR_EVENT_CLOSED); } -static CharDriverState *qemu_chr_open_socket(QemuOpts *opts) +static int qemu_chr_open_socket(QemuOpts *opts, CharDriverState **_chr) { CharDriverState *chr = NULL; TCPCharDriver *s = NULL; @@ -2188,6 +2211,7 @@ static CharDriverState *qemu_chr_open_socket(QemuOpts *opts) int do_nodelay; int is_unix; int is_telnet; + int ret; is_listen = qemu_opt_get_bool(opts, "server", 0); is_waitconnect = qemu_opt_get_bool(opts, "wait", 1); @@ -2213,8 +2237,10 @@ static CharDriverState *qemu_chr_open_socket(QemuOpts *opts) fd = inet_connect_opts(opts); } } - if (fd < 0) + if (fd < 0) { + ret = -errno; goto fail; + } if (!is_waitconnect) socket_set_nonblock(fd); @@ -2266,14 +2292,16 @@ static CharDriverState *qemu_chr_open_socket(QemuOpts *opts) tcp_chr_accept(chr); socket_set_nonblock(s->listen_fd); } - return chr; + + *_chr = chr; + return 0; fail: if (fd >= 0) closesocket(fd); qemu_free(s); qemu_free(chr); - return NULL; + return ret; } /***********************************************************/ @@ -2466,7 +2494,7 @@ fail: static const struct { const char *name; - CharDriverState *(*open)(QemuOpts *opts); + int (*open)(QemuOpts *opts, CharDriverState **chr); } backend_table[] = { { .name = "null", .open = qemu_chr_open_null }, { .name = "socket", .open = qemu_chr_open_socket }, @@ -2506,6 +2534,7 @@ CharDriverState *qemu_chr_open_opts(QemuOpts *opts, { CharDriverState *chr; int i; + int ret; if (qemu_opts_id(opts) == NULL) { fprintf(stderr, "chardev: no id specified\n"); @@ -2527,10 +2556,10 @@ CharDriverState *qemu_chr_open_opts(QemuOpts *opts, return NULL; } - chr = backend_table[i].open(opts); - if (!chr) { - fprintf(stderr, "chardev: opening backend \"%s\" failed\n", - qemu_opt_get(opts, "backend")); + ret = backend_table[i].open(opts, &chr); + if (ret < 0) { + fprintf(stderr, "chardev: opening backend \"%s\" failed: %s\n", + qemu_opt_get(opts, "backend"), strerror(-ret)); return NULL; } diff --git a/spice-qemu-char.c b/spice-qemu-char.c index 605c241239..95bf6b65d1 100644 --- a/spice-qemu-char.c +++ b/spice-qemu-char.c @@ -159,7 +159,7 @@ static void print_allowed_subtypes(void) fprintf(stderr, "\n"); } -CharDriverState *qemu_chr_open_spice(QemuOpts *opts) +int qemu_chr_open_spice(QemuOpts *opts, CharDriverState **_chr) { CharDriverState *chr; SpiceCharDriver *s; @@ -171,7 +171,7 @@ CharDriverState *qemu_chr_open_spice(QemuOpts *opts) if (name == NULL) { fprintf(stderr, "spice-qemu-char: missing name parameter\n"); print_allowed_subtypes(); - return NULL; + return -EINVAL; } for(;*psubtype != NULL; ++psubtype) { if (strcmp(name, *psubtype) == 0) { @@ -182,7 +182,7 @@ CharDriverState *qemu_chr_open_spice(QemuOpts *opts) if (subtype == NULL) { fprintf(stderr, "spice-qemu-char: unsupported name\n"); print_allowed_subtypes(); - return NULL; + return -EINVAL; } chr = qemu_mallocz(sizeof(CharDriverState)); @@ -199,5 +199,6 @@ CharDriverState *qemu_chr_open_spice(QemuOpts *opts) qemu_chr_generic_open(chr); - return chr; + *_chr = chr; + return 0; } diff --git a/ui/qemu-spice.h b/ui/qemu-spice.h index 3c6f1fe419..f34be69f52 100644 --- a/ui/qemu-spice.h +++ b/ui/qemu-spice.h @@ -42,7 +42,7 @@ int qemu_spice_migrate_info(const char *hostname, int port, int tls_port, void do_info_spice_print(Monitor *mon, const QObject *data); void do_info_spice(Monitor *mon, QObject **ret_data); -CharDriverState *qemu_chr_open_spice(QemuOpts *opts); +int qemu_chr_open_spice(QemuOpts *opts, CharDriverState **_chr); #else /* CONFIG_SPICE */ From 0fe28e0d9f549fab79793d3fa2f139e643d3f134 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Tue, 14 Jun 2011 12:53:08 -0700 Subject: [PATCH 036/230] vga: Fix type of lfb/map_addr/end. These addresses have been passed through pci_to_cpu_addr, and thus need to be full target_phys_addr_t. Acked-by: Jan Kiszka Signed-off-by: Richard Henderson Cc: Jan Kiszka Signed-off-by: Anthony Liguori --- hw/vga_int.h | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/hw/vga_int.h b/hw/vga_int.h index d2811bdf1c..eee91a84f3 100644 --- a/hw/vga_int.h +++ b/hw/vga_int.h @@ -106,13 +106,13 @@ typedef void (* vga_update_retrace_info_fn)(struct VGACommonState *s); typedef struct VGACommonState { uint8_t *vram_ptr; ram_addr_t vram_offset; + target_phys_addr_t lfb_addr; + target_phys_addr_t lfb_end; + target_phys_addr_t map_addr; + target_phys_addr_t map_end; uint32_t vram_size; - uint32_t lfb_addr; - uint32_t lfb_end; - uint32_t map_addr; - uint32_t map_end; - uint32_t lfb_vram_mapped; /* whether 0xa0000 is mapped as ram */ uint32_t latch; + uint32_t lfb_vram_mapped; /* whether 0xa0000 is mapped as ram */ uint8_t sr_index; uint8_t sr[256]; uint8_t gr_index; From ea0e541812c515e0bbdf598d3237d6f0bee3fbbf Mon Sep 17 00:00:00 2001 From: Sasha Levin Date: Wed, 29 Jun 2011 23:29:39 -0400 Subject: [PATCH 037/230] vl.c: Don't limit node count by smp count [I've sent this patch couple of months ago and noticed it didn't make it's way in - so I'm sending it again] It is possible to create CPU-less NUMA nodes, node amount shouldn't be limited by amount of CPUs. Tested-by: Michael Roth Acked-by: Andre Przywara Signed-off-by: Sasha Levin Signed-off-by: Anthony Liguori --- vl.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/vl.c b/vl.c index acfff858a1..65438b3382 100644 --- a/vl.c +++ b/vl.c @@ -3129,8 +3129,8 @@ int main(int argc, char **argv, char **envp) if (nb_numa_nodes > 0) { int i; - if (nb_numa_nodes > smp_cpus) { - nb_numa_nodes = smp_cpus; + if (nb_numa_nodes > MAX_NODES) { + nb_numa_nodes = MAX_NODES; } /* If no memory size if given for any node, assume the default case From e69ae5c49af45a67ebdf6a7cf465eb91b5c8ee11 Mon Sep 17 00:00:00 2001 From: Wen Congyang Date: Fri, 17 Jun 2011 10:25:22 +0800 Subject: [PATCH 038/230] do not reset no_shutdown after we shutdown the vm Daniel P. Berrange sent a libvirt's patch to support reboots with the QEMU driver. He implements it in json model like this: 1. add -no-shutdown in the qemu's option: qemu -no-shutdown xxxx 2. shutdown the vm by monitor command system_powerdown 3. wait for shutdown event 4. reset the vm by monitor command system_reset no_shutdown will be reset to 0 if the vm is powered down. We only can reboot the vm once. If no_shutdown is not reset to 0, we can reboot the vm many times. Signed-off-by: Wen Congyang Signed-off-by: Anthony Liguori --- vl.c | 1 - 1 file changed, 1 deletion(-) diff --git a/vl.c b/vl.c index 65438b3382..99d92012c1 100644 --- a/vl.c +++ b/vl.c @@ -1398,7 +1398,6 @@ static void main_loop(void) monitor_protocol_event(QEVENT_SHUTDOWN, NULL); if (no_shutdown) { vm_stop(VMSTOP_SHUTDOWN); - no_shutdown = 0; } else break; } From 7e7e2ebc942da8285931ceabf12823e165dced8b Mon Sep 17 00:00:00 2001 From: "Daniel P. Berrange" Date: Thu, 23 Jun 2011 13:31:41 +0100 Subject: [PATCH 039/230] Store VNC auth scheme per-client as well as per-server A future patch will introduce a situation where different clients may have different authentication schemes set. When a new client arrives, copy the 'auth' and 'subauth' fields from VncDisplay into the client's VncState, and use the latter in all authentication functions. * ui/vnc.h: Add 'auth' and 'subauth' to VncState * ui/vnc-auth-sasl.c, ui/vnc-auth-vencrypt.c, ui/vnc.c: Make auth functions pull auth scheme from VncState instead of VncDisplay Signed-off-by: Anthony Liguori --- ui/vnc-auth-sasl.c | 8 ++++---- ui/vnc-auth-vencrypt.c | 18 +++++++++--------- ui/vnc.c | 39 ++++++++++++++++++++++++++------------- ui/vnc.h | 2 ++ 4 files changed, 41 insertions(+), 26 deletions(-) diff --git a/ui/vnc-auth-sasl.c b/ui/vnc-auth-sasl.c index 17a621a2ba..8aac5ec004 100644 --- a/ui/vnc-auth-sasl.c +++ b/ui/vnc-auth-sasl.c @@ -538,8 +538,8 @@ void start_auth_sasl(VncState *vs) #ifdef CONFIG_VNC_TLS /* Inform SASL that we've got an external SSF layer from TLS/x509 */ - if (vs->vd->auth == VNC_AUTH_VENCRYPT && - vs->vd->subauth == VNC_AUTH_VENCRYPT_X509SASL) { + if (vs->auth == VNC_AUTH_VENCRYPT && + vs->subauth == VNC_AUTH_VENCRYPT_X509SASL) { gnutls_cipher_algorithm_t cipher; sasl_ssf_t ssf; @@ -570,8 +570,8 @@ void start_auth_sasl(VncState *vs) #ifdef CONFIG_VNC_TLS /* Disable SSF, if using TLS+x509+SASL only. TLS without x509 is not sufficiently strong */ - || (vs->vd->auth == VNC_AUTH_VENCRYPT && - vs->vd->subauth == VNC_AUTH_VENCRYPT_X509SASL) + || (vs->auth == VNC_AUTH_VENCRYPT && + vs->subauth == VNC_AUTH_VENCRYPT_X509SASL) #endif /* CONFIG_VNC_TLS */ ) { /* If we've got TLS or UNIX domain sock, we don't care about SSF */ diff --git a/ui/vnc-auth-vencrypt.c b/ui/vnc-auth-vencrypt.c index 07c169186a..674ba97dc7 100644 --- a/ui/vnc-auth-vencrypt.c +++ b/ui/vnc-auth-vencrypt.c @@ -29,7 +29,7 @@ static void start_auth_vencrypt_subauth(VncState *vs) { - switch (vs->vd->subauth) { + switch (vs->subauth) { case VNC_AUTH_VENCRYPT_TLSNONE: case VNC_AUTH_VENCRYPT_X509NONE: VNC_DEBUG("Accept TLS auth none\n"); @@ -51,7 +51,7 @@ static void start_auth_vencrypt_subauth(VncState *vs) #endif /* CONFIG_VNC_SASL */ default: /* Should not be possible, but just in case */ - VNC_DEBUG("Reject subauth %d server bug\n", vs->vd->auth); + VNC_DEBUG("Reject subauth %d server bug\n", vs->auth); vnc_write_u8(vs, 1); if (vs->minor >= 8) { static const char err[] = "Unsupported authentication type"; @@ -110,17 +110,17 @@ static void vnc_tls_handshake_io(void *opaque) { #define NEED_X509_AUTH(vs) \ - ((vs)->vd->subauth == VNC_AUTH_VENCRYPT_X509NONE || \ - (vs)->vd->subauth == VNC_AUTH_VENCRYPT_X509VNC || \ - (vs)->vd->subauth == VNC_AUTH_VENCRYPT_X509PLAIN || \ - (vs)->vd->subauth == VNC_AUTH_VENCRYPT_X509SASL) + ((vs)->subauth == VNC_AUTH_VENCRYPT_X509NONE || \ + (vs)->subauth == VNC_AUTH_VENCRYPT_X509VNC || \ + (vs)->subauth == VNC_AUTH_VENCRYPT_X509PLAIN || \ + (vs)->subauth == VNC_AUTH_VENCRYPT_X509SASL) static int protocol_client_vencrypt_auth(VncState *vs, uint8_t *data, size_t len) { int auth = read_u32(data, 0); - if (auth != vs->vd->subauth) { + if (auth != vs->subauth) { VNC_DEBUG("Rejecting auth %d\n", auth); vnc_write_u8(vs, 0); /* Reject auth */ vnc_flush(vs); @@ -153,10 +153,10 @@ static int protocol_client_vencrypt_init(VncState *vs, uint8_t *data, size_t len vnc_flush(vs); vnc_client_error(vs); } else { - VNC_DEBUG("Sending allowed auth %d\n", vs->vd->subauth); + VNC_DEBUG("Sending allowed auth %d\n", vs->subauth); vnc_write_u8(vs, 0); /* Accept version */ vnc_write_u8(vs, 1); /* Number of sub-auths */ - vnc_write_u32(vs, vs->vd->subauth); /* The supported auth */ + vnc_write_u32(vs, vs->subauth); /* The supported auth */ vnc_flush(vs); vnc_read_when(vs, protocol_client_vencrypt_auth, 4); } diff --git a/ui/vnc.c b/ui/vnc.c index 14f2930d1a..39b5b51fa9 100644 --- a/ui/vnc.c +++ b/ui/vnc.c @@ -2124,7 +2124,7 @@ static int protocol_client_auth(VncState *vs, uint8_t *data, size_t len) { /* We only advertise 1 auth scheme at a time, so client * must pick the one we sent. Verify this */ - if (data[0] != vs->vd->auth) { /* Reject auth */ + if (data[0] != vs->auth) { /* Reject auth */ VNC_DEBUG("Reject auth %d because it didn't match advertized\n", (int)data[0]); vnc_write_u32(vs, 1); if (vs->minor >= 8) { @@ -2135,7 +2135,7 @@ static int protocol_client_auth(VncState *vs, uint8_t *data, size_t len) vnc_client_error(vs); } else { /* Accept requested auth */ VNC_DEBUG("Client requested auth %d\n", (int)data[0]); - switch (vs->vd->auth) { + switch (vs->auth) { case VNC_AUTH_NONE: VNC_DEBUG("Accept auth none\n"); if (vs->minor >= 8) { @@ -2165,7 +2165,7 @@ static int protocol_client_auth(VncState *vs, uint8_t *data, size_t len) #endif /* CONFIG_VNC_SASL */ default: /* Should not be possible, but just in case */ - VNC_DEBUG("Reject auth %d server code bug\n", vs->vd->auth); + VNC_DEBUG("Reject auth %d server code bug\n", vs->auth); vnc_write_u8(vs, 1); if (vs->minor >= 8) { static const char err[] = "Authentication failed"; @@ -2210,26 +2210,26 @@ static int protocol_version(VncState *vs, uint8_t *version, size_t len) vs->minor = 3; if (vs->minor == 3) { - if (vs->vd->auth == VNC_AUTH_NONE) { + if (vs->auth == VNC_AUTH_NONE) { VNC_DEBUG("Tell client auth none\n"); - vnc_write_u32(vs, vs->vd->auth); + vnc_write_u32(vs, vs->auth); vnc_flush(vs); start_client_init(vs); - } else if (vs->vd->auth == VNC_AUTH_VNC) { + } else if (vs->auth == VNC_AUTH_VNC) { VNC_DEBUG("Tell client VNC auth\n"); - vnc_write_u32(vs, vs->vd->auth); + vnc_write_u32(vs, vs->auth); vnc_flush(vs); start_auth_vnc(vs); } else { - VNC_DEBUG("Unsupported auth %d for protocol 3.3\n", vs->vd->auth); + VNC_DEBUG("Unsupported auth %d for protocol 3.3\n", vs->auth); vnc_write_u32(vs, VNC_AUTH_INVALID); vnc_flush(vs); vnc_client_error(vs); } } else { - VNC_DEBUG("Telling client we support auth %d\n", vs->vd->auth); + VNC_DEBUG("Telling client we support auth %d\n", vs->auth); vnc_write_u8(vs, 1); /* num auth */ - vnc_write_u8(vs, vs->vd->auth); + vnc_write_u8(vs, vs->auth); vnc_read_when(vs, protocol_client_auth, 1); vnc_flush(vs); } @@ -2494,12 +2494,25 @@ static void vnc_remove_timer(VncDisplay *vd) } } -static void vnc_connect(VncDisplay *vd, int csock) +static void vnc_connect(VncDisplay *vd, int csock, int skipauth) { VncState *vs = qemu_mallocz(sizeof(VncState)); int i; vs->csock = csock; + + if (skipauth) { + vs->auth = VNC_AUTH_NONE; +#ifdef CONFIG_VNC_TLS + vs->subauth = VNC_AUTH_INVALID; +#endif + } else { + vs->auth = vd->auth; +#ifdef CONFIG_VNC_TLS + vs->subauth = vd->subauth; +#endif + } + vs->lossy_rect = qemu_mallocz(VNC_STAT_ROWS * sizeof (*vs->lossy_rect)); for (i = 0; i < VNC_STAT_ROWS; ++i) { vs->lossy_rect[i] = qemu_mallocz(VNC_STAT_COLS * sizeof (uint8_t)); @@ -2557,7 +2570,7 @@ static void vnc_listen_read(void *opaque) int csock = qemu_accept(vs->lsock, (struct sockaddr *)&addr, &addrlen); if (csock != -1) { - vnc_connect(vs, csock); + vnc_connect(vs, csock, 0); } } @@ -2887,7 +2900,7 @@ int vnc_display_open(DisplayState *ds, const char *display) } else { int csock = vs->lsock; vs->lsock = -1; - vnc_connect(vs, csock); + vnc_connect(vs, csock, 0); } return 0; diff --git a/ui/vnc.h b/ui/vnc.h index f10c5dc4e0..66689f1d60 100644 --- a/ui/vnc.h +++ b/ui/vnc.h @@ -256,8 +256,10 @@ struct VncState int major; int minor; + int auth; char challenge[VNC_AUTH_CHALLENGE_SIZE]; #ifdef CONFIG_VNC_TLS + int subauth; /* Used by VeNCrypt */ VncStateTLS tls; #endif #ifdef CONFIG_VNC_SASL From 13661089810d3e59931f3e80d7cb541b99af7071 Mon Sep 17 00:00:00 2001 From: "Daniel P. Berrange" Date: Thu, 23 Jun 2011 13:31:42 +0100 Subject: [PATCH 040/230] Introduce a 'client_add' monitor command accepting an open FD Allow client connections for VNC and socket based character devices to be passed in over the monitor using SCM_RIGHTS. One intended usage scenario is to start QEMU with VNC on a UNIX domain socket. An unprivileged user which cannot access the UNIX domain socket, can then connect to QEMU's VNC server by passing an open FD to libvirt, which passes it onto QEMU. { "execute": "get_fd", "arguments": { "fdname": "myclient" } } { "return": {} } { "execute": "add_client", "arguments": { "protocol": "vnc", "fdname": "myclient", "skipauth": true } } { "return": {} } In this case 'protocol' can be 'vnc' or 'spice', or the name of a character device (eg from -chardev id=XXXX) The 'skipauth' parameter can be used to skip any configured VNC authentication scheme, which is useful if the mgmt layer talking to the monitor has already authenticated the client in another way. * console.h: Define 'vnc_display_add_client' method * monitor.c: Implement 'client_add' command * qemu-char.c, qemu-char.h: Add 'qemu_char_add_client' method * qerror.c, qerror.h: Add QERR_ADD_CLIENT_FAILED * qmp-commands.hx: Declare 'client_add' command * ui/vnc.c: Implement 'vnc_display_add_client' method Signed-off-by: Anthony Liguori --- console.h | 1 + monitor.c | 32 ++++++++++++++++++++++++++++++++ qemu-char.c | 30 ++++++++++++++++++++++++------ qemu-char.h | 2 ++ qerror.c | 4 ++++ qerror.h | 3 +++ qmp-commands.hx | 27 +++++++++++++++++++++++++++ ui/vnc.c | 7 +++++++ 8 files changed, 100 insertions(+), 6 deletions(-) diff --git a/console.h b/console.h index c09537b861..67d137384e 100644 --- a/console.h +++ b/console.h @@ -372,6 +372,7 @@ void cocoa_display_init(DisplayState *ds, int full_screen); void vnc_display_init(DisplayState *ds); void vnc_display_close(DisplayState *ds); int vnc_display_open(DisplayState *ds, const char *display); +void vnc_display_add_client(DisplayState *ds, int csock, int skipauth); int vnc_display_disable_login(DisplayState *ds); char *vnc_display_local_addr(DisplayState *ds); #ifdef CONFIG_VNC diff --git a/monitor.c b/monitor.c index 67ceb46a62..718935b881 100644 --- a/monitor.c +++ b/monitor.c @@ -1185,6 +1185,38 @@ static int expire_password(Monitor *mon, const QDict *qdict, QObject **ret_data) return -1; } +static int add_graphics_client(Monitor *mon, const QDict *qdict, QObject **ret_data) +{ + const char *protocol = qdict_get_str(qdict, "protocol"); + const char *fdname = qdict_get_str(qdict, "fdname"); + int skipauth = qdict_get_try_bool(qdict, "skipauth", 0); + CharDriverState *s; + + if (strcmp(protocol, "spice") == 0) { + if (!using_spice) { + /* correct one? spice isn't a device ,,, */ + qerror_report(QERR_DEVICE_NOT_ACTIVE, "spice"); + return -1; + } + qerror_report(QERR_ADD_CLIENT_FAILED); + return -1; + } else if (strcmp(protocol, "vnc") == 0) { + int fd = monitor_get_fd(mon, fdname); + vnc_display_add_client(NULL, fd, skipauth); + return 0; + } else if ((s = qemu_chr_find(protocol)) != NULL) { + int fd = monitor_get_fd(mon, fdname); + if (qemu_chr_add_client(s, fd) < 0) { + qerror_report(QERR_ADD_CLIENT_FAILED); + return -1; + } + return 0; + } + + qerror_report(QERR_INVALID_PARAMETER, "protocol"); + return -1; +} + static int client_migrate_info(Monitor *mon, const QDict *qdict, QObject **ret_data) { const char *protocol = qdict_get_str(qdict, "protocol"); diff --git a/qemu-char.c b/qemu-char.c index 926987b98a..dcf706592b 100644 --- a/qemu-char.c +++ b/qemu-char.c @@ -168,6 +168,11 @@ int qemu_chr_get_msgfd(CharDriverState *s) return s->get_msgfd ? s->get_msgfd(s) : -1; } +int qemu_chr_add_client(CharDriverState *s, int fd) +{ + return s->chr_add_client ? s->chr_add_client(s, fd) : -1; +} + void qemu_chr_accept_input(CharDriverState *s) { if (s->chr_accept_input) @@ -2146,6 +2151,22 @@ static void socket_set_nodelay(int fd) setsockopt(fd, IPPROTO_TCP, TCP_NODELAY, (char *)&val, sizeof(val)); } +static int tcp_chr_add_client(CharDriverState *chr, int fd) +{ + TCPCharDriver *s = chr->opaque; + if (s->fd != -1) + return -1; + + socket_set_nonblock(fd); + if (s->do_nodelay) + socket_set_nodelay(fd); + s->fd = fd; + qemu_set_fd_handler(s->listen_fd, NULL, NULL, NULL); + tcp_chr_connect(chr); + + return 0; +} + static void tcp_chr_accept(void *opaque) { CharDriverState *chr = opaque; @@ -2178,12 +2199,8 @@ static void tcp_chr_accept(void *opaque) break; } } - socket_set_nonblock(fd); - if (s->do_nodelay) - socket_set_nodelay(fd); - s->fd = fd; - qemu_set_fd_handler(s->listen_fd, NULL, NULL, NULL); - tcp_chr_connect(chr); + if (tcp_chr_add_client(chr, fd) < 0) + close(fd); } static void tcp_chr_close(CharDriverState *chr) @@ -2256,6 +2273,7 @@ static int qemu_chr_open_socket(QemuOpts *opts, CharDriverState **_chr) chr->chr_write = tcp_chr_write; chr->chr_close = tcp_chr_close; chr->get_msgfd = tcp_get_msgfd; + chr->chr_add_client = tcp_chr_add_client; if (is_listen) { s->listen_fd = fd; diff --git a/qemu-char.h b/qemu-char.h index 892c6da9aa..f361c6d281 100644 --- a/qemu-char.h +++ b/qemu-char.h @@ -57,6 +57,7 @@ struct CharDriverState { void (*chr_update_read_handler)(struct CharDriverState *s); int (*chr_ioctl)(struct CharDriverState *s, int cmd, void *arg); int (*get_msgfd)(struct CharDriverState *s); + int (*chr_add_client)(struct CharDriverState *chr, int fd); IOEventHandler *chr_event; IOCanReadHandler *chr_can_read; IOReadHandler *chr_read; @@ -99,6 +100,7 @@ int qemu_chr_can_read(CharDriverState *s); void qemu_chr_read(CharDriverState *s, uint8_t *buf, int len); int qemu_chr_get_msgfd(CharDriverState *s); void qemu_chr_accept_input(CharDriverState *s); +int qemu_chr_add_client(CharDriverState *s, int fd); void qemu_chr_info_print(Monitor *mon, const QObject *ret_data); void qemu_chr_info(Monitor *mon, QObject **ret_data); CharDriverState *qemu_chr_find(const char *name); diff --git a/qerror.c b/qerror.c index 229d0d63e3..69c1bc9af7 100644 --- a/qerror.c +++ b/qerror.c @@ -197,6 +197,10 @@ static const QErrorStringTable qerror_table[] = { .error_fmt = QERR_SET_PASSWD_FAILED, .desc = "Could not set password", }, + { + .error_fmt = QERR_ADD_CLIENT_FAILED, + .desc = "Could not add client", + }, { .error_fmt = QERR_TOO_MANY_FILES, .desc = "Too many open files", diff --git a/qerror.h b/qerror.h index 7ec0fc12d7..8058456d2e 100644 --- a/qerror.h +++ b/qerror.h @@ -166,6 +166,9 @@ QError *qobject_to_qerror(const QObject *obj); #define QERR_SET_PASSWD_FAILED \ "{ 'class': 'SetPasswdFailed', 'data': {} }" +#define QERR_ADD_CLIENT_FAILED \ + "{ 'class': 'AddClientFailed', 'data': {} }" + #define QERR_TOO_MANY_FILES \ "{ 'class': 'TooManyFiles', 'data': {} }" diff --git a/qmp-commands.hx b/qmp-commands.hx index 5d44edf4e7..54e313ce52 100644 --- a/qmp-commands.hx +++ b/qmp-commands.hx @@ -918,6 +918,33 @@ Example: EQMP + { + .name = "add_client", + .args_type = "protocol:s,fdname:s,skipauth:b?", + .params = "protocol fdname skipauth", + .help = "add a graphics client", + .user_print = monitor_user_noop, + .mhandler.cmd_new = add_graphics_client, + }, + +SQMP +add_client +---------- + +Add a graphics client + +Arguments: + +- "protocol": protocol name (json-string) +- "fdname": file descriptor name (json-string) + +Example: + +-> { "execute": "add_client", "arguments": { "protocol": "vnc", + "fdname": "myclient" } } +<- { "return": {} } + +EQMP { .name = "qmp_capabilities", .args_type = "", diff --git a/ui/vnc.c b/ui/vnc.c index 39b5b51fa9..8602adc68b 100644 --- a/ui/vnc.c +++ b/ui/vnc.c @@ -2924,3 +2924,10 @@ int vnc_display_open(DisplayState *ds, const char *display) } return qemu_set_fd_handler2(vs->lsock, NULL, vnc_listen_read, NULL, vs); } + +void vnc_display_add_client(DisplayState *ds, int csock, int skipauth) +{ + VncDisplay *vs = ds ? (VncDisplay *)ds->opaque : vnc_display; + + return vnc_connect(vs, csock, skipauth); +} From 3836620c091626c26caa77af66a90c047a8e8384 Mon Sep 17 00:00:00 2001 From: "Daniel P. Berrange" Date: Thu, 23 Jun 2011 13:31:43 +0100 Subject: [PATCH 041/230] Remove unused USES_X509_AUTH macro from VNC sasl code The USES_X509_AUTH macro is defined in several VNC files, but not used in all of them. Remove the unused definition. * ui/vnc-auth-sasl.c: Remove USES_X509_AUTH macro Signed-off-by: Anthony Liguori --- ui/vnc-auth-sasl.c | 7 ------- 1 file changed, 7 deletions(-) diff --git a/ui/vnc-auth-sasl.c b/ui/vnc-auth-sasl.c index 8aac5ec004..15af49bdb1 100644 --- a/ui/vnc-auth-sasl.c +++ b/ui/vnc-auth-sasl.c @@ -491,13 +491,6 @@ static int protocol_client_auth_sasl_mechname_len(VncState *vs, uint8_t *data, s return 0; } -#define USES_X509_AUTH(vs) \ - ((vs)->subauth == VNC_AUTH_VENCRYPT_X509NONE || \ - (vs)->subauth == VNC_AUTH_VENCRYPT_X509VNC || \ - (vs)->subauth == VNC_AUTH_VENCRYPT_X509PLAIN || \ - (vs)->subauth == VNC_AUTH_VENCRYPT_X509SASL) - - void start_auth_sasl(VncState *vs) { const char *mechlist = NULL; From 1cc2428ccec8759130c45033c8a44867e919485f Mon Sep 17 00:00:00 2001 From: Markus Armbruster Date: Thu, 19 May 2011 13:37:14 +0200 Subject: [PATCH 042/230] usb-ccid: Drop unused CCIDCardInfo callback print() Signed-off-by: Markus Armbruster Signed-off-by: Anthony Liguori --- hw/ccid.h | 1 - hw/usb-ccid.c | 11 ----------- 2 files changed, 12 deletions(-) diff --git a/hw/ccid.h b/hw/ccid.h index dbfc13c4f5..d3e03716c9 100644 --- a/hw/ccid.h +++ b/hw/ccid.h @@ -29,7 +29,6 @@ struct CCIDCardState { */ struct CCIDCardInfo { DeviceInfo qdev; - void (*print)(Monitor *mon, CCIDCardState *card, int indent); const uint8_t *(*get_atr)(CCIDCardState *card, uint32_t *len); void (*apdu_from_guest)(CCIDCardState *card, const uint8_t *apdu, diff --git a/hw/usb-ccid.c b/hw/usb-ccid.c index d3922998c5..4dda2c4833 100644 --- a/hw/usb-ccid.c +++ b/hw/usb-ccid.c @@ -1104,20 +1104,9 @@ static Answer *ccid_peek_next_answer(USBCCIDState *s) : &s->pending_answers[s->pending_answers_start % PENDING_ANSWERS_NUM]; } -static void ccid_bus_dev_print(Monitor *mon, DeviceState *qdev, int indent) -{ - CCIDCardState *card = DO_UPCAST(CCIDCardState, qdev, qdev); - CCIDCardInfo *info = DO_UPCAST(CCIDCardInfo, qdev, qdev->info); - - if (info->print) { - info->print(mon, card, indent); - } -} - static struct BusInfo ccid_bus_info = { .name = "ccid-bus", .size = sizeof(CCIDBus), - .print_dev = ccid_bus_dev_print, .props = (Property[]) { DEFINE_PROP_UINT32("slot", struct CCIDCardState, slot, 0), DEFINE_PROP_END_OF_LIST(), From 021a1318604e0898cee3800d5b13033e68191f4e Mon Sep 17 00:00:00 2001 From: Markus Armbruster Date: Thu, 19 May 2011 13:37:15 +0200 Subject: [PATCH 043/230] virtio-serial: Clean up virtser_bus_dev_print() output Old version looks like this in info qtree (last four lines): dev: virtconsole, id "" dev-prop: is_console = 1 dev-prop: nr = 0 dev-prop: chardev = dev-prop: name = dev-prop-int: id: 0 dev-prop-int: guest_connected: 1 dev-prop-int: host_connected: 0 dev-prop-int: throttled: 0 Indentation is off, and "dev-prop-int" suggests these are properties you can configure with -device, which isn't the case. The other buses' print_dev() callbacks don't do that. For instance, PCI's output looks like this: class Ethernet controller, addr 00:03.0, pci id 1af4:1000 (sub 1af4:0001) bar 0: i/o at 0xffffffffffffffff [0x1e] bar 1: mem at 0xffffffffffffffff [0xffe] bar 6: mem at 0xffffffffffffffff [0xfffe] Change virtser_bus_dev_print() to that style. Result: dev: virtconsole, id "" dev-prop: is_console = 1 dev-prop: nr = 0 dev-prop: chardev = dev-prop: name = port 0, guest on, host off, throttle off Signed-off-by: Markus Armbruster Signed-off-by: Anthony Liguori --- hw/virtio-serial-bus.c | 13 +++++-------- 1 file changed, 5 insertions(+), 8 deletions(-) diff --git a/hw/virtio-serial-bus.c b/hw/virtio-serial-bus.c index bdc760c36e..e7e12f0927 100644 --- a/hw/virtio-serial-bus.c +++ b/hw/virtio-serial-bus.c @@ -674,14 +674,11 @@ static void virtser_bus_dev_print(Monitor *mon, DeviceState *qdev, int indent) { VirtIOSerialPort *port = DO_UPCAST(VirtIOSerialPort, dev, qdev); - monitor_printf(mon, "%*s dev-prop-int: id: %u\n", - indent, "", port->id); - monitor_printf(mon, "%*s dev-prop-int: guest_connected: %d\n", - indent, "", port->guest_connected); - monitor_printf(mon, "%*s dev-prop-int: host_connected: %d\n", - indent, "", port->host_connected); - monitor_printf(mon, "%*s dev-prop-int: throttled: %d\n", - indent, "", port->throttled); + monitor_printf(mon, "%*sport %d, guest %s, host %s, throttle %s\n", + indent, "", port->id, + port->guest_connected ? "on" : "off", + port->host_connected ? "on" : "off", + port->throttled ? "on" : "off"); } /* This function is only used if a port id is not provided by the user */ From d6cca4b048c5b63547d5c09fb47a10f0057b88bf Mon Sep 17 00:00:00 2001 From: Markus Armbruster Date: Thu, 19 May 2011 13:37:16 +0200 Subject: [PATCH 044/230] virtio-serial: Turn props any virtio-serial-bus device must have into bus props Signed-off-by: Markus Armbruster Signed-off-by: Anthony Liguori --- hw/virtio-console.c | 4 ---- hw/virtio-serial-bus.c | 5 +++++ 2 files changed, 5 insertions(+), 4 deletions(-) diff --git a/hw/virtio-console.c b/hw/virtio-console.c index 7ebfa26516..fe5e188bf4 100644 --- a/hw/virtio-console.c +++ b/hw/virtio-console.c @@ -139,9 +139,7 @@ static VirtIOSerialPortInfo virtconsole_info = { .init = virtconsole_initfn, .exit = virtconsole_exitfn, .qdev.props = (Property[]) { - DEFINE_PROP_UINT32("nr", VirtConsole, port.id, VIRTIO_CONSOLE_BAD_ID), DEFINE_PROP_CHR("chardev", VirtConsole, chr), - DEFINE_PROP_STRING("name", VirtConsole, port.name), DEFINE_PROP_END_OF_LIST(), }, }; @@ -158,9 +156,7 @@ static VirtIOSerialPortInfo virtserialport_info = { .init = virtconsole_initfn, .exit = virtconsole_exitfn, .qdev.props = (Property[]) { - DEFINE_PROP_UINT32("nr", VirtConsole, port.id, VIRTIO_CONSOLE_BAD_ID), DEFINE_PROP_CHR("chardev", VirtConsole, chr), - DEFINE_PROP_STRING("name", VirtConsole, port.name), DEFINE_PROP_END_OF_LIST(), }, }; diff --git a/hw/virtio-serial-bus.c b/hw/virtio-serial-bus.c index e7e12f0927..c5eb931095 100644 --- a/hw/virtio-serial-bus.c +++ b/hw/virtio-serial-bus.c @@ -668,6 +668,11 @@ static struct BusInfo virtser_bus_info = { .name = "virtio-serial-bus", .size = sizeof(VirtIOSerialBus), .print_dev = virtser_bus_dev_print, + .props = (Property[]) { + DEFINE_PROP_UINT32("nr", VirtIOSerialPort, id, VIRTIO_CONSOLE_BAD_ID), + DEFINE_PROP_STRING("name", VirtIOSerialPort, name), + DEFINE_PROP_END_OF_LIST() + } }; static void virtser_bus_dev_print(Monitor *mon, DeviceState *qdev, int indent) From e0e8384dd471376c3f815c3070f161480a28cc90 Mon Sep 17 00:00:00 2001 From: Markus Armbruster Date: Thu, 19 May 2011 13:37:17 +0200 Subject: [PATCH 045/230] ide: Turn properties any IDE device must have into bus properties Signed-off-by: Markus Armbruster Signed-off-by: Anthony Liguori --- hw/ide/qdev.c | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/hw/ide/qdev.c b/hw/ide/qdev.c index d9b8f24bb5..6bd8d20270 100644 --- a/hw/ide/qdev.c +++ b/hw/ide/qdev.c @@ -31,6 +31,10 @@ static struct BusInfo ide_bus_info = { .name = "IDE", .size = sizeof(IDEBus), .get_fw_dev_path = idebus_get_fw_dev_path, + .props = (Property[]) { + DEFINE_PROP_UINT32("unit", IDEDevice, unit, -1), + DEFINE_PROP_END_OF_LIST(), + }, }; void ide_bus_new(IDEBus *idebus, DeviceState *dev, int bus_id) @@ -174,7 +178,6 @@ static int ide_drive_initfn(IDEDevice *dev) } #define DEFINE_IDE_DEV_PROPERTIES() \ - DEFINE_PROP_UINT32("unit", IDEDrive, dev.unit, -1), \ DEFINE_BLOCK_PROPERTIES(IDEDrive, dev.conf), \ DEFINE_PROP_STRING("ver", IDEDrive, dev.version), \ DEFINE_PROP_STRING("serial", IDEDrive, dev.serial) From 9e8dd45164af05a5dab00324dd10b037f5bd1e2a Mon Sep 17 00:00:00 2001 From: Jan Kiszka Date: Mon, 20 Jun 2011 14:06:26 +0200 Subject: [PATCH 046/230] notifier: Pass data argument to callback This allows to pass additional information to the notifier callback which is useful if sender and receiver do not share any other distinct data structure. Will be used first for the clock reset notifier. Signed-off-by: Jan Kiszka Signed-off-by: Anthony Liguori --- hw/acpi_piix4.c | 2 +- hw/fw_cfg.c | 2 +- input.c | 2 +- migration.c | 12 ++++++------ notify.c | 4 ++-- notify.h | 4 ++-- ui/sdl.c | 2 +- ui/spice-core.c | 2 +- ui/spice-input.c | 4 ++-- ui/vnc.c | 4 ++-- usb-linux.c | 2 +- vl.c | 4 ++-- xen-all.c | 2 +- 13 files changed, 23 insertions(+), 23 deletions(-) diff --git a/hw/acpi_piix4.c b/hw/acpi_piix4.c index 03bd768366..29f0f76c35 100644 --- a/hw/acpi_piix4.c +++ b/hw/acpi_piix4.c @@ -313,7 +313,7 @@ static void piix4_powerdown(void *opaque, int irq, int power_failing) acpi_pm1_evt_power_down(pm1a, tmr); } -static void piix4_pm_machine_ready(struct Notifier* n) +static void piix4_pm_machine_ready(Notifier *n, void *opaque) { PIIX4PMState *s = container_of(n, PIIX4PMState, machine_ready); uint8_t *pci_conf; diff --git a/hw/fw_cfg.c b/hw/fw_cfg.c index 85c8c3c7bf..34e7526d59 100644 --- a/hw/fw_cfg.c +++ b/hw/fw_cfg.c @@ -316,7 +316,7 @@ int fw_cfg_add_file(FWCfgState *s, const char *filename, uint8_t *data, return 1; } -static void fw_cfg_machine_ready(struct Notifier* n) +static void fw_cfg_machine_ready(struct Notifier *n, void *data) { uint32_t len; FWCfgState *s = container_of(n, FWCfgState, machine_ready); diff --git a/input.c b/input.c index f0a02e783d..310bad58fd 100644 --- a/input.c +++ b/input.c @@ -59,7 +59,7 @@ static void check_mode_change(void) if (is_absolute != current_is_absolute || has_absolute != current_has_absolute) { - notifier_list_notify(&mouse_mode_notifiers); + notifier_list_notify(&mouse_mode_notifiers, NULL); } current_is_absolute = is_absolute; diff --git a/migration.c b/migration.c index af3a1f2702..2a15b98db9 100644 --- a/migration.c +++ b/migration.c @@ -124,7 +124,7 @@ int do_migrate(Monitor *mon, const QDict *qdict, QObject **ret_data) } current_migration = s; - notifier_list_notify(&migration_state_notifiers); + notifier_list_notify(&migration_state_notifiers, NULL); return 0; } @@ -276,7 +276,7 @@ void migrate_fd_error(FdMigrationState *s) { DPRINTF("setting error state\n"); s->state = MIG_STATE_ERROR; - notifier_list_notify(&migration_state_notifiers); + notifier_list_notify(&migration_state_notifiers, NULL); migrate_fd_cleanup(s); } @@ -334,7 +334,7 @@ ssize_t migrate_fd_put_buffer(void *opaque, const void *data, size_t size) monitor_resume(s->mon); } s->state = MIG_STATE_ERROR; - notifier_list_notify(&migration_state_notifiers); + notifier_list_notify(&migration_state_notifiers, NULL); } return ret; @@ -395,7 +395,7 @@ void migrate_fd_put_ready(void *opaque) state = MIG_STATE_ERROR; } s->state = state; - notifier_list_notify(&migration_state_notifiers); + notifier_list_notify(&migration_state_notifiers, NULL); } } @@ -415,7 +415,7 @@ void migrate_fd_cancel(MigrationState *mig_state) DPRINTF("cancelling migration\n"); s->state = MIG_STATE_CANCELLED; - notifier_list_notify(&migration_state_notifiers); + notifier_list_notify(&migration_state_notifiers, NULL); qemu_savevm_state_cancel(s->mon, s->file); migrate_fd_cleanup(s); @@ -429,7 +429,7 @@ void migrate_fd_release(MigrationState *mig_state) if (s->state == MIG_STATE_ACTIVE) { s->state = MIG_STATE_CANCELLED; - notifier_list_notify(&migration_state_notifiers); + notifier_list_notify(&migration_state_notifiers, NULL); migrate_fd_cleanup(s); } qemu_free(s); diff --git a/notify.c b/notify.c index bcd3fc532f..a6bac1f783 100644 --- a/notify.c +++ b/notify.c @@ -29,11 +29,11 @@ void notifier_list_remove(NotifierList *list, Notifier *notifier) QTAILQ_REMOVE(&list->notifiers, notifier, node); } -void notifier_list_notify(NotifierList *list) +void notifier_list_notify(NotifierList *list, void *data) { Notifier *notifier, *next; QTAILQ_FOREACH_SAFE(notifier, &list->notifiers, node, next) { - notifier->notify(notifier); + notifier->notify(notifier, data); } } diff --git a/notify.h b/notify.h index b40522f582..54fc57cec1 100644 --- a/notify.h +++ b/notify.h @@ -20,7 +20,7 @@ typedef struct Notifier Notifier; struct Notifier { - void (*notify)(Notifier *notifier); + void (*notify)(Notifier *notifier, void *data); QTAILQ_ENTRY(Notifier) node; }; @@ -38,6 +38,6 @@ void notifier_list_add(NotifierList *list, Notifier *notifier); void notifier_list_remove(NotifierList *list, Notifier *notifier); -void notifier_list_notify(NotifierList *list); +void notifier_list_notify(NotifierList *list, void *data); #endif diff --git a/ui/sdl.c b/ui/sdl.c index f2bd4a035b..6dbc5cb0ed 100644 --- a/ui/sdl.c +++ b/ui/sdl.c @@ -481,7 +481,7 @@ static void sdl_grab_end(void) sdl_update_caption(); } -static void sdl_mouse_mode_change(Notifier *notify) +static void sdl_mouse_mode_change(Notifier *notify, void *data) { if (kbd_mouse_is_absolute()) { if (!absolute_enabled) { diff --git a/ui/spice-core.c b/ui/spice-core.c index 1100417698..3d77c01448 100644 --- a/ui/spice-core.c +++ b/ui/spice-core.c @@ -416,7 +416,7 @@ void do_info_spice(Monitor *mon, QObject **ret_data) *ret_data = QOBJECT(server); } -static void migration_state_notifier(Notifier *notifier) +static void migration_state_notifier(Notifier *notifier, void *data) { int state = get_migration_state(); diff --git a/ui/spice-input.c b/ui/spice-input.c index 37c8578a2c..75abf5fbe9 100644 --- a/ui/spice-input.c +++ b/ui/spice-input.c @@ -178,7 +178,7 @@ static const SpiceTabletInterface tablet_interface = { .buttons = tablet_buttons, }; -static void mouse_mode_notifier(Notifier *notifier) +static void mouse_mode_notifier(Notifier *notifier, void *data) { QemuSpicePointer *pointer = container_of(notifier, QemuSpicePointer, mouse_mode); bool is_absolute = kbd_mouse_is_absolute(); @@ -213,5 +213,5 @@ void qemu_spice_input_init(void) pointer->absolute = false; pointer->mouse_mode.notify = mouse_mode_notifier; qemu_add_mouse_mode_change_notifier(&pointer->mouse_mode); - mouse_mode_notifier(&pointer->mouse_mode); + mouse_mode_notifier(&pointer->mouse_mode, NULL); } diff --git a/ui/vnc.c b/ui/vnc.c index 8602adc68b..4425180a84 100644 --- a/ui/vnc.c +++ b/ui/vnc.c @@ -1346,7 +1346,7 @@ static void client_cut_text(VncState *vs, size_t len, uint8_t *text) { } -static void check_pointer_type_change(Notifier *notifier) +static void check_pointer_type_change(Notifier *notifier, void *data) { VncState *vs = container_of(notifier, VncState, mouse_mode_notifier); int absolute = kbd_mouse_is_absolute(); @@ -1769,7 +1769,7 @@ static void set_encodings(VncState *vs, int32_t *encodings, size_t n_encodings) } } vnc_desktop_resize(vs); - check_pointer_type_change(&vs->mouse_mode_notifier); + check_pointer_type_change(&vs->mouse_mode_notifier, NULL); } static void set_pixel_conversion(VncState *vs) diff --git a/usb-linux.c b/usb-linux.c index 1a2deb35c9..53cc5fc00e 100644 --- a/usb-linux.c +++ b/usb-linux.c @@ -1260,7 +1260,7 @@ static int usb_host_close(USBHostDevice *dev) return 0; } -static void usb_host_exit_notifier(struct Notifier* n) +static void usb_host_exit_notifier(struct Notifier *n, void *data) { USBHostDevice *s = container_of(n, USBHostDevice, exit); diff --git a/vl.c b/vl.c index 99d92012c1..4b6688b553 100644 --- a/vl.c +++ b/vl.c @@ -2009,7 +2009,7 @@ void qemu_remove_exit_notifier(Notifier *notify) static void qemu_run_exit_notifiers(void) { - notifier_list_notify(&exit_notifiers); + notifier_list_notify(&exit_notifiers, NULL); } void qemu_add_machine_init_done_notifier(Notifier *notify) @@ -2019,7 +2019,7 @@ void qemu_add_machine_init_done_notifier(Notifier *notify) static void qemu_run_machine_init_done_notifiers(void) { - notifier_list_notify(&machine_init_done_notifiers); + notifier_list_notify(&machine_init_done_notifiers, NULL); } static const QEMUOption *lookup_opt(int argc, char **argv, diff --git a/xen-all.c b/xen-all.c index 8105c83683..167bed6dbd 100644 --- a/xen-all.c +++ b/xen-all.c @@ -839,7 +839,7 @@ static void xen_vm_change_state_handler(void *opaque, int running, int reason) } } -static void xen_exit_notifier(Notifier *n) +static void xen_exit_notifier(Notifier *n, void *data) { XenIOState *state = container_of(n, XenIOState, exit); From 691a0c9c9b71360271220c12f20a7238bc302503 Mon Sep 17 00:00:00 2001 From: Jan Kiszka Date: Mon, 20 Jun 2011 14:06:27 +0200 Subject: [PATCH 047/230] qemu-timer: Introduce clock reset notifier QEMU_CLOCK_HOST is based on the system time which may jump backward in case the admin or NTP adjusts it. RTC emulations and other device models can suffer in this case as timers will stall for the period the clock was tuned back. This adds a detection mechanism that checks on every host clock readout if the new time is before the last result. If that is the case a notifier list is informed. Device models interested in this event can register a notifier with the clock. Signed-off-by: Jan Kiszka Signed-off-by: Anthony Liguori --- qemu-timer.c | 29 ++++++++++++++++++++++++++++- qemu-timer.h | 5 +++++ 2 files changed, 33 insertions(+), 1 deletion(-) diff --git a/qemu-timer.c b/qemu-timer.c index 67c2974959..f95374c7c1 100644 --- a/qemu-timer.c +++ b/qemu-timer.c @@ -150,6 +150,9 @@ struct QEMUClock { int enabled; QEMUTimer *warp_timer; + + NotifierList reset_notifiers; + int64_t last; }; struct QEMUTimer { @@ -376,9 +379,15 @@ static QEMUTimer *active_timers[QEMU_NUM_CLOCKS]; static QEMUClock *qemu_new_clock(int type) { QEMUClock *clock; + clock = qemu_mallocz(sizeof(QEMUClock)); clock->type = type; clock->enabled = 1; + notifier_list_init(&clock->reset_notifiers); + /* required to detect & report backward jumps */ + if (type == QEMU_CLOCK_HOST) { + clock->last = get_clock_realtime(); + } return clock; } @@ -593,6 +602,8 @@ static void qemu_run_timers(QEMUClock *clock) int64_t qemu_get_clock_ns(QEMUClock *clock) { + int64_t now, last; + switch(clock->type) { case QEMU_CLOCK_REALTIME: return get_clock(); @@ -604,10 +615,26 @@ int64_t qemu_get_clock_ns(QEMUClock *clock) return cpu_get_clock(); } case QEMU_CLOCK_HOST: - return get_clock_realtime(); + now = get_clock_realtime(); + last = clock->last; + clock->last = now; + if (now < last) { + notifier_list_notify(&clock->reset_notifiers, &now); + } + return now; } } +void qemu_register_clock_reset_notifier(QEMUClock *clock, Notifier *notifier) +{ + notifier_list_add(&clock->reset_notifiers, notifier); +} + +void qemu_unregister_clock_reset_notifier(QEMUClock *clock, Notifier *notifier) +{ + notifier_list_remove(&clock->reset_notifiers, notifier); +} + void init_clocks(void) { rt_clock = qemu_new_clock(QEMU_CLOCK_REALTIME); diff --git a/qemu-timer.h b/qemu-timer.h index 06cbe20914..0a43469847 100644 --- a/qemu-timer.h +++ b/qemu-timer.h @@ -2,6 +2,7 @@ #define QEMU_TIMER_H #include "qemu-common.h" +#include "notify.h" #include #include @@ -40,6 +41,10 @@ int64_t qemu_get_clock_ns(QEMUClock *clock); void qemu_clock_enable(QEMUClock *clock, int enabled); void qemu_clock_warp(QEMUClock *clock); +void qemu_register_clock_reset_notifier(QEMUClock *clock, Notifier *notifier); +void qemu_unregister_clock_reset_notifier(QEMUClock *clock, + Notifier *notifier); + QEMUTimer *qemu_new_timer(QEMUClock *clock, int scale, QEMUTimerCB *cb, void *opaque); void qemu_free_timer(QEMUTimer *ts); From 17604dac28b2410c021a4a52dcfa58e8803dfb24 Mon Sep 17 00:00:00 2001 From: Jan Kiszka Date: Mon, 20 Jun 2011 14:06:28 +0200 Subject: [PATCH 048/230] mc146818rtc: Handle host clock resets Make use of the new clock reset notifier to update the RTC whenever rtc_clock is the host clock and that happens to jump backward. This avoids that the RTC stalls for the period the host clock was set back. Signed-off-by: Jan Kiszka Signed-off-by: Anthony Liguori --- hw/mc146818rtc.c | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) diff --git a/hw/mc146818rtc.c b/hw/mc146818rtc.c index 1c9a706b1b..feb3b25acd 100644 --- a/hw/mc146818rtc.c +++ b/hw/mc146818rtc.c @@ -99,6 +99,7 @@ typedef struct RTCState { QEMUTimer *coalesced_timer; QEMUTimer *second_timer; QEMUTimer *second_timer2; + Notifier clock_reset_notifier; } RTCState; static void rtc_set_time(RTCState *s); @@ -572,6 +573,22 @@ static const VMStateDescription vmstate_rtc = { } }; +static void rtc_notify_clock_reset(Notifier *notifier, void *data) +{ + RTCState *s = container_of(notifier, RTCState, clock_reset_notifier); + int64_t now = *(int64_t *)data; + + rtc_set_date_from_host(&s->dev); + s->next_second_time = now + (get_ticks_per_sec() * 99) / 100; + qemu_mod_timer(s->second_timer2, s->next_second_time); + rtc_timer_update(s, now); +#ifdef TARGET_I386 + if (rtc_td_hack) { + rtc_coalesced_timer_update(s); + } +#endif +} + static void rtc_reset(void *opaque) { RTCState *s = opaque; @@ -608,6 +625,9 @@ static int rtc_initfn(ISADevice *dev) s->second_timer = qemu_new_timer_ns(rtc_clock, rtc_update_second, s); s->second_timer2 = qemu_new_timer_ns(rtc_clock, rtc_update_second2, s); + s->clock_reset_notifier.notify = rtc_notify_clock_reset; + qemu_register_clock_reset_notifier(rtc_clock, &s->clock_reset_notifier); + s->next_second_time = qemu_get_clock_ns(rtc_clock) + (get_ticks_per_sec() * 99) / 100; qemu_mod_timer(s->second_timer2, s->next_second_time); From d25f89c9e91d6c46b85969922411a211a6347a7d Mon Sep 17 00:00:00 2001 From: Jan Kiszka Date: Fri, 17 Jun 2011 11:25:49 +0200 Subject: [PATCH 049/230] Register Linux dyntick timer as per-thread signal Derived from kvm-tool patch http://thread.gmane.org/gmane.comp.emulators.kvm.devel/74309 Ingo Molnar pointed out that sending the timer signal to the whole process, just blocking it everywhere, is suboptimal with an increasing number of threads. QEMU is also using this pattern so far. Linux provides a (non-portable) way to restrict the signal to a single thread: We can use SIGEV_THREAD_ID unless we are forced to emulate signalfd via an additional thread. That case could theoretically be optimized as well, but it doesn't look worth bothering. Reviewed-by: Richard Henderson Signed-off-by: Jan Kiszka Signed-off-by: Anthony Liguori --- compatfd.c | 11 +++++++++++ compatfd.h | 1 + qemu-timer.c | 8 ++++++++ 3 files changed, 20 insertions(+) diff --git a/compatfd.c b/compatfd.c index 41586ceaea..31654c62a6 100644 --- a/compatfd.c +++ b/compatfd.c @@ -115,3 +115,14 @@ int qemu_signalfd(const sigset_t *mask) return qemu_signalfd_compat(mask); } + +bool qemu_signalfd_available(void) +{ +#ifdef CONFIG_SIGNALFD + errno = 0; + syscall(SYS_signalfd, -1, NULL, _NSIG / 8); + return errno != ENOSYS; +#else + return false; +#endif +} diff --git a/compatfd.h b/compatfd.h index fc3791520f..6b04877b97 100644 --- a/compatfd.h +++ b/compatfd.h @@ -39,5 +39,6 @@ struct qemu_signalfd_siginfo { }; int qemu_signalfd(const sigset_t *mask); +bool qemu_signalfd_available(void); #endif diff --git a/qemu-timer.c b/qemu-timer.c index f95374c7c1..30e8f1272e 100644 --- a/qemu-timer.c +++ b/qemu-timer.c @@ -831,6 +831,8 @@ static int64_t qemu_next_alarm_deadline(void) #if defined(__linux__) +#include "compatfd.h" + static int dynticks_start_timer(struct qemu_alarm_timer *t) { struct sigevent ev; @@ -850,6 +852,12 @@ static int dynticks_start_timer(struct qemu_alarm_timer *t) memset(&ev, 0, sizeof(ev)); ev.sigev_value.sival_int = 0; ev.sigev_notify = SIGEV_SIGNAL; +#ifdef SIGEV_THREAD_ID + if (qemu_signalfd_available()) { + ev.sigev_notify = SIGEV_THREAD_ID; + ev._sigev_un._tid = qemu_get_thread_id(); + } +#endif /* SIGEV_THREAD_ID */ ev.sigev_signo = SIGALRM; if (timer_create(CLOCK_REALTIME, &ev, &host_timer)) { From 8e31bf388e56e5babd9600b110a94381d1be07b1 Mon Sep 17 00:00:00 2001 From: Matthew Fernandez Date: Sun, 26 Jun 2011 12:21:35 +1000 Subject: [PATCH 050/230] Correct spelling of licensed Correct typos of "licenced" to "licensed". Reviewed-by: Stefan Weil Reviewed-by: Andreas F=E4rber Signed-off-by: Matthew Fernandez Signed-off-by: Anthony Liguori --- hw/a9mpcore.c | 2 +- hw/an5206.c | 2 +- hw/arm-misc.h | 2 +- hw/arm11mpcore.c | 2 +- hw/arm_boot.c | 2 +- hw/arm_gic.c | 2 +- hw/arm_pic.c | 2 +- hw/arm_sysctl.c | 2 +- hw/arm_timer.c | 2 +- hw/armv7m_nvic.c | 2 +- hw/bitbang_i2c.c | 2 +- hw/ccid-card-emulated.c | 2 +- hw/ccid.h | 2 +- hw/ds1338.c | 2 +- hw/dummy_m68k.c | 2 +- hw/i2c.c | 2 +- hw/integratorcp.c | 2 +- hw/lan9118.c | 2 +- hw/lsi53c895a.c | 2 +- hw/marvell_88w8618_audio.c | 2 +- hw/mcf5206.c | 2 +- hw/mcf5208.c | 2 +- hw/mcf_fec.c | 2 +- hw/mcf_intc.c | 2 +- hw/mcf_uart.c | 2 +- hw/mpcore.c | 2 +- hw/musicpal.c | 2 +- hw/pl011.c | 2 +- hw/pl022.c | 2 +- hw/pl050.c | 2 +- hw/pl061.c | 2 +- hw/pl080.c | 2 +- hw/pl110.c | 2 +- hw/pl110_template.h | 2 +- hw/pl181.c | 2 +- hw/pl190.c | 2 +- hw/ptimer.c | 2 +- hw/pxa.h | 2 +- hw/pxa2xx.c | 2 +- hw/pxa2xx_dma.c | 2 +- hw/pxa2xx_pic.c | 2 +- hw/pxa2xx_timer.c | 2 +- hw/realview.c | 2 +- hw/realview_gic.c | 2 +- hw/scsi-disk.c | 2 +- hw/scsi-generic.c | 2 +- hw/sh_intc.c | 2 +- hw/sh_timer.c | 2 +- hw/smbus.c | 2 +- hw/smc91c111.c | 2 +- hw/ssd0303.c | 2 +- hw/ssd0323.c | 2 +- hw/ssi-sd.c | 2 +- hw/ssi.c | 2 +- hw/stellaris.c | 2 +- hw/stellaris_enet.c | 2 +- hw/stellaris_input.c | 2 +- hw/usb-msd.c | 2 +- hw/usb-serial.c | 2 +- hw/versatile_pci.c | 2 +- hw/versatilepb.c | 2 +- softmmu-semi.h | 2 +- target-arm/neon_helper.c | 2 +- target-arm/op_addsub.h | 2 +- 64 files changed, 64 insertions(+), 64 deletions(-) diff --git a/hw/a9mpcore.c b/hw/a9mpcore.c index b5e5328395..6f108f4ce2 100644 --- a/hw/a9mpcore.c +++ b/hw/a9mpcore.c @@ -4,7 +4,7 @@ * Copyright (c) 2009 CodeSourcery. * Written by Paul Brook * - * This code is licenced under the GPL. + * This code is licensed under the GPL. */ /* 64 external IRQ lines. */ diff --git a/hw/an5206.c b/hw/an5206.c index 42a0163fbd..04ca420a90 100644 --- a/hw/an5206.c +++ b/hw/an5206.c @@ -3,7 +3,7 @@ * * Copyright (c) 2007 CodeSourcery. * - * This code is licenced under the GPL + * This code is licensed under the GPL */ #include "hw.h" diff --git a/hw/arm-misc.h b/hw/arm-misc.h index 9aeeaea759..f8a747289b 100644 --- a/hw/arm-misc.h +++ b/hw/arm-misc.h @@ -4,7 +4,7 @@ * Copyright (c) 2006 CodeSourcery. * Written by Paul Brook * - * This code is licenced under the LGPL. + * This code is licensed under the LGPL. * */ diff --git a/hw/arm11mpcore.c b/hw/arm11mpcore.c index 3bbd8856cf..b47707f7bb 100644 --- a/hw/arm11mpcore.c +++ b/hw/arm11mpcore.c @@ -4,7 +4,7 @@ * Copyright (c) 2006-2007 CodeSourcery. * Written by Paul Brook * - * This code is licenced under the GPL. + * This code is licensed under the GPL. */ /* ??? The MPCore TRM says the on-chip controller has 224 external IRQ lines diff --git a/hw/arm_boot.c b/hw/arm_boot.c index e0215768b1..215d5dec64 100644 --- a/hw/arm_boot.c +++ b/hw/arm_boot.c @@ -4,7 +4,7 @@ * Copyright (c) 2006-2007 CodeSourcery. * Written by Paul Brook * - * This code is licenced under the GPL. + * This code is licensed under the GPL. */ #include "hw.h" diff --git a/hw/arm_gic.c b/hw/arm_gic.c index 0e934ecd64..fb07314d52 100644 --- a/hw/arm_gic.c +++ b/hw/arm_gic.c @@ -4,7 +4,7 @@ * Copyright (c) 2006-2007 CodeSourcery. * Written by Paul Brook * - * This code is licenced under the GPL. + * This code is licensed under the GPL. */ /* This file contains implementation code for the RealView EB interrupt diff --git a/hw/arm_pic.c b/hw/arm_pic.c index f44568cebb..985148a380 100644 --- a/hw/arm_pic.c +++ b/hw/arm_pic.c @@ -4,7 +4,7 @@ * Copyright (c) 2006 CodeSourcery. * Written by Paul Brook * - * This code is licenced under the LGPL + * This code is licensed under the LGPL */ #include "hw.h" diff --git a/hw/arm_sysctl.c b/hw/arm_sysctl.c index 9225b588b8..fd0c8bc3d6 100644 --- a/hw/arm_sysctl.c +++ b/hw/arm_sysctl.c @@ -4,7 +4,7 @@ * Copyright (c) 2006-2007 CodeSourcery. * Written by Paul Brook * - * This code is licenced under the GPL. + * This code is licensed under the GPL. */ #include "hw.h" diff --git a/hw/arm_timer.c b/hw/arm_timer.c index dac9e70750..fd9448f055 100644 --- a/hw/arm_timer.c +++ b/hw/arm_timer.c @@ -4,7 +4,7 @@ * Copyright (c) 2005-2006 CodeSourcery. * Written by Paul Brook * - * This code is licenced under the GPL. + * This code is licensed under the GPL. */ #include "sysbus.h" diff --git a/hw/armv7m_nvic.c b/hw/armv7m_nvic.c index d06eec9b39..1df8d4db45 100644 --- a/hw/armv7m_nvic.c +++ b/hw/armv7m_nvic.c @@ -4,7 +4,7 @@ * Copyright (c) 2006-2007 CodeSourcery. * Written by Paul Brook * - * This code is licenced under the GPL. + * This code is licensed under the GPL. * * The ARMv7M System controller is fairly tightly tied in with the * NVIC. Much of that is also implemented here. diff --git a/hw/bitbang_i2c.c b/hw/bitbang_i2c.c index 2937b5c4a1..53e9c5c4c4 100644 --- a/hw/bitbang_i2c.c +++ b/hw/bitbang_i2c.c @@ -4,7 +4,7 @@ * * Copyright (c) 2008 Jan Kiszka * - * This code is licenced under the GNU GPL v2. + * This code is licensed under the GNU GPL v2. */ #include "hw.h" #include "bitbang_i2c.h" diff --git a/hw/ccid-card-emulated.c b/hw/ccid-card-emulated.c index 0b0718426d..4762e85116 100644 --- a/hw/ccid-card-emulated.c +++ b/hw/ccid-card-emulated.c @@ -4,7 +4,7 @@ * Copyright (c) 2011 Red Hat. * Written by Alon Levy. * - * This code is licenced under the GNU LGPL, version 2 or later. + * This code is licensed under the GNU LGPL, version 2 or later. */ /* diff --git a/hw/ccid.h b/hw/ccid.h index d3e03716c9..9e3abe1b4c 100644 --- a/hw/ccid.h +++ b/hw/ccid.h @@ -4,7 +4,7 @@ * Copyright (c) 2011 Red Hat. * Written by Alon Levy. * - * This code is licenced under the GNU LGPL, version 2 or later. + * This code is licensed under the GNU LGPL, version 2 or later. */ #ifndef CCID_H diff --git a/hw/ds1338.c b/hw/ds1338.c index 6f5ae5e6c1..3522af5b5a 100644 --- a/hw/ds1338.c +++ b/hw/ds1338.c @@ -4,7 +4,7 @@ * Copyright (c) 2009 CodeSourcery. * Written by Paul Brook * - * This code is licenced under the GNU GPL v2. + * This code is licensed under the GNU GPL v2. */ #include "i2c.h" diff --git a/hw/dummy_m68k.c b/hw/dummy_m68k.c index cec1cc8e82..eed9e3843c 100644 --- a/hw/dummy_m68k.c +++ b/hw/dummy_m68k.c @@ -3,7 +3,7 @@ * * Copyright (c) 2007 CodeSourcery. * - * This code is licenced under the GPL + * This code is licensed under the GPL */ #include "hw.h" diff --git a/hw/i2c.c b/hw/i2c.c index f80d12db4f..49b9ecb8b6 100644 --- a/hw/i2c.c +++ b/hw/i2c.c @@ -4,7 +4,7 @@ * Copyright (c) 2007 CodeSourcery. * Written by Paul Brook * - * This code is licenced under the LGPL. + * This code is licensed under the LGPL. */ #include "i2c.h" diff --git a/hw/integratorcp.c b/hw/integratorcp.c index a6c27be82c..281410899f 100644 --- a/hw/integratorcp.c +++ b/hw/integratorcp.c @@ -4,7 +4,7 @@ * Copyright (c) 2005-2007 CodeSourcery. * Written by Paul Brook * - * This code is licenced under the GPL + * This code is licensed under the GPL */ #include "sysbus.h" diff --git a/hw/lan9118.c b/hw/lan9118.c index 3f3c05df4c..73a8661ca3 100644 --- a/hw/lan9118.c +++ b/hw/lan9118.c @@ -4,7 +4,7 @@ * Copyright (c) 2009 CodeSourcery, LLC. * Written by Paul Brook * - * This code is licenced under the GNU GPL v2 + * This code is licensed under the GNU GPL v2 */ #include "sysbus.h" diff --git a/hw/lsi53c895a.c b/hw/lsi53c895a.c index 69eec1d2fe..e9904c49d9 100644 --- a/hw/lsi53c895a.c +++ b/hw/lsi53c895a.c @@ -4,7 +4,7 @@ * Copyright (c) 2006 CodeSourcery. * Written by Paul Brook * - * This code is licenced under the LGPL. + * This code is licensed under the LGPL. */ /* ??? Need to check if the {read,write}[wl] routines work properly on diff --git a/hw/marvell_88w8618_audio.c b/hw/marvell_88w8618_audio.c index 3eff925b0e..f8c5242867 100644 --- a/hw/marvell_88w8618_audio.c +++ b/hw/marvell_88w8618_audio.c @@ -4,7 +4,7 @@ * * Copyright (c) 2008 Jan Kiszka * - * This code is licenced under the GNU GPL v2. + * This code is licensed under the GNU GPL v2. */ #include "sysbus.h" #include "hw.h" diff --git a/hw/mcf5206.c b/hw/mcf5206.c index 2a618d4446..fce282d98b 100644 --- a/hw/mcf5206.c +++ b/hw/mcf5206.c @@ -3,7 +3,7 @@ * * Copyright (c) 2007 CodeSourcery. * - * This code is licenced under the GPL + * This code is licensed under the GPL */ #include "hw.h" #include "mcf.h" diff --git a/hw/mcf5208.c b/hw/mcf5208.c index 17a692d4a3..78fbc5f232 100644 --- a/hw/mcf5208.c +++ b/hw/mcf5208.c @@ -3,7 +3,7 @@ * * Copyright (c) 2007 CodeSourcery. * - * This code is licenced under the GPL + * This code is licensed under the GPL */ #include "hw.h" #include "mcf.h" diff --git a/hw/mcf_fec.c b/hw/mcf_fec.c index 5477e0e159..748eb5906b 100644 --- a/hw/mcf_fec.c +++ b/hw/mcf_fec.c @@ -3,7 +3,7 @@ * * Copyright (c) 2007 CodeSourcery. * - * This code is licenced under the GPL + * This code is licensed under the GPL */ #include "hw.h" #include "net.h" diff --git a/hw/mcf_intc.c b/hw/mcf_intc.c index ac04295198..6cb0a09b7f 100644 --- a/hw/mcf_intc.c +++ b/hw/mcf_intc.c @@ -3,7 +3,7 @@ * * Copyright (c) 2007 CodeSourcery. * - * This code is licenced under the GPL + * This code is licensed under the GPL */ #include "hw.h" #include "mcf.h" diff --git a/hw/mcf_uart.c b/hw/mcf_uart.c index db57096af2..905e116de6 100644 --- a/hw/mcf_uart.c +++ b/hw/mcf_uart.c @@ -3,7 +3,7 @@ * * Copyright (c) 2007 CodeSourcery. * - * This code is licenced under the GPL + * This code is licensed under the GPL */ #include "hw.h" #include "mcf.h" diff --git a/hw/mpcore.c b/hw/mpcore.c index 379065a3eb..d778507516 100644 --- a/hw/mpcore.c +++ b/hw/mpcore.c @@ -4,7 +4,7 @@ * Copyright (c) 2006-2007 CodeSourcery. * Written by Paul Brook * - * This code is licenced under the GPL. + * This code is licensed under the GPL. */ #include "sysbus.h" diff --git a/hw/musicpal.c b/hw/musicpal.c index 52b2931d15..63dd391176 100644 --- a/hw/musicpal.c +++ b/hw/musicpal.c @@ -3,7 +3,7 @@ * * Copyright (c) 2008 Jan Kiszka * - * This code is licenced under the GNU GPL v2. + * This code is licensed under the GNU GPL v2. */ #include "sysbus.h" diff --git a/hw/pl011.c b/hw/pl011.c index 3b94b14cb9..997ce848f8 100644 --- a/hw/pl011.c +++ b/hw/pl011.c @@ -4,7 +4,7 @@ * Copyright (c) 2006 CodeSourcery. * Written by Paul Brook * - * This code is licenced under the GPL. + * This code is licensed under the GPL. */ #include "sysbus.h" diff --git a/hw/pl022.c b/hw/pl022.c index 00e494a0de..9a1cb710f3 100644 --- a/hw/pl022.c +++ b/hw/pl022.c @@ -4,7 +4,7 @@ * Copyright (c) 2007 CodeSourcery. * Written by Paul Brook * - * This code is licenced under the GPL. + * This code is licensed under the GPL. */ #include "sysbus.h" diff --git a/hw/pl050.c b/hw/pl050.c index b155cc07b6..f7fa2e253c 100644 --- a/hw/pl050.c +++ b/hw/pl050.c @@ -4,7 +4,7 @@ * Copyright (c) 2006-2007 CodeSourcery. * Written by Paul Brook * - * This code is licenced under the GPL. + * This code is licensed under the GPL. */ #include "sysbus.h" diff --git a/hw/pl061.c b/hw/pl061.c index 372dfc2da2..79e5c53e89 100644 --- a/hw/pl061.c +++ b/hw/pl061.c @@ -5,7 +5,7 @@ * Copyright (c) 2007 CodeSourcery. * Written by Paul Brook * - * This code is licenced under the GPL. + * This code is licensed under the GPL. */ #include "sysbus.h" diff --git a/hw/pl080.c b/hw/pl080.c index dd8139ba96..5ba3b0859b 100644 --- a/hw/pl080.c +++ b/hw/pl080.c @@ -4,7 +4,7 @@ * Copyright (c) 2006 CodeSourcery. * Written by Paul Brook * - * This code is licenced under the GPL. + * This code is licensed under the GPL. */ #include "sysbus.h" diff --git a/hw/pl110.c b/hw/pl110.c index 06d2dfada6..62aba17ad4 100644 --- a/hw/pl110.c +++ b/hw/pl110.c @@ -4,7 +4,7 @@ * Copyright (c) 2005-2009 CodeSourcery. * Written by Paul Brook * - * This code is licenced under the GNU LGPL + * This code is licensed under the GNU LGPL */ #include "sysbus.h" diff --git a/hw/pl110_template.h b/hw/pl110_template.h index b3c9077dcc..d303336786 100644 --- a/hw/pl110_template.h +++ b/hw/pl110_template.h @@ -4,7 +4,7 @@ * Copyright (c) 2005 CodeSourcery, LLC. * Written by Paul Brook * - * This code is licenced under the GNU LGPL + * This code is licensed under the GNU LGPL * * Framebuffer format conversion routines. */ diff --git a/hw/pl181.c b/hw/pl181.c index 6bc79f5f7a..0943c09eca 100644 --- a/hw/pl181.c +++ b/hw/pl181.c @@ -4,7 +4,7 @@ * Copyright (c) 2007 CodeSourcery. * Written by Paul Brook * - * This code is licenced under the GPL. + * This code is licensed under the GPL. */ #include "blockdev.h" diff --git a/hw/pl190.c b/hw/pl190.c index 75f2ba1966..8dc7e42861 100644 --- a/hw/pl190.c +++ b/hw/pl190.c @@ -4,7 +4,7 @@ * Copyright (c) 2006 CodeSourcery. * Written by Paul Brook * - * This code is licenced under the GPL. + * This code is licensed under the GPL. */ #include "sysbus.h" diff --git a/hw/ptimer.c b/hw/ptimer.c index 47964a67e1..6f13ce92fc 100644 --- a/hw/ptimer.c +++ b/hw/ptimer.c @@ -3,7 +3,7 @@ * * Copyright (c) 2007 CodeSourcery. * - * This code is licenced under the GNU LGPL. + * This code is licensed under the GNU LGPL. */ #include "hw.h" #include "qemu-timer.h" diff --git a/hw/pxa.h b/hw/pxa.h index d982f00c5d..859fc676e4 100644 --- a/hw/pxa.h +++ b/hw/pxa.h @@ -4,7 +4,7 @@ * Copyright (c) 2006 Openedhand Ltd. * Written by Andrzej Zaborowski * - * This code is licenced under the GNU GPL v2. + * This code is licensed under the GNU GPL v2. */ #ifndef PXA_H # define PXA_H "pxa.h" diff --git a/hw/pxa2xx.c b/hw/pxa2xx.c index ac5d95d718..cf9311014d 100644 --- a/hw/pxa2xx.c +++ b/hw/pxa2xx.c @@ -4,7 +4,7 @@ * Copyright (c) 2006 Openedhand Ltd. * Written by Andrzej Zaborowski * - * This code is licenced under the GPL. + * This code is licensed under the GPL. */ #include "sysbus.h" diff --git a/hw/pxa2xx_dma.c b/hw/pxa2xx_dma.c index a67498b2bc..599581e266 100644 --- a/hw/pxa2xx_dma.c +++ b/hw/pxa2xx_dma.c @@ -5,7 +5,7 @@ * Copyright (c) 2006 Thorsten Zitterell * Written by Andrzej Zaborowski * - * This code is licenced under the GPL. + * This code is licensed under the GPL. */ #include "hw.h" diff --git a/hw/pxa2xx_pic.c b/hw/pxa2xx_pic.c index e9a536102b..bdd82e6bf2 100644 --- a/hw/pxa2xx_pic.c +++ b/hw/pxa2xx_pic.c @@ -5,7 +5,7 @@ * Copyright (c) 2006 Thorsten Zitterell * Written by Andrzej Zaborowski * - * This code is licenced under the GPL. + * This code is licensed under the GPL. */ #include "hw.h" diff --git a/hw/pxa2xx_timer.c b/hw/pxa2xx_timer.c index f777a21226..4235e42639 100644 --- a/hw/pxa2xx_timer.c +++ b/hw/pxa2xx_timer.c @@ -4,7 +4,7 @@ * Copyright (c) 2006 Openedhand Ltd. * Copyright (c) 2006 Thorsten Zitterell * - * This code is licenced under the GPL. + * This code is licensed under the GPL. */ #include "hw.h" diff --git a/hw/realview.c b/hw/realview.c index 82f3d82d44..94ab900512 100644 --- a/hw/realview.c +++ b/hw/realview.c @@ -4,7 +4,7 @@ * Copyright (c) 2006-2007 CodeSourcery. * Written by Paul Brook * - * This code is licenced under the GPL. + * This code is licensed under the GPL. */ #include "sysbus.h" diff --git a/hw/realview_gic.c b/hw/realview_gic.c index db908b6439..43a2a0d5ed 100644 --- a/hw/realview_gic.c +++ b/hw/realview_gic.c @@ -4,7 +4,7 @@ * Copyright (c) 2006-2007 CodeSourcery. * Written by Paul Brook * - * This code is licenced under the GPL. + * This code is licensed under the GPL. */ #include "sysbus.h" diff --git a/hw/scsi-disk.c b/hw/scsi-disk.c index 05d14ab2fd..f42a5d1f85 100644 --- a/hw/scsi-disk.c +++ b/hw/scsi-disk.c @@ -12,7 +12,7 @@ * 2009-Oct-13 Artyom Tarasenko : implemented the block descriptor in the * MODE SENSE response. * - * This code is licenced under the LGPL. + * This code is licensed under the LGPL. * * Note that this file only handles the SCSI architecture model and device * commands. Emulation of interface/link layer protocols is handled by diff --git a/hw/scsi-generic.c b/hw/scsi-generic.c index 90345a714f..63361b3542 100644 --- a/hw/scsi-generic.c +++ b/hw/scsi-generic.c @@ -7,7 +7,7 @@ * * Written by Laurent Vivier * - * This code is licenced under the LGPL. + * This code is licensed under the LGPL. * */ diff --git a/hw/sh_intc.c b/hw/sh_intc.c index 0734da90f0..c43b99f811 100644 --- a/hw/sh_intc.c +++ b/hw/sh_intc.c @@ -5,7 +5,7 @@ * Based on sh_timer.c and arm_timer.c by Paul Brook * Copyright (c) 2005-2006 CodeSourcery. * - * This code is licenced under the GPL. + * This code is licensed under the GPL. */ #include "sh_intc.h" diff --git a/hw/sh_timer.c b/hw/sh_timer.c index 5eec6b7c14..5df7fb64bc 100644 --- a/hw/sh_timer.c +++ b/hw/sh_timer.c @@ -5,7 +5,7 @@ * Based on arm_timer.c by Paul Brook * Copyright (c) 2005-2006 CodeSourcery. * - * This code is licenced under the GPL. + * This code is licensed under the GPL. */ #include "hw.h" diff --git a/hw/smbus.c b/hw/smbus.c index e464539150..ff027c814f 100644 --- a/hw/smbus.c +++ b/hw/smbus.c @@ -4,7 +4,7 @@ * Copyright (c) 2007 CodeSourcery. * Written by Paul Brook * - * This code is licenced under the LGPL. + * This code is licensed under the LGPL. */ /* TODO: Implement PEC. */ diff --git a/hw/smc91c111.c b/hw/smc91c111.c index 701baafe6c..3a8a85c1f1 100644 --- a/hw/smc91c111.c +++ b/hw/smc91c111.c @@ -4,7 +4,7 @@ * Copyright (c) 2005 CodeSourcery, LLC. * Written by Paul Brook * - * This code is licenced under the GPL + * This code is licensed under the GPL */ #include "sysbus.h" diff --git a/hw/ssd0303.c b/hw/ssd0303.c index b39e2596fb..401fdf592a 100644 --- a/hw/ssd0303.c +++ b/hw/ssd0303.c @@ -4,7 +4,7 @@ * Copyright (c) 2006-2007 CodeSourcery. * Written by Paul Brook * - * This code is licenced under the GPL. + * This code is licensed under the GPL. */ /* The controller can support a variety of different displays, but we only diff --git a/hw/ssd0323.c b/hw/ssd0323.c index 8643961144..1eb3823fed 100644 --- a/hw/ssd0323.c +++ b/hw/ssd0323.c @@ -4,7 +4,7 @@ * Copyright (c) 2006-2007 CodeSourcery. * Written by Paul Brook * - * This code is licenced under the GPL. + * This code is licensed under the GPL. */ /* The controller can support a variety of different displays, but we only diff --git a/hw/ssi-sd.c b/hw/ssi-sd.c index fb4b649279..18dabd64a6 100644 --- a/hw/ssi-sd.c +++ b/hw/ssi-sd.c @@ -4,7 +4,7 @@ * Copyright (c) 2007-2009 CodeSourcery. * Written by Paul Brook * - * This code is licenced under the GNU GPL v2. + * This code is licensed under the GNU GPL v2. */ #include "blockdev.h" diff --git a/hw/ssi.c b/hw/ssi.c index cfe7c072f1..3f4c5f9f06 100644 --- a/hw/ssi.c +++ b/hw/ssi.c @@ -4,7 +4,7 @@ * Copyright (c) 2009 CodeSourcery. * Written by Paul Brook * - * This code is licenced under the GNU GPL v2. + * This code is licensed under the GNU GPL v2. */ #include "ssi.h" diff --git a/hw/stellaris.c b/hw/stellaris.c index b8a7cebd8c..a28093043a 100644 --- a/hw/stellaris.c +++ b/hw/stellaris.c @@ -4,7 +4,7 @@ * Copyright (c) 2006 CodeSourcery. * Written by Paul Brook * - * This code is licenced under the GPL. + * This code is licensed under the GPL. */ #include "sysbus.h" diff --git a/hw/stellaris_enet.c b/hw/stellaris_enet.c index 6a0583a256..12919317ec 100644 --- a/hw/stellaris_enet.c +++ b/hw/stellaris_enet.c @@ -4,7 +4,7 @@ * Copyright (c) 2007 CodeSourcery. * Written by Paul Brook * - * This code is licenced under the GPL. + * This code is licensed under the GPL. */ #include "sysbus.h" #include "net.h" diff --git a/hw/stellaris_input.c b/hw/stellaris_input.c index 06c5f9d955..95604ecded 100644 --- a/hw/stellaris_input.c +++ b/hw/stellaris_input.c @@ -4,7 +4,7 @@ * Copyright (c) 2007 CodeSourcery. * Written by Paul Brook * - * This code is licenced under the GPL. + * This code is licensed under the GPL. */ #include "hw.h" #include "devices.h" diff --git a/hw/usb-msd.c b/hw/usb-msd.c index bfea096893..6391dad108 100644 --- a/hw/usb-msd.c +++ b/hw/usb-msd.c @@ -4,7 +4,7 @@ * Copyright (c) 2006 CodeSourcery. * Written by Paul Brook * - * This code is licenced under the LGPL. + * This code is licensed under the LGPL. */ #include "qemu-common.h" diff --git a/hw/usb-serial.c b/hw/usb-serial.c index 59cb0fb2f7..c69c4374e1 100644 --- a/hw/usb-serial.c +++ b/hw/usb-serial.c @@ -5,7 +5,7 @@ * Copyright (c) 2008 Samuel Thibault * Written by Paul Brook, reused for FTDI by Samuel Thibault * - * This code is licenced under the LGPL. + * This code is licensed under the LGPL. */ #include "qemu-common.h" diff --git a/hw/versatile_pci.c b/hw/versatile_pci.c index 8e75ffccfb..290a9009b2 100644 --- a/hw/versatile_pci.c +++ b/hw/versatile_pci.c @@ -4,7 +4,7 @@ * Copyright (c) 2006-2009 CodeSourcery. * Written by Paul Brook * - * This code is licenced under the LGPL. + * This code is licensed under the LGPL. */ #include "sysbus.h" diff --git a/hw/versatilepb.c b/hw/versatilepb.c index 46b6a3f383..147fe29b61 100644 --- a/hw/versatilepb.c +++ b/hw/versatilepb.c @@ -4,7 +4,7 @@ * Copyright (c) 2005-2007 CodeSourcery. * Written by Paul Brook * - * This code is licenced under the GPL. + * This code is licensed under the GPL. */ #include "sysbus.h" diff --git a/softmmu-semi.h b/softmmu-semi.h index 79278cc763..86a9f8a846 100644 --- a/softmmu-semi.h +++ b/softmmu-semi.h @@ -4,7 +4,7 @@ * * Copyright (c) 2007 CodeSourcery. * - * This code is licenced under the GPL + * This code is licensed under the GPL */ static inline uint32_t softmmu_tget32(CPUState *env, uint32_t addr) diff --git a/target-arm/neon_helper.c b/target-arm/neon_helper.c index 28306279a8..b51e35a30f 100644 --- a/target-arm/neon_helper.c +++ b/target-arm/neon_helper.c @@ -4,7 +4,7 @@ * Copyright (c) 2007, 2008 CodeSourcery. * Written by Paul Brook * - * This code is licenced under the GNU GPL v2. + * This code is licensed under the GNU GPL v2. */ #include #include diff --git a/target-arm/op_addsub.h b/target-arm/op_addsub.h index c02c92adfa..ca4a1893c3 100644 --- a/target-arm/op_addsub.h +++ b/target-arm/op_addsub.h @@ -4,7 +4,7 @@ * Copyright (c) 2007 CodeSourcery. * Written by Paul Brook * - * This code is licenced under the GPL. + * This code is licensed under the GPL. */ #ifdef ARITH_GE From b8095f24f24e50a7d4be33d8a79474aff3324295 Mon Sep 17 00:00:00 2001 From: Anthony Liguori Date: Sat, 23 Jul 2011 11:56:07 -0500 Subject: [PATCH 051/230] Bump version to reflect v0.15.0-rc0 Signed-off-by: Anthony Liguori --- VERSION | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/VERSION b/VERSION index d07c6d0e05..fc6f2940ae 100644 --- a/VERSION +++ b/VERSION @@ -1 +1 @@ -0.14.50 +0.14.90 From 1167bfd63d983eaa4816ee0edb185f98ff070d6d Mon Sep 17 00:00:00 2001 From: Anthony Liguori Date: Sat, 23 Jul 2011 11:57:53 -0500 Subject: [PATCH 052/230] Open 1.0 development branch. Signed-off-by: Anthony Liguori --- VERSION | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/VERSION b/VERSION index fc6f2940ae..e653127e45 100644 --- a/VERSION +++ b/VERSION @@ -1 +1 @@ -0.14.90 +0.15.50 From 4eb36d40da8062400a2e7e27f1038e1252df9ede Mon Sep 17 00:00:00 2001 From: Anthony Liguori Date: Sat, 23 Jul 2011 16:14:37 -0500 Subject: [PATCH 053/230] guest-agent: only enable FSFREEZE when it's supported by the kernel Signed-off-by: Anthony Liguori --- qga/guest-agent-commands.c | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/qga/guest-agent-commands.c b/qga/guest-agent-commands.c index 624972e84c..30c406848f 100644 --- a/qga/guest-agent-commands.c +++ b/qga/guest-agent-commands.c @@ -10,15 +10,17 @@ * See the COPYING file in the top-level directory. */ -#if defined(__linux__) -#define CONFIG_FSFREEZE -#endif - #include -#if defined(CONFIG_FSFREEZE) + +#if defined(__linux__) #include #include + +#if defined(__linux__) && defined(FIFREEZE) +#define CONFIG_FSFREEZE #endif +#endif + #include #include #include "qga/guest-agent-core.h" From 1fc7bd4a86a2bfeafcec29445871eb97469a2699 Mon Sep 17 00:00:00 2001 From: Anthony Liguori Date: Sat, 23 Jul 2011 17:57:47 -0500 Subject: [PATCH 054/230] qemu-ga: remove dependency on gio and gthread As far as I can tell, there isn't a dependency on gthread. Also, the only use of gio was to enable GSocket to accept a unix domain socket. Since GSocket isn't available on OpenSuSE 11.1, let's just remove that dependency. Signed-off-by: Anthony Liguori --- configure | 6 +++--- qemu-ga.c | 35 +++++++++-------------------------- 2 files changed, 12 insertions(+), 29 deletions(-) diff --git a/configure b/configure index 6911c3be6a..600da9baa8 100755 --- a/configure +++ b/configure @@ -1811,9 +1811,9 @@ fi ########################################## # glib support probe -if $pkg_config --modversion gthread-2.0 gio-2.0 > /dev/null 2>&1 ; then - glib_cflags=`$pkg_config --cflags gthread-2.0 gio-2.0 2>/dev/null` - glib_libs=`$pkg_config --libs gthread-2.0 gio-2.0 2>/dev/null` +if $pkg_config --modversion glib-2.0 > /dev/null 2>&1 ; then + glib_cflags=`$pkg_config --cflags glib-2.0 2>/dev/null` + glib_libs=`$pkg_config --libs glib-2.0 2>/dev/null` libs_softmmu="$glib_libs $libs_softmmu" libs_tools="$glib_libs $libs_tools" else diff --git a/qemu-ga.c b/qemu-ga.c index 6e2f61fe3c..869ee3709b 100644 --- a/qemu-ga.c +++ b/qemu-ga.c @@ -14,7 +14,6 @@ #include #include #include -#include #include #include #include @@ -37,9 +36,7 @@ struct GAState { JSONMessageParser parser; GMainLoop *main_loop; - GSocket *conn_sock; GIOChannel *conn_channel; - GSocket *listen_sock; GIOChannel *listen_channel; const char *path; const char *method; @@ -412,18 +409,20 @@ static gboolean listen_channel_accept(GIOChannel *channel, GIOCondition condition, gpointer data) { GAState *s = data; - GError *err = NULL; g_assert(channel != NULL); - int ret; + int ret, conn_fd; bool accepted = false; + struct sockaddr_un addr; + socklen_t addrlen = sizeof(addr); - s->conn_sock = g_socket_accept(s->listen_sock, NULL, &err); - if (err != NULL) { - g_warning("error converting fd to gsocket: %s", err->message); - g_error_free(err); + conn_fd = qemu_accept(g_io_channel_unix_get_fd(s->listen_channel), + (struct sockaddr *)&addr, &addrlen); + if (conn_fd == -1) { + g_warning("error converting fd to gsocket: %s", strerror(errno)); goto out; } - ret = conn_channel_add(s, g_socket_get_fd(s->conn_sock)); + fcntl(conn_fd, F_SETFL, O_NONBLOCK); + ret = conn_channel_add(s, conn_fd); if (ret) { g_warning("error setting up connection"); goto out; @@ -440,19 +439,8 @@ out: */ static int listen_channel_add(GAState *s, int listen_fd, bool new) { - GError *err = NULL; - if (new) { s->listen_channel = g_io_channel_unix_new(listen_fd); - if (s->listen_sock) { - g_object_unref(s->listen_sock); - } - s->listen_sock = g_socket_new_from_fd(listen_fd, &err); - if (err != NULL) { - g_warning("error converting fd to gsocket: %s", err->message); - g_error_free(err); - return -1; - } } g_io_add_watch(s->listen_channel, G_IO_IN, listen_channel_accept, s); @@ -466,8 +454,6 @@ static void conn_channel_close(GAState *s) { if (strcmp(s->method, "unix-listen") == 0) { g_io_channel_shutdown(s->conn_channel, true, NULL); - g_object_unref(s->conn_sock); - s->conn_sock = NULL; listen_channel_add(s, 0, false); } else if (strcmp(s->method, "virtio-serial") == 0) { /* we spin on EOF for virtio-serial, so back off a bit. also, @@ -624,9 +610,6 @@ int main(int argc, char **argv) become_daemon(pidfile); } - g_type_init(); - g_thread_init(NULL); - s = qemu_mallocz(sizeof(GAState)); s->conn_channel = NULL; s->path = path; From aad04cd024f0c59f0b96f032cde2e24eb3abba6d Mon Sep 17 00:00:00 2001 From: Blue Swirl Date: Sat, 23 Jul 2011 19:26:08 +0000 Subject: [PATCH 055/230] Fix chrdev return value conversion 6e1db57b2ac9025c2443c665a0d9e78748637b26 didn't convert brlapi or win32 chrdevs, breaking build for those. Fix by converting the chrdevs. Acked-by: Kevin Wolf Signed-off-by: Blue Swirl --- hw/baum.h | 2 +- qemu-char.c | 7 ++++--- 2 files changed, 5 insertions(+), 4 deletions(-) diff --git a/hw/baum.h b/hw/baum.h index 8af710fa21..3f28cc339a 100644 --- a/hw/baum.h +++ b/hw/baum.h @@ -23,4 +23,4 @@ */ /* char device */ -CharDriverState *chr_baum_init(QemuOpts *opts); +int chr_baum_init(QemuOpts *opts, CharDriverState **_chr); diff --git a/qemu-char.c b/qemu-char.c index dcf706592b..2982bfd7bb 100644 --- a/qemu-char.c +++ b/qemu-char.c @@ -1782,7 +1782,7 @@ static int qemu_chr_open_win_pipe(QemuOpts *opts, CharDriverState **_chr) return 0; } -static CharDriverState *qemu_chr_open_win_file(HANDLE fd_out) +static int qemu_chr_open_win_file(HANDLE fd_out, CharDriverState **pchr) { CharDriverState *chr; WinCharState *s; @@ -1793,10 +1793,11 @@ static CharDriverState *qemu_chr_open_win_file(HANDLE fd_out) chr->opaque = s; chr->chr_write = win_chr_write; qemu_chr_generic_open(chr); - return chr; + *pchr = chr; + return 0; } -static int qemu_chr_open_win_con(QemuOpts *opts, CharDriverState **_chr) +static int qemu_chr_open_win_con(QemuOpts *opts, CharDriverState **chr) { return qemu_chr_open_win_file(GetStdHandle(STD_OUTPUT_HANDLE), chr); } From 00aa0040e8b8ec45a75be4e8926a84b82cc75838 Mon Sep 17 00:00:00 2001 From: Blue Swirl Date: Sat, 23 Jul 2011 20:04:29 +0000 Subject: [PATCH 056/230] Wrap recv to avoid warnings Avoid warnings like these by wrapping recv(): CC slirp/ip_icmp.o /src/qemu/slirp/ip_icmp.c: In function 'icmp_receive': /src/qemu/slirp/ip_icmp.c:418:5: error: passing argument 2 of 'recv' from incompatible pointer type [-Werror] /usr/local/lib/gcc/i686-mingw32msvc/4.6.0/../../../../i686-mingw32msvc/include/winsock2.h:547:32: note: expected 'char *' but argument is of type 'struct icmp *' Remove also casts used to avoid warnings. Reviewed-by: Anthony Liguori Signed-off-by: Blue Swirl --- block/sheepdog.c | 2 +- gdbstub.c | 2 +- linux-user/syscall.c | 2 +- nbd.c | 2 +- net/socket.c | 4 ++-- qemu-char.c | 4 ++-- qemu-common.h | 6 ++++++ savevm.c | 2 +- slirp/ip_icmp.c | 2 +- slirp/slirp.c | 2 +- slirp/socket.c | 4 ++-- ui/vnc-tls.c | 2 +- ui/vnc.c | 2 +- 13 files changed, 21 insertions(+), 15 deletions(-) diff --git a/block/sheepdog.c b/block/sheepdog.c index 77a4de5100..e150ac0123 100644 --- a/block/sheepdog.c +++ b/block/sheepdog.c @@ -496,7 +496,7 @@ static ssize_t recvmsg(int s, struct msghdr *msg, int flags) } buf = qemu_malloc(size); - ret = recv(s, buf, size, flags); + ret = qemu_recv(s, buf, size, flags); if (ret < 0) { goto out; } diff --git a/gdbstub.c b/gdbstub.c index c085a5afb3..27b0cfa81d 100644 --- a/gdbstub.c +++ b/gdbstub.c @@ -319,7 +319,7 @@ static int get_char(GDBState *s) int ret; for(;;) { - ret = recv(s->fd, &ch, 1, 0); + ret = qemu_recv(s->fd, &ch, 1, 0); if (ret < 0) { if (errno == ECONNRESET) s->fd = -1; diff --git a/linux-user/syscall.c b/linux-user/syscall.c index 1dd7aad43c..73f9baa6f9 100644 --- a/linux-user/syscall.c +++ b/linux-user/syscall.c @@ -2004,7 +2004,7 @@ static abi_long do_recvfrom(int fd, abi_ulong msg, size_t len, int flags, ret = get_errno(recvfrom(fd, host_msg, len, flags, addr, &addrlen)); } else { addr = NULL; /* To keep compiler quiet. */ - ret = get_errno(recv(fd, host_msg, len, flags)); + ret = get_errno(qemu_recv(fd, host_msg, len, flags)); } if (!is_error(ret)) { if (target_addr) { diff --git a/nbd.c b/nbd.c index 0dcd86b52f..e7a585dcb0 100644 --- a/nbd.c +++ b/nbd.c @@ -78,7 +78,7 @@ size_t nbd_wr_sync(int fd, void *buffer, size_t size, bool do_read) ssize_t len; if (do_read) { - len = recv(fd, buffer + offset, size - offset, 0); + len = qemu_recv(fd, buffer + offset, size - offset, 0); } else { len = send(fd, buffer + offset, size - offset, 0); } diff --git a/net/socket.c b/net/socket.c index bc1bf58894..11fe5f383f 100644 --- a/net/socket.c +++ b/net/socket.c @@ -76,7 +76,7 @@ static void net_socket_send(void *opaque) uint8_t buf1[4096]; const uint8_t *buf; - size = recv(s->fd, (void *)buf1, sizeof(buf1), 0); + size = qemu_recv(s->fd, buf1, sizeof(buf1), 0); if (size < 0) { err = socket_error(); if (err != EWOULDBLOCK) @@ -138,7 +138,7 @@ static void net_socket_send_dgram(void *opaque) NetSocketState *s = opaque; int size; - size = recv(s->fd, (void *)s->buf, sizeof(s->buf), 0); + size = qemu_recv(s->fd, s->buf, sizeof(s->buf), 0); if (size < 0) return; if (size == 0) { diff --git a/qemu-char.c b/qemu-char.c index 2982bfd7bb..8e8cf31a29 100644 --- a/qemu-char.c +++ b/qemu-char.c @@ -1860,7 +1860,7 @@ static void udp_chr_read(void *opaque) if (s->max_size == 0) return; - s->bufcnt = recv(s->fd, (void *)s->buf, sizeof(s->buf), 0); + s->bufcnt = qemu_recv(s->fd, s->buf, sizeof(s->buf), 0); s->bufptr = s->bufcnt; if (s->bufcnt <= 0) return; @@ -2078,7 +2078,7 @@ static ssize_t tcp_chr_recv(CharDriverState *chr, char *buf, size_t len) static ssize_t tcp_chr_recv(CharDriverState *chr, char *buf, size_t len) { TCPCharDriver *s = chr->opaque; - return recv(s->fd, buf, len, 0); + return qemu_recv(s->fd, buf, len, 0); } #endif diff --git a/qemu-common.h b/qemu-common.h index ba55719700..391fadda56 100644 --- a/qemu-common.h +++ b/qemu-common.h @@ -200,6 +200,12 @@ int qemu_eventfd(int pipefd[2]); int qemu_pipe(int pipefd[2]); #endif +#ifdef _WIN32 +#define qemu_recv(sockfd, buf, len, flags) recv(sockfd, (void *)buf, len, flags) +#else +#define qemu_recv(sockfd, buf, len, flags) recv(sockfd, buf, len, flags) +#endif + /* Error handling. */ void QEMU_NORETURN hw_error(const char *fmt, ...) GCC_FMT_ATTR(1, 2); diff --git a/savevm.c b/savevm.c index 8139bc7e29..79db4cbd18 100644 --- a/savevm.c +++ b/savevm.c @@ -194,7 +194,7 @@ static int socket_get_buffer(void *opaque, uint8_t *buf, int64_t pos, int size) ssize_t len; do { - len = recv(s->fd, (void *)buf, size, 0); + len = qemu_recv(s->fd, buf, size, 0); } while (len == -1 && socket_error() == EINTR); if (len == -1) diff --git a/slirp/ip_icmp.c b/slirp/ip_icmp.c index 14a5312c84..4b43994dbc 100644 --- a/slirp/ip_icmp.c +++ b/slirp/ip_icmp.c @@ -415,7 +415,7 @@ void icmp_receive(struct socket *so) icp = mtod(m, struct icmp *); id = icp->icmp_id; - len = recv(so->s, icp, m->m_len, 0); + len = qemu_recv(so->s, icp, m->m_len, 0); icp->icmp_id = id; m->m_data -= hlen; diff --git a/slirp/slirp.c b/slirp/slirp.c index faaa2f36c7..df787ea1d9 100644 --- a/slirp/slirp.c +++ b/slirp/slirp.c @@ -522,7 +522,7 @@ void slirp_select_poll(fd_set *readfds, fd_set *writefds, fd_set *xfds, */ #ifdef PROBE_CONN if (so->so_state & SS_ISFCONNECTING) { - ret = recv(so->s, (char *)&ret, 0,0); + ret = qemu_recv(so->s, &ret, 0,0); if (ret < 0) { /* XXX */ diff --git a/slirp/socket.c b/slirp/socket.c index 9b8ae13f5e..77b0c98197 100644 --- a/slirp/socket.c +++ b/slirp/socket.c @@ -166,7 +166,7 @@ soread(struct socket *so) nn = readv(so->s, (struct iovec *)iov, n); DEBUG_MISC((dfd, " ... read nn = %d bytes\n", nn)); #else - nn = recv(so->s, iov[0].iov_base, iov[0].iov_len,0); + nn = qemu_recv(so->s, iov[0].iov_base, iov[0].iov_len,0); #endif if (nn <= 0) { if (nn < 0 && (errno == EINTR || errno == EAGAIN)) @@ -191,7 +191,7 @@ soread(struct socket *so) */ if (n == 2 && nn == iov[0].iov_len) { int ret; - ret = recv(so->s, iov[1].iov_base, iov[1].iov_len,0); + ret = qemu_recv(so->s, iov[1].iov_base, iov[1].iov_len,0); if (ret > 0) nn += ret; } diff --git a/ui/vnc-tls.c b/ui/vnc-tls.c index dec626c539..31f1467ad0 100644 --- a/ui/vnc-tls.c +++ b/ui/vnc-tls.c @@ -89,7 +89,7 @@ static ssize_t vnc_tls_pull(gnutls_transport_ptr_t transport, int ret; retry: - ret = recv(vs->csock, data, len, 0); + ret = qemu_recv(vs->csock, data, len, 0); if (ret < 0) { if (errno == EINTR) goto retry; diff --git a/ui/vnc.c b/ui/vnc.c index 4425180a84..f1e27d97b8 100644 --- a/ui/vnc.c +++ b/ui/vnc.c @@ -1199,7 +1199,7 @@ long vnc_client_read_buf(VncState *vs, uint8_t *data, size_t datalen) } } else #endif /* CONFIG_VNC_TLS */ - ret = recv(vs->csock, (void *)data, datalen, 0); + ret = qemu_recv(vs->csock, data, datalen, 0); VNC_DEBUG("Read wire %p %zd -> %ld\n", data, datalen, ret); return vnc_client_io_error(vs, ret, socket_error()); } From 0caf448b8062b6e186f7bf12dbb27c3ab4ca6136 Mon Sep 17 00:00:00 2001 From: Blue Swirl Date: Sat, 23 Jul 2011 21:21:14 +0000 Subject: [PATCH 057/230] simpletrace: suppress a warning from unused variable Avoid this warning: CC simpletrace.o /src/qemu/simpletrace.c: In function 'writeout_thread': /src/qemu/simpletrace.c:122:12: error: variable 'unused' set but not used [-Werror=unused-but-set-variable] by adding GCC attribute unused to the variable. Signed-off-by: Blue Swirl --- simpletrace.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/simpletrace.c b/simpletrace.c index f1dbb5e502..de355e9675 100644 --- a/simpletrace.c +++ b/simpletrace.c @@ -119,7 +119,7 @@ static void *writeout_thread(void *opaque) TraceRecord record; unsigned int writeout_idx = 0; unsigned int num_available, idx; - size_t unused; + size_t unused __attribute__ ((unused)); for (;;) { wait_for_trace_records_available(); From c886edfb851c0c590d4e77f058f2ec8ed95ad1b5 Mon Sep 17 00:00:00 2001 From: Blue Swirl Date: Fri, 22 Jul 2011 21:08:09 +0000 Subject: [PATCH 058/230] Let users select their pythons Add configure check for python, exit if not found. Add switches for specifying the path to python, use the path in Makefile. Signed-off-by: Blue Swirl --- Makefile | 12 ++++++------ configure | 13 +++++++++++++ 2 files changed, 19 insertions(+), 6 deletions(-) diff --git a/Makefile b/Makefile index f3a03ad87e..daa3aa09b5 100644 --- a/Makefile +++ b/Makefile @@ -168,22 +168,22 @@ test-visitor.o test-qmp-commands.o qemu-ga$(EXESUF): QEMU_CFLAGS += -I $(qapi-di $(qapi-dir)/test-qapi-types.c: $(qapi-dir)/test-qapi-types.h $(qapi-dir)/test-qapi-types.h: $(SRC_PATH)/qapi-schema-test.json $(SRC_PATH)/scripts/qapi-types.py - $(call quiet-command,python $(SRC_PATH)/scripts/qapi-types.py -o "$(qapi-dir)" -p "test-" < $<, " GEN $@") + $(call quiet-command,$(PYTHON) $(SRC_PATH)/scripts/qapi-types.py -o "$(qapi-dir)" -p "test-" < $<, " GEN $@") $(qapi-dir)/test-qapi-visit.c: $(qapi-dir)/test-qapi-visit.h $(qapi-dir)/test-qapi-visit.h: $(SRC_PATH)/qapi-schema-test.json $(SRC_PATH)/scripts/qapi-visit.py - $(call quiet-command,python $(SRC_PATH)/scripts/qapi-visit.py -o "$(qapi-dir)" -p "test-" < $<, " GEN $@") + $(call quiet-command,$(PYTHON) $(SRC_PATH)/scripts/qapi-visit.py -o "$(qapi-dir)" -p "test-" < $<, " GEN $@") $(qapi-dir)/test-qmp-commands.h: $(qapi-dir)/test-qmp-marshal.c $(qapi-dir)/test-qmp-marshal.c: $(SRC_PATH)/qapi-schema-test.json $(SRC_PATH)/scripts/qapi-commands.py - $(call quiet-command,python $(SRC_PATH)/scripts/qapi-commands.py -o "$(qapi-dir)" -p "test-" < $<, " GEN $@") + $(call quiet-command,$(PYTHON) $(SRC_PATH)/scripts/qapi-commands.py -o "$(qapi-dir)" -p "test-" < $<, " GEN $@") $(qapi-dir)/qga-qapi-types.c: $(qapi-dir)/qga-qapi-types.h $(qapi-dir)/qga-qapi-types.h: $(SRC_PATH)/qapi-schema-guest.json $(SRC_PATH)/scripts/qapi-types.py - $(call quiet-command,python $(SRC_PATH)/scripts/qapi-types.py -o "$(qapi-dir)" -p "qga-" < $<, " GEN $@") + $(call quiet-command,$(PYTHON) $(SRC_PATH)/scripts/qapi-types.py -o "$(qapi-dir)" -p "qga-" < $<, " GEN $@") $(qapi-dir)/qga-qapi-visit.c: $(qapi-dir)/qga-qapi-visit.h $(qapi-dir)/qga-qapi-visit.h: $(SRC_PATH)/qapi-schema-guest.json $(SRC_PATH)/scripts/qapi-visit.py - $(call quiet-command,python $(SRC_PATH)/scripts/qapi-visit.py -o "$(qapi-dir)" -p "qga-" < $<, " GEN $@") + $(call quiet-command,$(PYTHON) $(SRC_PATH)/scripts/qapi-visit.py -o "$(qapi-dir)" -p "qga-" < $<, " GEN $@") $(qapi-dir)/qga-qmp-marshal.c: $(SRC_PATH)/qapi-schema-guest.json $(SRC_PATH)/scripts/qapi-commands.py - $(call quiet-command,python $(SRC_PATH)/scripts/qapi-commands.py -o "$(qapi-dir)" -p "qga-" < $<, " GEN $@") + $(call quiet-command,$(PYTHON) $(SRC_PATH)/scripts/qapi-commands.py -o "$(qapi-dir)" -p "qga-" < $<, " GEN $@") test-visitor.o: $(addprefix $(qapi-dir)/, test-qapi-types.c test-qapi-types.h test-qapi-visit.c test-qapi-visit.h) $(qapi-obj-y) test-visitor: test-visitor.o qfloat.o qint.o qdict.o qstring.o qlist.o qbool.o $(qapi-obj-y) error.o osdep.o qemu-malloc.o $(oslib-obj-y) qjson.o json-streamer.o json-lexer.o json-parser.o qerror.o qemu-error.o qemu-tool.o $(qapi-dir)/test-qapi-visit.o $(qapi-dir)/test-qapi-types.o diff --git a/configure b/configure index 600da9baa8..38e3724f33 100755 --- a/configure +++ b/configure @@ -475,6 +475,7 @@ fi : ${make=${MAKE-make}} : ${install=${INSTALL-install}} +: ${python=${PYTHON-python}} if test "$mingw32" = "yes" ; then EXESUF=".exe" @@ -516,6 +517,8 @@ for opt do ;; --install=*) install="$optarg" ;; + --python=*) python="$optarg" + ;; --extra-cflags=*) ;; --extra-ldflags=*) @@ -924,6 +927,7 @@ echo " --extra-cflags=CFLAGS append extra C compiler flags QEMU_CFLAGS" echo " --extra-ldflags=LDFLAGS append extra linker flags LDFLAGS" echo " --make=MAKE use specified make [$make]" echo " --install=INSTALL use specified install [$install]" +echo " --python=PYTHON use specified python [$python]" echo " --static enable static build [$static]" echo " --mandir=PATH install man pages in PATH" echo " --datadir=PATH install firmware in PATH" @@ -1084,6 +1088,13 @@ if test "$solaris" = "yes" ; then fi fi +if has $python; then + : +else + echo "Python not found. Use --python=/path/to/python" + exit 1 +fi + if test -z "$target_list" ; then target_list="$default_target_list" else @@ -2591,6 +2602,7 @@ echo "QEMU_CFLAGS $QEMU_CFLAGS" echo "LDFLAGS $LDFLAGS" echo "make $make" echo "install $install" +echo "python $python" echo "host CPU $cpu" echo "host big endian $bigendian" echo "target list $target_list" @@ -3003,6 +3015,7 @@ echo "INSTALL=$install" >> $config_host_mak echo "INSTALL_DIR=$install -d -m0755 -p" >> $config_host_mak echo "INSTALL_DATA=$install -m0644 -p" >> $config_host_mak echo "INSTALL_PROG=$install -m0755 -p" >> $config_host_mak +echo "PYTHON=$python" >> $config_host_mak echo "CC=$cc" >> $config_host_mak echo "CC_I386=$cc_i386" >> $config_host_mak echo "HOST_CC=$host_cc" >> $config_host_mak From fb4bb2b587549612a0da92de68fcc096ffd8a7d7 Mon Sep 17 00:00:00 2001 From: Anthony PERARD Date: Fri, 15 Jul 2011 00:33:42 +0000 Subject: [PATCH 059/230] xen: introduce xen_change_state_handler Remove the call to xenstore_record_dm_state from xen_main_loop_prepare that is HVM specific. Add a new vm_change_state_handler shared between xen_pv and xen_hvm machines to record the VM state to xenstore. Signed-off-by: Anthony PERARD Signed-off-by: Stefano Stabellini Signed-off-by: Alexander Graf --- xen-all.c | 25 ++++++++++++++++++------- 1 file changed, 18 insertions(+), 7 deletions(-) diff --git a/xen-all.c b/xen-all.c index 167bed6dbd..83c5476764 100644 --- a/xen-all.c +++ b/xen-all.c @@ -797,12 +797,17 @@ void xenstore_store_pv_console_info(int i, CharDriverState *chr) } } -static void xenstore_record_dm_state(XenIOState *s, const char *state) +static void xenstore_record_dm_state(struct xs_handle *xs, const char *state) { char path[50]; + if (xs == NULL) { + fprintf(stderr, "xenstore connection not initialized\n"); + exit(1); + } + snprintf(path, sizeof (path), "/local/domain/0/device-model/%u/state", xen_domid); - if (!xs_write(s->xenstore, XBT_NULL, path, state, strlen(state))) { + if (!xs_write(xs, XBT_NULL, path, state, strlen(state))) { fprintf(stderr, "error recording dm state\n"); exit(1); } @@ -823,15 +828,20 @@ static void xen_main_loop_prepare(XenIOState *state) if (evtchn_fd != -1) { qemu_set_fd_handler(evtchn_fd, cpu_handle_ioreq, NULL, state); } - - /* record state running */ - xenstore_record_dm_state(state, "running"); } /* Initialise Xen */ -static void xen_vm_change_state_handler(void *opaque, int running, int reason) +static void xen_change_state_handler(void *opaque, int running, int reason) +{ + if (running) { + /* record state running */ + xenstore_record_dm_state(xenstore, "running"); + } +} + +static void xen_hvm_change_state_handler(void *opaque, int running, int reason) { XenIOState *state = opaque; if (running) { @@ -854,6 +864,7 @@ int xen_init(void) xen_be_printf(NULL, 0, "can't open xen interface\n"); return -1; } + qemu_add_vm_change_state_handler(xen_change_state_handler, NULL); return 0; } @@ -915,7 +926,7 @@ int xen_hvm_init(void) xen_map_cache_init(); xen_ram_init(ram_size); - qemu_add_vm_change_state_handler(xen_vm_change_state_handler, state); + qemu_add_vm_change_state_handler(xen_hvm_change_state_handler, state); state->client = xen_cpu_phys_memory_client; QLIST_INIT(&state->physmap); From 30ab61252b71446977e298f146be124eb4a5b333 Mon Sep 17 00:00:00 2001 From: Anthony PERARD Date: Fri, 15 Jul 2011 04:32:52 +0000 Subject: [PATCH 060/230] xen: Fix xen_enabled(). Use the "host" CONFIG_ define instead of the "target" one. Signed-off-by: Anthony PERARD Acked-by: Paolo Bonzini Signed-off-by: Alexander Graf --- hw/xen.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/hw/xen.h b/hw/xen.h index e432705f45..43b95d6880 100644 --- a/hw/xen.h +++ b/hw/xen.h @@ -24,7 +24,7 @@ extern int xen_allowed; static inline int xen_enabled(void) { -#ifdef CONFIG_XEN +#ifdef CONFIG_XEN_BACKEND return xen_allowed; #else return 0; From 8ca5692df4a635b2c9211d6dc3dea15bd2b674c8 Mon Sep 17 00:00:00 2001 From: Anthony PERARD Date: Fri, 15 Jul 2011 04:32:53 +0000 Subject: [PATCH 061/230] exec.c: Use ram_addr_t in cpu_physical_memory_rw(...). As the variable pd and addr1 inside the function cpu_physical_memory_rw are mean to handle a RAM address, they should be of the ram_addr_t type instead of unsigned long. Signed-off-by: Anthony PERARD Acked-by: Paolo Bonzini Signed-off-by: Alexander Graf --- exec.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/exec.c b/exec.c index 2160ded401..0393d39998 100644 --- a/exec.c +++ b/exec.c @@ -3858,7 +3858,7 @@ void cpu_physical_memory_rw(target_phys_addr_t addr, uint8_t *buf, uint8_t *ptr; uint32_t val; target_phys_addr_t page; - unsigned long pd; + ram_addr_t pd; PhysPageDesc *p; while (len > 0) { @@ -3898,7 +3898,7 @@ void cpu_physical_memory_rw(target_phys_addr_t addr, uint8_t *buf, l = 1; } } else { - unsigned long addr1; + ram_addr_t addr1; addr1 = (pd & TARGET_PAGE_MASK) + (addr & ~TARGET_PAGE_MASK); /* RAM case */ ptr = qemu_get_ram_ptr(addr1); From f03a4ac12222ce1fb7886dc6cb38a027b73ccff2 Mon Sep 17 00:00:00 2001 From: Alexander Graf Date: Sun, 17 Jul 2011 07:22:06 +0200 Subject: [PATCH 062/230] xen: remove CONFIG_XEN_MAPCACHE We were still exporting CONFIG_XEN_MAPCACHE, even though it's completely unused by now. Remove it. Signed-off-by: Alexander Graf --- configure | 3 --- 1 file changed, 3 deletions(-) diff --git a/configure b/configure index 38e3724f33..cd399dc4ce 100755 --- a/configure +++ b/configure @@ -3290,9 +3290,6 @@ case "$target_arch2" in if test "$xen" = "yes" -a "$target_softmmu" = "yes" ; then target_phys_bits=64 echo "CONFIG_XEN=y" >> $config_target_mak - if test "$cpu" = "i386" -o "$cpu" = "x86_64"; then - echo "CONFIG_XEN_MAPCACHE=y" >> $config_target_mak - fi fi esac case "$target_arch2" in From 59d21e537b6c3a964d4e346e9cf2a1e17267a27c Mon Sep 17 00:00:00 2001 From: Alexander Graf Date: Sun, 17 Jul 2011 07:30:29 +0200 Subject: [PATCH 063/230] xen: make xen_enabled even more clever When using xen_enabled() we're currently only checking if xen is enabled at all during the build. But what if you want to build multiple targets out of which only one can potentially run xen code? That means that for generic code we'll still have to fall back to the variable and potentially slow the code down, but it's not as important as that is mostly xen device emulation which is not touched for non-xen targets. The target specific code however can with this patch see that it's unable to ever execute xen code. We can thus always return 0 on xen_enabled(), giving gcc enough hints to evict the mapcache code from the target memory management code. Signed-off-by: Alexander Graf Acked-by: Anthony PERARD --- configure | 5 +++++ hw/xen.h | 2 +- 2 files changed, 6 insertions(+), 1 deletion(-) diff --git a/configure b/configure index cd399dc4ce..bc3495c6fb 100755 --- a/configure +++ b/configure @@ -3290,7 +3290,12 @@ case "$target_arch2" in if test "$xen" = "yes" -a "$target_softmmu" = "yes" ; then target_phys_bits=64 echo "CONFIG_XEN=y" >> $config_target_mak + else + echo "CONFIG_NO_XEN=y" >> $config_target_mak fi + ;; + *) + echo "CONFIG_NO_XEN=y" >> $config_target_mak esac case "$target_arch2" in i386|x86_64|ppcemb|ppc|ppc64|s390x) diff --git a/hw/xen.h b/hw/xen.h index 43b95d6880..21621115e4 100644 --- a/hw/xen.h +++ b/hw/xen.h @@ -24,7 +24,7 @@ extern int xen_allowed; static inline int xen_enabled(void) { -#ifdef CONFIG_XEN_BACKEND +#if defined(CONFIG_XEN_BACKEND) && !defined(CONFIG_NO_XEN) return xen_allowed; #else return 0; From f15fbc4bd1a24bd1477a846e63e62c6d435912f8 Mon Sep 17 00:00:00 2001 From: Anthony PERARD Date: Wed, 20 Jul 2011 08:17:42 +0000 Subject: [PATCH 064/230] cpu-common: Have a ram_addr_t of uint64 with Xen. In Xen case, memory can be bigger than the host memory. that mean a 32bits host (and QEMU) should be able to handle a RAM address of 64bits. Signed-off-by: Anthony PERARD Signed-off-by: Alexander Graf --- cpu-common.h | 8 ++++++++ exec.c | 9 +++++---- xen-all.c | 2 +- 3 files changed, 14 insertions(+), 5 deletions(-) diff --git a/cpu-common.h b/cpu-common.h index 44b04b3839..070010130c 100644 --- a/cpu-common.h +++ b/cpu-common.h @@ -27,7 +27,15 @@ enum device_endian { }; /* address in the RAM (different from a physical address) */ +#if defined(CONFIG_XEN_BACKEND) && TARGET_PHYS_ADDR_BITS == 64 +typedef uint64_t ram_addr_t; +# define RAM_ADDR_MAX UINT64_MAX +# define RAM_ADDR_FMT "%" PRIx64 +#else typedef unsigned long ram_addr_t; +# define RAM_ADDR_MAX ULONG_MAX +# define RAM_ADDR_FMT "%lx" +#endif /* memory API */ diff --git a/exec.c b/exec.c index 0393d39998..bfc9a43ce7 100644 --- a/exec.c +++ b/exec.c @@ -2863,13 +2863,13 @@ static void *file_ram_alloc(RAMBlock *block, static ram_addr_t find_ram_offset(ram_addr_t size) { RAMBlock *block, *next_block; - ram_addr_t offset = 0, mingap = ULONG_MAX; + ram_addr_t offset = 0, mingap = RAM_ADDR_MAX; if (QLIST_EMPTY(&ram_list.blocks)) return 0; QLIST_FOREACH(block, &ram_list.blocks, next) { - ram_addr_t end, next = ULONG_MAX; + ram_addr_t end, next = RAM_ADDR_MAX; end = block->offset + block->length; @@ -3081,7 +3081,8 @@ void qemu_ram_remap(ram_addr_t addr, ram_addr_t length) #endif } if (area != vaddr) { - fprintf(stderr, "Could not remap addr: %lx@%lx\n", + fprintf(stderr, "Could not remap addr: " + RAM_ADDR_FMT "@" RAM_ADDR_FMT "\n", length, addr); exit(1); } @@ -4052,7 +4053,7 @@ void *cpu_physical_memory_map(target_phys_addr_t addr, target_phys_addr_t page; unsigned long pd; PhysPageDesc *p; - ram_addr_t raddr = ULONG_MAX; + ram_addr_t raddr = RAM_ADDR_MAX; ram_addr_t rlen; void *ret; diff --git a/xen-all.c b/xen-all.c index 83c5476764..53296bf2ca 100644 --- a/xen-all.c +++ b/xen-all.c @@ -184,7 +184,7 @@ void xen_ram_alloc(ram_addr_t ram_addr, ram_addr_t size) } if (xc_domain_populate_physmap_exact(xen_xc, xen_domid, nr_pfn, 0, 0, pfn_list)) { - hw_error("xen: failed to populate ram at %lx", ram_addr); + hw_error("xen: failed to populate ram at " RAM_ADDR_FMT, ram_addr); } qemu_free(pfn_list); From 8a369e20e701c9d220834e0daa027e65acd35214 Mon Sep 17 00:00:00 2001 From: Anthony PERARD Date: Wed, 20 Jul 2011 08:17:43 +0000 Subject: [PATCH 065/230] xen: Fix the memory registration to reflect of what is done by Xen. A Xen guest memory is allocated by libxc. But this memory is not allocated continuously, instead, it leaves the VGA IO memory space not allocated, same for the MMIO space (at HVM_BELOW_4G_MMIO_START of size HVM_BELOW_4G_MMIO_LENGTH). So to reflect that, we do not register the physical memory for this two holes. But we still keep only one RAMBlock for the all RAM as it is more easier than have two separate blocks (1 above 4G). Also this prevent QEMU from use the MMIO space for a ROM. Signed-off-by: Anthony PERARD Signed-off-by: Alexander Graf --- xen-all.c | 27 ++++++++++++++++++++------- 1 file changed, 20 insertions(+), 7 deletions(-) diff --git a/xen-all.c b/xen-all.c index 53296bf2ca..9eaeac1654 100644 --- a/xen-all.c +++ b/xen-all.c @@ -19,6 +19,7 @@ #include #include +#include //#define DEBUG_XEN @@ -144,6 +145,12 @@ static void xen_ram_init(ram_addr_t ram_size) new_block->host = NULL; new_block->offset = 0; new_block->length = ram_size; + if (ram_size >= HVM_BELOW_4G_RAM_END) { + /* Xen does not allocate the memory continuously, and keep a hole at + * HVM_BELOW_4G_MMIO_START of HVM_BELOW_4G_MMIO_LENGTH + */ + new_block->length += HVM_BELOW_4G_MMIO_LENGTH; + } QLIST_INSERT_HEAD(&ram_list.blocks, new_block, next); @@ -152,20 +159,26 @@ static void xen_ram_init(ram_addr_t ram_size) memset(ram_list.phys_dirty + (new_block->offset >> TARGET_PAGE_BITS), 0xff, new_block->length >> TARGET_PAGE_BITS); - if (ram_size >= 0xe0000000 ) { - above_4g_mem_size = ram_size - 0xe0000000; - below_4g_mem_size = 0xe0000000; + if (ram_size >= HVM_BELOW_4G_RAM_END) { + above_4g_mem_size = ram_size - HVM_BELOW_4G_RAM_END; + below_4g_mem_size = HVM_BELOW_4G_RAM_END; } else { below_4g_mem_size = ram_size; } - cpu_register_physical_memory(0, below_4g_mem_size, new_block->offset); -#if TARGET_PHYS_ADDR_BITS > 32 + cpu_register_physical_memory(0, 0xa0000, 0); + /* Skip of the VGA IO memory space, it will be registered later by the VGA + * emulated device. + * + * The area between 0xc0000 and 0x100000 will be used by SeaBIOS to load + * the Options ROM, so it is registered here as RAM. + */ + cpu_register_physical_memory(0xc0000, below_4g_mem_size - 0xc0000, + 0xc0000); if (above_4g_mem_size > 0) { cpu_register_physical_memory(0x100000000ULL, above_4g_mem_size, - new_block->offset + below_4g_mem_size); + 0x100000000ULL); } -#endif } void xen_ram_alloc(ram_addr_t ram_addr, ram_addr_t size) From 834e76ea1cc3f6fb261fe6a40f7571600bcb25b1 Mon Sep 17 00:00:00 2001 From: Anthony PERARD Date: Wed, 20 Jul 2011 08:17:44 +0000 Subject: [PATCH 066/230] vl.c: Check the asked ram_size later. As a Xen guest can have more than 2GB of RAM on a 32bit host, we move the conditions after than we now if we run one Xen or not. [agraf] separate xen branch from ram_size check Signed-off-by: Anthony PERARD Signed-off-by: Alexander Graf --- vl.c | 16 ++++++++++------ 1 file changed, 10 insertions(+), 6 deletions(-) diff --git a/vl.c b/vl.c index 4b6688b553..d8c7c01048 100644 --- a/vl.c +++ b/vl.c @@ -2440,11 +2440,6 @@ int main(int argc, char **argv, char **envp) exit(1); } - /* On 32-bit hosts, QEMU is limited by virtual address space */ - if (value > (2047 << 20) && HOST_LONG_BITS == 32) { - fprintf(stderr, "qemu: at most 2047 MB RAM can be simulated\n"); - exit(1); - } if (value != (uint64_t)(ram_addr_t)value) { fprintf(stderr, "qemu: ram size too large\n"); exit(1); @@ -3099,8 +3094,17 @@ int main(int argc, char **argv, char **envp) exit(1); /* init the memory */ - if (ram_size == 0) + if (ram_size == 0) { ram_size = DEFAULT_RAM_SIZE * 1024 * 1024; + } + + if (!xen_enabled()) { + /* On 32-bit hosts, QEMU is limited by virtual address space */ + if (ram_size > (2047 << 20) && HOST_LONG_BITS == 32) { + fprintf(stderr, "qemu: at most 2047 MB RAM can be simulated\n"); + exit(1); + } + } /* init the dynamic translator */ cpu_exec_init_all(tb_size * 1024 * 1024); From 679f4f8b178e7c66fbc2f39c905374ee8663d5d8 Mon Sep 17 00:00:00 2001 From: Stefano Stabellini Date: Mon, 18 Jul 2011 06:07:02 +0000 Subject: [PATCH 067/230] xen: implement unplug protocol in xen_platform The unplug protocol is necessary to support PV drivers in the guest: the drivers expect to be able to "unplug" emulated disks and nics before initializing the Xen PV interfaces. It is responsibility of the guest to make sure that the unplug is done before the emulated devices or the PV interface start to be used. We use pci_for_each_device to walk the PCI bus, identify the devices and disks that we want to disable and dynamically unplug them. Changes in v2: - use PCI_CLASS constants; - replace pci_unplug_device with qdev_unplug; - do not import hw/ide/internal.h in xen_platform.c; Changes in v3: - introduce piix3-ide-xen, that support hot-unplug; - move the unplug code to hw/ide/piix.c; - just call qdev_unplug from xen_platform.c to unplug the IDE disks; Signed-off-by: Stefano Stabellini Acked-by: Kevin Wolf Signed-off-by: Alexander Graf --- hw/ide.h | 1 + hw/ide/piix.c | 44 ++++++++++++++++++++++++++++++++++++++++++++ hw/pc_piix.c | 6 +++++- hw/xen_platform.c | 43 ++++++++++++++++++++++++++++++++++++++++++- 4 files changed, 92 insertions(+), 2 deletions(-) diff --git a/hw/ide.h b/hw/ide.h index 34d9394bcc..a490cbb6c5 100644 --- a/hw/ide.h +++ b/hw/ide.h @@ -13,6 +13,7 @@ ISADevice *isa_ide_init(int iobase, int iobase2, int isairq, /* ide-pci.c */ void pci_cmd646_ide_init(PCIBus *bus, DriveInfo **hd_table, int secondary_ide_enabled); +PCIDevice *pci_piix3_xen_ide_init(PCIBus *bus, DriveInfo **hd_table, int devfn); PCIDevice *pci_piix3_ide_init(PCIBus *bus, DriveInfo **hd_table, int devfn); PCIDevice *pci_piix4_ide_init(PCIBus *bus, DriveInfo **hd_table, int devfn); void vt82c686b_ide_init(PCIBus *bus, DriveInfo **hd_table, int devfn); diff --git a/hw/ide/piix.c b/hw/ide/piix.c index 84f72b0a66..f527dbd57e 100644 --- a/hw/ide/piix.c +++ b/hw/ide/piix.c @@ -149,6 +149,42 @@ static int pci_piix_ide_initfn(PCIDevice *dev) return 0; } +static int pci_piix3_xen_ide_unplug(DeviceState *dev) +{ + PCIDevice *pci_dev; + PCIIDEState *pci_ide; + DriveInfo *di; + int i = 0; + + pci_dev = DO_UPCAST(PCIDevice, qdev, dev); + pci_ide = DO_UPCAST(PCIIDEState, dev, pci_dev); + + for (; i < 3; i++) { + di = drive_get_by_index(IF_IDE, i); + if (di != NULL && di->bdrv != NULL && !di->bdrv->removable) { + DeviceState *ds = bdrv_get_attached(di->bdrv); + if (ds) { + bdrv_detach(di->bdrv, ds); + } + bdrv_close(di->bdrv); + pci_ide->bus[di->bus].ifs[di->unit].bs = NULL; + drive_put_ref(di); + } + } + qdev_reset_all(&(pci_ide->dev.qdev)); + return 0; +} + +PCIDevice *pci_piix3_xen_ide_init(PCIBus *bus, DriveInfo **hd_table, int devfn) +{ + PCIDevice *dev; + + dev = pci_create_simple(bus, devfn, "piix3-ide-xen"); + dev->qdev.info->unplug = pci_piix3_xen_ide_unplug; + pci_ide_create_devs(dev, hd_table); + return dev; +} + /* hd_table must contain 4 block drivers */ /* NOTE: for the PIIX3, the IRQs and IOports are hardcoded */ PCIDevice *pci_piix3_ide_init(PCIBus *bus, DriveInfo **hd_table, int devfn) @@ -181,6 +217,14 @@ static PCIDeviceInfo piix_ide_info[] = { .vendor_id = PCI_VENDOR_ID_INTEL, .device_id = PCI_DEVICE_ID_INTEL_82371SB_1, .class_id = PCI_CLASS_STORAGE_IDE, + },{ + .qdev.name = "piix3-ide-xen", + .qdev.size = sizeof(PCIIDEState), + .qdev.no_user = 1, + .init = pci_piix_ide_initfn, + .vendor_id = PCI_VENDOR_ID_INTEL, + .device_id = PCI_DEVICE_ID_INTEL_82371SB_1, + .class_id = PCI_CLASS_STORAGE_IDE, },{ .qdev.name = "piix4-ide", .qdev.size = sizeof(PCIIDEState), diff --git a/hw/pc_piix.c b/hw/pc_piix.c index c5c16b4571..40b73ea25c 100644 --- a/hw/pc_piix.c +++ b/hw/pc_piix.c @@ -155,7 +155,11 @@ static void pc_init1(ram_addr_t ram_size, ide_drive_get(hd, MAX_IDE_BUS); if (pci_enabled) { PCIDevice *dev; - dev = pci_piix3_ide_init(pci_bus, hd, piix3_devfn + 1); + if (xen_enabled()) { + dev = pci_piix3_xen_ide_init(pci_bus, hd, piix3_devfn + 1); + } else { + dev = pci_piix3_ide_init(pci_bus, hd, piix3_devfn + 1); + } idebus[0] = qdev_get_child_bus(&dev->qdev, "ide.0"); idebus[1] = qdev_get_child_bus(&dev->qdev, "ide.1"); } else { diff --git a/hw/xen_platform.c b/hw/xen_platform.c index f43e175b4e..fb6be6a464 100644 --- a/hw/xen_platform.c +++ b/hw/xen_platform.c @@ -76,6 +76,35 @@ static void log_writeb(PCIXenPlatformState *s, char val) } /* Xen Platform, Fixed IOPort */ +#define UNPLUG_ALL_IDE_DISKS 1 +#define UNPLUG_ALL_NICS 2 +#define UNPLUG_AUX_IDE_DISKS 4 + +static void unplug_nic(PCIBus *b, PCIDevice *d) +{ + if (pci_get_word(d->config + PCI_CLASS_DEVICE) == + PCI_CLASS_NETWORK_ETHERNET) { + qdev_unplug(&(d->qdev)); + } +} + +static void pci_unplug_nics(PCIBus *bus) +{ + pci_for_each_device(bus, 0, unplug_nic); +} + +static void unplug_disks(PCIBus *b, PCIDevice *d) +{ + if (pci_get_word(d->config + PCI_CLASS_DEVICE) == + PCI_CLASS_STORAGE_IDE) { + qdev_unplug(&(d->qdev)); + } +} + +static void pci_unplug_disks(PCIBus *bus) +{ + pci_for_each_device(bus, 0, unplug_disks); +} static void platform_fixed_ioport_writew(void *opaque, uint32_t addr, uint32_t val) { @@ -83,10 +112,22 @@ static void platform_fixed_ioport_writew(void *opaque, uint32_t addr, uint32_t v switch (addr - XEN_PLATFORM_IOPORT) { case 0: - /* TODO: */ /* Unplug devices. Value is a bitmask of which devices to unplug, with bit 0 the IDE devices, bit 1 the network devices, and bit 2 the non-primary-master IDE devices. */ + if (val & UNPLUG_ALL_IDE_DISKS) { + DPRINTF("unplug disks\n"); + qemu_aio_flush(); + bdrv_flush_all(); + pci_unplug_disks(s->pci_dev.bus); + } + if (val & UNPLUG_ALL_NICS) { + DPRINTF("unplug nics\n"); + pci_unplug_nics(s->pci_dev.bus); + } + if (val & UNPLUG_AUX_IDE_DISKS) { + DPRINTF("unplug auxiliary disks not supported\n"); + } break; case 2: switch (val) { From 0f94d6da357954857f95d5be69817d8551a5526f Mon Sep 17 00:00:00 2001 From: Alon Levy Date: Mon, 27 Jun 2011 11:58:20 +0200 Subject: [PATCH 068/230] libcacard: add pc file, install it + includes Additionally: + add --includedir configure parameters + make install-libcacard install vscclient as well --- configure | 5 +++++ libcacard/Makefile | 27 ++++++++++++++++++++++++--- libcacard/libcacard.pc.in | 13 +++++++++++++ 3 files changed, 42 insertions(+), 3 deletions(-) create mode 100644 libcacard/libcacard.pc.in diff --git a/configure b/configure index e57efb179c..1cc376703c 100755 --- a/configure +++ b/configure @@ -146,6 +146,7 @@ datadir="\${prefix}/share/qemu" docdir="\${prefix}/share/doc/qemu" bindir="\${prefix}/bin" libdir="\${prefix}/lib" +includedir="\${prefix}/include" sysconfdir="\${prefix}/etc" confsuffix="/qemu" slirp="yes" @@ -539,6 +540,8 @@ for opt do ;; --libdir=*) libdir="$optarg" ;; + --includedir=*) includedir="$optarg" + ;; --datadir=*) datadir="$optarg" ;; --docdir=*) docdir="$optarg" @@ -2542,6 +2545,7 @@ echo "Install prefix $prefix" echo "BIOS directory `eval echo $datadir`" echo "binary directory `eval echo $bindir`" echo "library directory `eval echo $libdir`" +echo "include directory `eval echo $includedir`" echo "config directory `eval echo $sysconfdir`" if test "$mingw32" = "no" ; then echo "Manual directory `eval echo $mandir`" @@ -2635,6 +2639,7 @@ echo all: >> $config_host_mak echo "prefix=$prefix" >> $config_host_mak echo "bindir=$bindir" >> $config_host_mak echo "libdir=$libdir" >> $config_host_mak +echo "includedir=$includedir" >> $config_host_mak echo "mandir=$mandir" >> $config_host_mak echo "datadir=$datadir" >> $config_host_mak echo "sysconfdir=$sysconfdir" >> $config_host_mak diff --git a/libcacard/Makefile b/libcacard/Makefile index 9802c37ee8..bc34bf2449 100644 --- a/libcacard/Makefile +++ b/libcacard/Makefile @@ -2,7 +2,10 @@ -include $(SRC_PATH)/Makefile.objs -include $(SRC_PATH)/rules.mak -$(call set-vpath, $(SRC_PATH):$(SRC_PATH)/libcacard) +libcacard_srcpath=$(SRC_PATH)/libcacard +libcacard_includedir=$(includedir)/cacard + +$(call set-vpath, $(SRC_PATH):$(libcacard_srcpath)) # objects linked against normal qemu binaries, not compiled with libtool QEMU_OBJS=$(addprefix ../,$(oslib-obj-y) qemu-malloc.o qemu-timer-common.o $(trace-obj-y)) @@ -18,7 +21,7 @@ vscclient: $(libcacard-y) $(QEMU_OBJS) vscclient.o $(call quiet-command,$(CC) $(libcacard_libs) -lrt -o $@ $^," LINK $@") clean: - rm -f *.o */*.o *.d */*.d *.a */*.a *~ */*~ vscclient *.lo .libs/* *.la + rm -f *.o */*.o *.d */*.d *.a */*.a *~ */*~ vscclient *.lo .libs/* *.la *.pc rm -Rf .libs all: vscclient @@ -36,7 +39,25 @@ else libcacard.la: $(libcacard.lib-y) $(QEMU_OBJS_LIB) $(call quiet-command,libtool --mode=link --quiet --tag=CC $(CC) $(libcacard_libs) -lrt -rpath $(libdir) -o $@ $^," lt LINK $@") -install-libcacard: libcacard.la +libcacard.pc: $(libcacard_srcpath)/libcacard.pc.in + sed -e 's|@LIBDIR@|$(libdir)|' \ + -e 's|@INCLUDEDIR@|$(libcacard_includedir)|' \ + -e 's|@VERSION@|$(shell cat $(SRC_PATH)/VERSION)|' \ + -e 's|@PREFIX@|$(prefix)|' \ + < $(libcacard_srcpath)/libcacard.pc.in > libcacard.pc + +.PHONY: install-libcacard + +install-libcacard: libcacard.pc libcacard.la vscclient $(INSTALL_DIR) "$(DESTDIR)$(libdir)" + $(INSTALL_DIR) "$(DESTDIR)$(libdir)/pkgconfig" + $(INSTALL_DIR) "$(DESTDIR)$(libcacard_includedir)" + $(INSTALL_DIR) "$(DESTDIR)$(bindir)" + libtool --mode=install $(INSTALL_PROG) vscclient "$(DESTDIR)$(bindir)" libtool --mode=install $(INSTALL_PROG) libcacard.la "$(DESTDIR)$(libdir)" + libtool --mode=install $(INSTALL_PROG) libcacard.pc "$(DESTDIR)$(libdir)/pkgconfig" + for inc in *.h; do \ + libtool --mode=install $(INSTALL_PROG) $(libcacard_srcpath)/$$inc "$(DESTDIR)$(libcacard_includedir)"; \ + done + endif diff --git a/libcacard/libcacard.pc.in b/libcacard/libcacard.pc.in new file mode 100644 index 0000000000..b6859b0c1f --- /dev/null +++ b/libcacard/libcacard.pc.in @@ -0,0 +1,13 @@ +prefix=@PREFIX@ +exec_prefix=${prefix} +libdir=@LIBDIR@ +includedir=@INCLUDEDIR@ + +Name: cacard +Description: CA Card library +Version: @VERSION@ + +Requires: nss +Libs: -L${libdir} -lcacard +Libs.private: +Cflags: -I${includedir} From 42e4126b793d15ec40f3a84017e1d8afecda1b6d Mon Sep 17 00:00:00 2001 From: Jan Kiszka Date: Fri, 22 Jul 2011 11:05:01 +0200 Subject: [PATCH 069/230] pci: Common overflow prevention Introduce pci_config_read/write_common helpers to prevent passing accesses down the callback chain that go beyond the config space limits. Adjust length assertions as they are no longer correct (cutting may generate valid 3 byte accesses). Signed-off-by: Jan Kiszka Signed-off-by: Michael S. Tsirkin --- hw/pci.c | 6 ++---- hw/pci_host.c | 24 ++++++++++++++++++++---- hw/pci_host.h | 6 ++++++ hw/pcie_host.c | 12 ++++++------ 4 files changed, 34 insertions(+), 14 deletions(-) diff --git a/hw/pci.c b/hw/pci.c index b904a4ecb6..ef94739718 100644 --- a/hw/pci.c +++ b/hw/pci.c @@ -1108,8 +1108,7 @@ uint32_t pci_default_read_config(PCIDevice *d, uint32_t address, int len) { uint32_t val = 0; - assert(len == 1 || len == 2 || len == 4); - len = MIN(len, pci_config_size(d) - address); + memcpy(&val, d->config + address, len); return le32_to_cpu(val); } @@ -1117,9 +1116,8 @@ uint32_t pci_default_read_config(PCIDevice *d, void pci_default_write_config(PCIDevice *d, uint32_t addr, uint32_t val, int l) { int i, was_irq_disabled = pci_irq_disabled(d); - uint32_t config_size = pci_config_size(d); - for (i = 0; i < l && addr + i < config_size; val >>= 8, ++i) { + for (i = 0; i < l; val >>= 8, ++i) { uint8_t wmask = d->wmask[addr + i]; uint8_t w1cmask = d->w1cmask[addr + i]; assert(!(wmask & w1cmask)); diff --git a/hw/pci_host.c b/hw/pci_host.c index 728e2d4ce5..2e8a29f1e3 100644 --- a/hw/pci_host.c +++ b/hw/pci_host.c @@ -47,17 +47,33 @@ static inline PCIDevice *pci_dev_find_by_addr(PCIBus *bus, uint32_t addr) return pci_find_device(bus, bus_num, devfn); } +void pci_host_config_write_common(PCIDevice *pci_dev, uint32_t addr, + uint32_t limit, uint32_t val, uint32_t len) +{ + assert(len <= 4); + pci_dev->config_write(pci_dev, addr, val, MIN(len, limit - addr)); +} + +uint32_t pci_host_config_read_common(PCIDevice *pci_dev, uint32_t addr, + uint32_t limit, uint32_t len) +{ + assert(len <= 4); + return pci_dev->config_read(pci_dev, addr, MIN(len, limit - addr)); +} + void pci_data_write(PCIBus *s, uint32_t addr, uint32_t val, int len) { PCIDevice *pci_dev = pci_dev_find_by_addr(s, addr); uint32_t config_addr = addr & (PCI_CONFIG_SPACE_SIZE - 1); - if (!pci_dev) + if (!pci_dev) { return; + } PCI_DPRINTF("%s: %s: addr=%02" PRIx32 " val=%08" PRIx32 " len=%d\n", __func__, pci_dev->name, config_addr, val, len); - pci_dev->config_write(pci_dev, config_addr, val, len); + pci_host_config_write_common(pci_dev, config_addr, PCI_CONFIG_SPACE_SIZE, + val, len); } uint32_t pci_data_read(PCIBus *s, uint32_t addr, int len) @@ -66,12 +82,12 @@ uint32_t pci_data_read(PCIBus *s, uint32_t addr, int len) uint32_t config_addr = addr & (PCI_CONFIG_SPACE_SIZE - 1); uint32_t val; - assert(len == 1 || len == 2 || len == 4); if (!pci_dev) { return ~0x0; } - val = pci_dev->config_read(pci_dev, config_addr, len); + val = pci_host_config_read_common(pci_dev, config_addr, + PCI_CONFIG_SPACE_SIZE, len); PCI_DPRINTF("%s: %s: addr=%02"PRIx32" val=%08"PRIx32" len=%d\n", __func__, pci_dev->name, config_addr, val, len); diff --git a/hw/pci_host.h b/hw/pci_host.h index 0a585951e0..c8390eec56 100644 --- a/hw/pci_host.h +++ b/hw/pci_host.h @@ -39,6 +39,12 @@ struct PCIHostState { PCIBus *bus; }; +/* common internal helpers for PCI/PCIe hosts, cut off overflows */ +void pci_host_config_write_common(PCIDevice *pci_dev, uint32_t addr, + uint32_t limit, uint32_t val, uint32_t len); +uint32_t pci_host_config_read_common(PCIDevice *pci_dev, uint32_t addr, + uint32_t limit, uint32_t len); + void pci_data_write(PCIBus *s, uint32_t addr, uint32_t val, int len); uint32_t pci_data_read(PCIBus *s, uint32_t addr, int len); diff --git a/hw/pcie_host.c b/hw/pcie_host.c index b7498656f2..f0b3d13aae 100644 --- a/hw/pcie_host.c +++ b/hw/pcie_host.c @@ -57,22 +57,22 @@ static void pcie_mmcfg_data_write(PCIBus *s, { PCIDevice *pci_dev = pcie_dev_find_by_mmcfg_addr(s, mmcfg_addr); - if (!pci_dev) + if (!pci_dev) { return; - - pci_dev->config_write(pci_dev, - PCIE_MMCFG_CONFOFFSET(mmcfg_addr), val, len); + } + pci_host_config_write_common(pci_dev, PCIE_MMCFG_CONFOFFSET(mmcfg_addr), + pci_config_size(pci_dev), val, len); } static uint32_t pcie_mmcfg_data_read(PCIBus *s, uint32_t addr, int len) { PCIDevice *pci_dev = pcie_dev_find_by_mmcfg_addr(s, addr); - assert(len == 1 || len == 2 || len == 4); if (!pci_dev) { return ~0x0; } - return pci_dev->config_read(pci_dev, PCIE_MMCFG_CONFOFFSET(addr), len); + return pci_host_config_read_common(pci_dev, PCIE_MMCFG_CONFOFFSET(addr), + pci_config_size(pci_dev), len); } static void pcie_mmcfg_data_writeb(void *opaque, From 023367e6cd41199521613674b44e9c703c8be1a1 Mon Sep 17 00:00:00 2001 From: Wolfgang Mauerer Date: Mon, 11 Jul 2011 14:57:43 +0200 Subject: [PATCH 070/230] vhost build fix for i386 vhost.c uses __sync_fetch_and_and(), which is only available for -march=i486 and above (see https://bugzilla.redhat.com/show_bug.cgi?id=624279). Signed-off-by: Wolfgang Mauerer Signed-off-by: Stefan Hajnoczi --- configure | 23 +++++++++++++++++++++++ 1 file changed, 23 insertions(+) diff --git a/configure b/configure index 38e3724f33..9bfe917c57 100755 --- a/configure +++ b/configure @@ -2509,6 +2509,29 @@ if test "$trace_backend" = "dtrace"; then fi fi +########################################## +# __sync_fetch_and_and requires at least -march=i486. Many toolchains +# use i686 as default anyway, but for those that don't, an explicit +# specification is necessary +if test $vhost_net = "yes" && test $cpu = "i386"; then + cat > $TMPC << EOF +int sfaa(unsigned *ptr) +{ + return __sync_fetch_and_and(ptr, 0); +} + +int main(int argc, char **argv) +{ + int val = 42; + sfaa(&val); + return val; +} +EOF + if ! compile_prog "" "" ; then + CFLAGS+="-march=i486" + fi +fi + ########################################## # End of CC checks # After here, no more $cc or $ld runs From 45b75ae4ee19a74c826a94e073762b5c7080a90c Mon Sep 17 00:00:00 2001 From: Alexandre Raymond Date: Wed, 20 Jul 2011 23:12:15 -0400 Subject: [PATCH 071/230] Makefile: Minor cscope fixups Create cscope symbols for assembly files in addition to .c/.h files. Create cscope database with full path instead of relative path so cscope can be used with CSCOPE_DB in any directory. Signed-off-by: Alexandre Raymond Signed-off-by: Stefan Hajnoczi --- Makefile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Makefile b/Makefile index daa3aa09b5..eb1c788d35 100644 --- a/Makefile +++ b/Makefile @@ -291,7 +291,7 @@ TAGS: cscope: rm -f ./cscope.* - find . -name "*.[ch]" -print | sed 's,^\./,,' > ./cscope.files + find "$(SRC_PATH)" -name "*.[chsS]" -print | sed 's,^\./,,' > ./cscope.files cscope -b # documentation From cf2846b5fa5cf85685e8a238323194b2ff9b5faf Mon Sep 17 00:00:00 2001 From: Stefan Weil Date: Thu, 21 Jul 2011 21:46:45 +0200 Subject: [PATCH 072/230] slirp: Fix unusual "comments" in unused code cppcheck detected two rather strange comments which were not correctly written as C comments. They did not cause any harm because they were framed by #ifdef notdef ... #endif, so they were never compiled. Fix them nevertheless (we could also remove the unused code). Signed-off-by: Stefan Weil Signed-off-by: Stefan Hajnoczi --- slirp/ip_input.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/slirp/ip_input.c b/slirp/ip_input.c index 5e67631ab4..c7b3eb4806 100644 --- a/slirp/ip_input.c +++ b/slirp/ip_input.c @@ -511,7 +511,7 @@ typedef uint32_t n_time; */ break; } - off--; / * 0 origin * / + off--; /* 0 origin */ if (off > optlen - sizeof(struct in_addr)) { /* * End of source route. Should be for us. @@ -554,7 +554,7 @@ typedef uint32_t n_time; /* * If no space remains, ignore. */ - off--; * 0 origin * + off--; /* 0 origin */ if (off > optlen - sizeof(struct in_addr)) break; bcopy((caddr_t)(&ip->ip_dst), (caddr_t)&ipaddr.sin_addr, From c20cdf8b91b45a4f60a5ceeaab31b830b02adb7a Mon Sep 17 00:00:00 2001 From: Zhi Yong Wu Date: Wed, 27 Jul 2011 14:32:56 +0800 Subject: [PATCH 073/230] qmp: fix efect -> effect typo in qmp-commands.hx Signed-off-by: Zhi Yong Wu Signed-off-by: Stefan Hajnoczi --- qmp-commands.hx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/qmp-commands.hx b/qmp-commands.hx index 54e313ce52..03f67da198 100644 --- a/qmp-commands.hx +++ b/qmp-commands.hx @@ -42,7 +42,7 @@ and we're going to establish a deprecation policy for badly defined commands. If you're planning to adopt QMP, please observe the following: - 1. The deprecation policy will take efect and be documented soon, please + 1. The deprecation policy will take effect and be documented soon, please check the documentation of each used command as soon as a new release of QEMU is available From 016c77ad62a8ad607dd4349d8cb8ad1365bab831 Mon Sep 17 00:00:00 2001 From: Michael Roth Date: Tue, 26 Jul 2011 11:39:24 -0500 Subject: [PATCH 074/230] Makefile: add missing deps on $(GENERATED_HEADERS) This fixes a build issue with make -j6+ due to qapi-generated files being built before $(GENERATED_HEADERS) have been created. Tested-by: Stefan Berger Signed-off-by: Michael Roth Signed-off-by: Stefan Hajnoczi --- Makefile | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/Makefile b/Makefile index eb1c788d35..48552512d6 100644 --- a/Makefile +++ b/Makefile @@ -192,8 +192,10 @@ test-qmp-commands.o: $(addprefix $(qapi-dir)/, test-qapi-types.c test-qapi-types test-qmp-commands: test-qmp-commands.o qfloat.o qint.o qdict.o qstring.o qlist.o qbool.o $(qapi-obj-y) error.o osdep.o qemu-malloc.o $(oslib-obj-y) qjson.o json-streamer.o json-lexer.o json-parser.o qerror.o qemu-error.o qemu-tool.o $(qapi-dir)/test-qapi-visit.o $(qapi-dir)/test-qapi-types.o $(qapi-dir)/test-qmp-marshal.o module.o QGALIB=qga/guest-agent-command-state.o qga/guest-agent-commands.o +QGALIB_GEN=$(addprefix $(qapi-dir)/, qga-qapi-types.c qga-qapi-types.h qga-qapi-visit.c qga-qmp-marshal.c) -qemu-ga.o: $(addprefix $(qapi-dir)/, qga-qapi-types.c qga-qapi-types.h qga-qapi-visit.c qga-qmp-marshal.c) $(qapi-obj-y) +$(QGALIB_GEN): $(GENERATED_HEADERS) +$(QGALIB) qemu-ga.o: $(QGALIB_GEN) $(qapi-obj-y) qemu-ga$(EXESUF): qemu-ga.o $(QGALIB) qemu-tool.o qemu-error.o error.o $(oslib-obj-y) $(trace-obj-y) $(block-obj-y) $(qobject-obj-y) $(version-obj-y) $(qapi-obj-y) qemu-timer-common.o qemu-sockets.o module.o qapi/qmp-dispatch.o qapi/qmp-registry.o $(qapi-dir)/qga-qapi-visit.o $(qapi-dir)/qga-qapi-types.o $(qapi-dir)/qga-qmp-marshal.o QEMULIBS=libhw32 libhw64 libuser libdis libdis-user From ec67464c4f137f58c040d1d351f540268e883b85 Mon Sep 17 00:00:00 2001 From: Juan Quintela Date: Mon, 11 Jul 2011 18:15:11 +0200 Subject: [PATCH 075/230] xen_mapcache: remove unused variable Signed-off-by: Juan Quintela Acked-by: Stefano Stabellini Signed-off-by: Stefan Hajnoczi --- xen-mapcache.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/xen-mapcache.c b/xen-mapcache.c index 007136af26..15d12413d4 100644 --- a/xen-mapcache.c +++ b/xen-mapcache.c @@ -237,7 +237,7 @@ uint8_t *xen_map_cache(target_phys_addr_t phys_addr, target_phys_addr_t size, ram_addr_t xen_ram_addr_from_mapcache(void *ptr) { - MapCacheEntry *entry = NULL, *pentry = NULL; + MapCacheEntry *entry = NULL; MapCacheRev *reventry; target_phys_addr_t paddr_index; target_phys_addr_t size; @@ -263,7 +263,6 @@ ram_addr_t xen_ram_addr_from_mapcache(void *ptr) entry = &mapcache->entry[paddr_index % mapcache->nr_buckets]; while (entry && (entry->paddr_index != paddr_index || entry->size != size)) { - pentry = entry; entry = entry->next; } if (!entry) { From 1129714ff43bd947740d587956a655210e8b93ed Mon Sep 17 00:00:00 2001 From: "Michael S. Tsirkin" Date: Wed, 27 Jul 2011 11:08:20 +0300 Subject: [PATCH 076/230] virtio-pci: use generic logic for command access In practice, guests don't generate config requests that cross a word boundary, so the logic to detect command word access is correct because PCI_COMMAND is 0x4. But depending on this is tricky, further, it will break with guests that do try to generate a misaligned access as we pass it to devices without splitting. Better to use the generic range_covers_byte for this. Signed-off-by: Michael S. Tsirkin --- hw/virtio-pci.c | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/hw/virtio-pci.c b/hw/virtio-pci.c index d685243728..4f770fe185 100644 --- a/hw/virtio-pci.c +++ b/hw/virtio-pci.c @@ -27,6 +27,7 @@ #include "kvm.h" #include "blockdev.h" #include "virtio-pci.h" +#include "range.h" /* from Linux's linux/virtio_pci.h */ @@ -516,17 +517,16 @@ static void virtio_write_config(PCIDevice *pci_dev, uint32_t address, { VirtIOPCIProxy *proxy = DO_UPCAST(VirtIOPCIProxy, pci_dev, pci_dev); - if (PCI_COMMAND == address) { - if (!(val & PCI_COMMAND_MASTER)) { - if (!(proxy->flags & VIRTIO_PCI_FLAG_BUS_MASTER_BUG)) { - virtio_pci_stop_ioeventfd(proxy); - virtio_set_status(proxy->vdev, - proxy->vdev->status & ~VIRTIO_CONFIG_S_DRIVER_OK); - } - } + pci_default_write_config(pci_dev, address, val, len); + + if (range_covers_byte(address, len, PCI_COMMAND) && + !(pci_dev->config[PCI_COMMAND] & PCI_COMMAND_MASTER) && + !(proxy->flags & VIRTIO_PCI_FLAG_BUS_MASTER_BUG)) { + virtio_pci_stop_ioeventfd(proxy); + virtio_set_status(proxy->vdev, + proxy->vdev->status & ~VIRTIO_CONFIG_S_DRIVER_OK); } - pci_default_write_config(pci_dev, address, val, len); msix_write_config(pci_dev, address, val, len); } From d92551f28eff7cb6572ed3147399e51f5f5dfc22 Mon Sep 17 00:00:00 2001 From: Amit Shah Date: Wed, 27 Jul 2011 14:00:30 +0530 Subject: [PATCH 077/230] virtio-blk: Fix memleak on exit Calling virtio_cleanup() will free up memory allocated in virtio_common_init(). Signed-off-by: Amit Shah Signed-off-by: Michael S. Tsirkin --- hw/virtio-blk.c | 1 + 1 file changed, 1 insertion(+) diff --git a/hw/virtio-blk.c b/hw/virtio-blk.c index 6471ac85ab..836dbc3c12 100644 --- a/hw/virtio-blk.c +++ b/hw/virtio-blk.c @@ -594,4 +594,5 @@ void virtio_blk_exit(VirtIODevice *vdev) { VirtIOBlock *s = to_virtio_blk(vdev); unregister_savevm(s->qdev, "virtio-blk", s); + virtio_cleanup(vdev); } From b52dfd71f33b902e612b12f6cc89f3b61e4d3e22 Mon Sep 17 00:00:00 2001 From: Amit Shah Date: Wed, 27 Jul 2011 14:00:31 +0530 Subject: [PATCH 078/230] virtio-net: don't use vdev after virtio_cleanup virtio_cleanup() will be changed by the following patch to remove the VirtIONet struct that gets allocated via virtio_common_init(). Ensure we don't dereference the structure after calling the cleanup function. Signed-off-by: Amit Shah Signed-off-by: Michael S. Tsirkin --- hw/virtio-net.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/hw/virtio-net.c b/hw/virtio-net.c index 6997e02dcf..09c665babe 100644 --- a/hw/virtio-net.c +++ b/hw/virtio-net.c @@ -1073,6 +1073,6 @@ void virtio_net_exit(VirtIODevice *vdev) qemu_bh_delete(n->tx_bh); } - virtio_cleanup(&n->vdev); qemu_del_vlan_client(&n->nic->nc); + virtio_cleanup(&n->vdev); } From 845f85fa1597c72609bd10a37b9586b445c13d49 Mon Sep 17 00:00:00 2001 From: Amit Shah Date: Wed, 27 Jul 2011 14:00:32 +0530 Subject: [PATCH 079/230] virtio: Plug memleak by freeing vdev virtio_common_init() allocates RAM for the vdev struct (and any additional memory, depending on the size passed to the function). This memory wasn't being freed until now. Signed-off-by: Amit Shah Signed-off-by: Michael S. Tsirkin --- hw/virtio.c | 1 + 1 file changed, 1 insertion(+) diff --git a/hw/virtio.c b/hw/virtio.c index a8f4940da2..93dfb1e359 100644 --- a/hw/virtio.c +++ b/hw/virtio.c @@ -834,6 +834,7 @@ void virtio_cleanup(VirtIODevice *vdev) if (vdev->config) qemu_free(vdev->config); qemu_free(vdev->vq); + qemu_free(vdev); } static void virtio_vmstate_change(void *opaque, int running, int reason) From 43e86c8f5b6d9f6279e20dede4e1f7829bdc43b7 Mon Sep 17 00:00:00 2001 From: Isaku Yamahata Date: Fri, 29 Jul 2011 10:01:43 +0900 Subject: [PATCH 080/230] pcie_host: verify mmcfg address range For a conventional pci device behind a pcie-to-pci bridge, pci_host handlers get confused by an out of bounds access in the range [256, 4K). Check for such an access and make it have no effect. Signed-off-by: Isaku Yamahata Signed-off-by: Michael S. Tsirkin --- hw/pcie_host.c | 28 ++++++++++++++++++++++------ 1 file changed, 22 insertions(+), 6 deletions(-) diff --git a/hw/pcie_host.c b/hw/pcie_host.c index f0b3d13aae..f9fea3d918 100644 --- a/hw/pcie_host.c +++ b/hw/pcie_host.c @@ -56,23 +56,39 @@ static void pcie_mmcfg_data_write(PCIBus *s, uint32_t mmcfg_addr, uint32_t val, int len) { PCIDevice *pci_dev = pcie_dev_find_by_mmcfg_addr(s, mmcfg_addr); + uint32_t addr; + uint32_t limit; if (!pci_dev) { return; } - pci_host_config_write_common(pci_dev, PCIE_MMCFG_CONFOFFSET(mmcfg_addr), - pci_config_size(pci_dev), val, len); + addr = PCIE_MMCFG_CONFOFFSET(mmcfg_addr); + limit = pci_config_size(pci_dev); + if (limit <= addr) { + /* conventional pci device can be behind pcie-to-pci bridge. + 256 <= addr < 4K has no effects. */ + return; + } + pci_host_config_write_common(pci_dev, addr, limit, val, len); } -static uint32_t pcie_mmcfg_data_read(PCIBus *s, uint32_t addr, int len) +static uint32_t pcie_mmcfg_data_read(PCIBus *s, uint32_t mmcfg_addr, int len) { - PCIDevice *pci_dev = pcie_dev_find_by_mmcfg_addr(s, addr); + PCIDevice *pci_dev = pcie_dev_find_by_mmcfg_addr(s, mmcfg_addr); + uint32_t addr; + uint32_t limit; if (!pci_dev) { return ~0x0; } - return pci_host_config_read_common(pci_dev, PCIE_MMCFG_CONFOFFSET(addr), - pci_config_size(pci_dev), len); + addr = PCIE_MMCFG_CONFOFFSET(mmcfg_addr); + limit = pci_config_size(pci_dev); + if (limit <= addr) { + /* conventional pci device can be behind pcie-to-pci bridge. + 256 <= addr < 4K has no effects. */ + return ~0x0; + } + return pci_host_config_read_common(pci_dev, addr, limit, len); } static void pcie_mmcfg_data_writeb(void *opaque, From 5ab28c8340f683121c081a181adfd9f72ab85cba Mon Sep 17 00:00:00 2001 From: Jan Kiszka Date: Sun, 24 Jul 2011 19:38:36 +0200 Subject: [PATCH 081/230] qdev: Reset hot-plugged devices Device models rely on the core invoking their reset handlers after init. We do this in the cold-plug case, but so far we miss this step after hot-plug. Signed-off-by: Jan Kiszka Signed-off-by: Anthony Liguori --- hw/qdev.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/hw/qdev.c b/hw/qdev.c index a0fcd06094..b4ea8e13d1 100644 --- a/hw/qdev.c +++ b/hw/qdev.c @@ -289,6 +289,9 @@ int qdev_init(DeviceState *dev) dev->alias_required_for_version); } dev->state = DEV_STATE_INITIALIZED; + if (dev->hotplugged && dev->info->reset) { + dev->info->reset(dev); + } return 0; } From 9d3a4736cb86f0ad7904b223eccb165de8d4327b Mon Sep 17 00:00:00 2001 From: Avi Kivity Date: Tue, 26 Jul 2011 14:26:00 +0300 Subject: [PATCH 082/230] Add memory API documentation Signed-off-by: Avi Kivity Signed-off-by: Anthony Liguori --- docs/memory.txt | 172 ++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 172 insertions(+) create mode 100644 docs/memory.txt diff --git a/docs/memory.txt b/docs/memory.txt new file mode 100644 index 0000000000..4460c0641a --- /dev/null +++ b/docs/memory.txt @@ -0,0 +1,172 @@ +The memory API +============== + +The memory API models the memory and I/O buses and controllers of a QEMU +machine. It attempts to allow modelling of: + + - ordinary RAM + - memory-mapped I/O (MMIO) + - memory controllers that can dynamically reroute physical memory regions + to different destinations + +The memory model provides support for + + - tracking RAM changes by the guest + - setting up coalesced memory for kvm + - setting up ioeventfd regions for kvm + +Memory is modelled as an tree (really acyclic graph) of MemoryRegion objects. +The root of the tree is memory as seen from the CPU's viewpoint (the system +bus). Nodes in the tree represent other buses, memory controllers, and +memory regions that have been rerouted. Leaves are RAM and MMIO regions. + +Types of regions +---------------- + +There are four types of memory regions (all represented by a single C type +MemoryRegion): + +- RAM: a RAM region is simply a range of host memory that can be made available + to the guest. + +- MMIO: a range of guest memory that is implemented by host callbacks; + each read or write causes a callback to be called on the host. + +- container: a container simply includes other memory regions, each at + a different offset. Containers are useful for grouping several regions + into one unit. For example, a PCI BAR may be composed of a RAM region + and an MMIO region. + + A container's subregions are usually non-overlapping. In some cases it is + useful to have overlapping regions; for example a memory controller that + can overlay a subregion of RAM with MMIO or ROM, or a PCI controller + that does not prevent card from claiming overlapping BARs. + +- alias: a subsection of another region. Aliases allow a region to be + split apart into discontiguous regions. Examples of uses are memory banks + used when the guest address space is smaller than the amount of RAM + addressed, or a memory controller that splits main memory to expose a "PCI + hole". Aliases may point to any type of region, including other aliases, + but an alias may not point back to itself, directly or indirectly. + + +Region names +------------ + +Regions are assigned names by the constructor. For most regions these are +only used for debugging purposes, but RAM regions also use the name to identify +live migration sections. This means that RAM region names need to have ABI +stability. + +Region lifecycle +---------------- + +A region is created by one of the constructor functions (memory_region_init*()) +and destroyed by the destructor (memory_region_destroy()). In between, +a region can be added to an address space by using memory_region_add_subregion() +and removed using memory_region_del_subregion(). Region attributes may be +changed at any point; they take effect once the region becomes exposed to the +guest. + +Overlapping regions and priority +-------------------------------- +Usually, regions may not overlap each other; a memory address decodes into +exactly one target. In some cases it is useful to allow regions to overlap, +and sometimes to control which of an overlapping regions is visible to the +guest. This is done with memory_region_add_subregion_overlap(), which +allows the region to overlap any other region in the same container, and +specifies a priority that allows the core to decide which of two regions at +the same address are visible (highest wins). + +Visibility +---------- +The memory core uses the following rules to select a memory region when the +guest accesses an address: + +- all direct subregions of the root region are matched against the address, in + descending priority order + - if the address lies outside the region offset/size, the subregion is + discarded + - if the subregion is a leaf (RAM or MMIO), the seach terminates + - if the subregion is a container, the same algorithm is used within the + subregion (after the address is adjusted by the subregion offset) + - if the subregion is an alias, the search is continues at the alias target + (after the address is adjusted by the subregion offset and alias offset) + +Example memory map +------------------ + +system_memory: container@0-2^48-1 + | + +---- lomem: alias@0-0xdfffffff ---> #ram (0-0xdfffffff) + | + +---- himem: alias@0x100000000-0x11fffffff ---> #ram (0xe0000000-0xffffffff) + | + +---- vga-window: alias@0xa0000-0xbfffff ---> #pci (0xa0000-0xbffff) + | (prio 1) + | + +---- pci-hole: alias@0xe0000000-0xffffffff ---> #pci (0xe0000000-0xffffffff) + +pci (0-2^32-1) + | + +--- vga-area: container@0xa0000-0xbffff + | | + | +--- alias@0x00000-0x7fff ---> #vram (0x010000-0x017fff) + | | + | +--- alias@0x08000-0xffff ---> #vram (0x020000-0x027fff) + | + +---- vram: ram@0xe1000000-0xe1ffffff + | + +---- vga-mmio: mmio@0xe2000000-0xe200ffff + +ram: ram@0x00000000-0xffffffff + +The is a (simplified) PC memory map. The 4GB RAM block is mapped into the +system address space via two aliases: "lomem" is a 1:1 mapping of the first +3.5GB; "himem" maps the last 0.5GB at address 4GB. This leaves 0.5GB for the +so-called PCI hole, that allows a 32-bit PCI bus to exist in a system with +4GB of memory. + +The memory controller diverts addresses in the range 640K-768K to the PCI +address space. This is modeled using the "vga-window" alias, mapped at a +higher priority so it obscures the RAM at the same addresses. The vga window +can be removed by programming the memory controller; this is modelled by +removing the alias and exposing the RAM underneath. + +The pci address space is not a direct child of the system address space, since +we only want parts of it to be visible (we accomplish this using aliases). +It has two subregions: vga-area models the legacy vga window and is occupied +by two 32K memory banks pointing at two sections of the framebuffer. +In addition the vram is mapped as a BAR at address e1000000, and an additional +BAR containing MMIO registers is mapped after it. + +Note that if the guest maps a BAR outside the PCI hole, it would not be +visible as the pci-hole alias clips it to a 0.5GB range. + +Attributes +---------- + +Various region attributes (read-only, dirty logging, coalesced mmio, ioeventfd) +can be changed during the region lifecycle. They take effect once the region +is made visible (which can be immediately, later, or never). + +MMIO Operations +--------------- + +MMIO regions are provided with ->read() and ->write() callbacks; in addition +various constraints can be supplied to control how these callbacks are called: + + - .valid.min_access_size, .valid.max_access_size define the access sizes + (in bytes) which the device accepts; accesses outside this range will + have device and bus specific behaviour (ignored, or machine check) + - .valid.aligned specifies that the device only accepts naturally aligned + accesses. Unaligned accesses invoke device and bus specific behaviour. + - .impl.min_access_size, .impl.max_access_size define the access sizes + (in bytes) supported by the *implementation*; other access sizes will be + emulated using the ones available. For example a 4-byte write will be + emulated using four 1-byte write, is .impl.max_access_size = 1. + - .impl.valid specifies that the *implementation* only supports unaligned + accesses; unaligned accesses will be emulated by two aligned accesses. + - .old_portio and .old_mmio can be used to ease porting from code using + cpu_register_io_memory() and register_ioport(). They should not be used + in new code. From 093bc2cd885e4e3420509a80a1b9e81848e4b8fe Mon Sep 17 00:00:00 2001 From: Avi Kivity Date: Tue, 26 Jul 2011 14:26:01 +0300 Subject: [PATCH 083/230] Hierarchical memory region API The memory API separates the attributes of a memory region (its size, how reads or writes are handled, dirty logging, and coalescing) from where it is mapped and whether it is enabled. This allows a device to configure a memory region once, then hand it off to its parent bus to map it according to the bus configuration. Hierarchical registration also allows a device to compose a region out of a number of sub-regions with different properties; for example some may be RAM while others may be MMIO. Reviewed-by: Anthony Liguori Signed-off-by: Avi Kivity Signed-off-by: Anthony Liguori --- Makefile.target | 1 + memory.c | 653 ++++++++++++++++++++++++++++++++++++++++++++++++ memory.h | 385 ++++++++++++++++++++++++++++ 3 files changed, 1039 insertions(+) create mode 100644 memory.c create mode 100644 memory.h diff --git a/Makefile.target b/Makefile.target index cde509ba76..8884a56aa3 100644 --- a/Makefile.target +++ b/Makefile.target @@ -198,6 +198,7 @@ obj-$(CONFIG_REALLY_VIRTFS) += 9pfs/virtio-9p-device.o obj-y += rwhandler.o obj-$(CONFIG_KVM) += kvm.o kvm-all.o obj-$(CONFIG_NO_KVM) += kvm-stub.o +obj-y += memory.o LIBS+=-lz QEMU_CFLAGS += $(VNC_TLS_CFLAGS) diff --git a/memory.c b/memory.c new file mode 100644 index 0000000000..a9cf3176e2 --- /dev/null +++ b/memory.c @@ -0,0 +1,653 @@ +/* + * Physical memory management + * + * Copyright 2011 Red Hat, Inc. and/or its affiliates + * + * Authors: + * Avi Kivity + * + * This work is licensed under the terms of the GNU GPL, version 2. See + * the COPYING file in the top-level directory. + * + */ + +#include "memory.h" +#include + +typedef struct AddrRange AddrRange; + +struct AddrRange { + uint64_t start; + uint64_t size; +}; + +static AddrRange addrrange_make(uint64_t start, uint64_t size) +{ + return (AddrRange) { start, size }; +} + +static bool addrrange_equal(AddrRange r1, AddrRange r2) +{ + return r1.start == r2.start && r1.size == r2.size; +} + +static uint64_t addrrange_end(AddrRange r) +{ + return r.start + r.size; +} + +static AddrRange addrrange_shift(AddrRange range, int64_t delta) +{ + range.start += delta; + return range; +} + +static bool addrrange_intersects(AddrRange r1, AddrRange r2) +{ + return (r1.start >= r2.start && r1.start < r2.start + r2.size) + || (r2.start >= r1.start && r2.start < r1.start + r1.size); +} + +static AddrRange addrrange_intersection(AddrRange r1, AddrRange r2) +{ + uint64_t start = MAX(r1.start, r2.start); + /* off-by-one arithmetic to prevent overflow */ + uint64_t end = MIN(addrrange_end(r1) - 1, addrrange_end(r2) - 1); + return addrrange_make(start, end - start + 1); +} + +struct CoalescedMemoryRange { + AddrRange addr; + QTAILQ_ENTRY(CoalescedMemoryRange) link; +}; + +typedef struct FlatRange FlatRange; +typedef struct FlatView FlatView; + +/* Range of memory in the global map. Addresses are absolute. */ +struct FlatRange { + MemoryRegion *mr; + target_phys_addr_t offset_in_region; + AddrRange addr; +}; + +/* Flattened global view of current active memory hierarchy. Kept in sorted + * order. + */ +struct FlatView { + FlatRange *ranges; + unsigned nr; + unsigned nr_allocated; +}; + +#define FOR_EACH_FLAT_RANGE(var, view) \ + for (var = (view)->ranges; var < (view)->ranges + (view)->nr; ++var) + +static FlatView current_memory_map; +static MemoryRegion *root_memory_region; + +static bool flatrange_equal(FlatRange *a, FlatRange *b) +{ + return a->mr == b->mr + && addrrange_equal(a->addr, b->addr) + && a->offset_in_region == b->offset_in_region; +} + +static void flatview_init(FlatView *view) +{ + view->ranges = NULL; + view->nr = 0; + view->nr_allocated = 0; +} + +/* Insert a range into a given position. Caller is responsible for maintaining + * sorting order. + */ +static void flatview_insert(FlatView *view, unsigned pos, FlatRange *range) +{ + if (view->nr == view->nr_allocated) { + view->nr_allocated = MAX(2 * view->nr, 10); + view->ranges = qemu_realloc(view->ranges, + view->nr_allocated * sizeof(*view->ranges)); + } + memmove(view->ranges + pos + 1, view->ranges + pos, + (view->nr - pos) * sizeof(FlatRange)); + view->ranges[pos] = *range; + ++view->nr; +} + +static void flatview_destroy(FlatView *view) +{ + qemu_free(view->ranges); +} + +/* Render a memory region into the global view. Ranges in @view obscure + * ranges in @mr. + */ +static void render_memory_region(FlatView *view, + MemoryRegion *mr, + target_phys_addr_t base, + AddrRange clip) +{ + MemoryRegion *subregion; + unsigned i; + target_phys_addr_t offset_in_region; + uint64_t remain; + uint64_t now; + FlatRange fr; + AddrRange tmp; + + base += mr->addr; + + tmp = addrrange_make(base, mr->size); + + if (!addrrange_intersects(tmp, clip)) { + return; + } + + clip = addrrange_intersection(tmp, clip); + + if (mr->alias) { + base -= mr->alias->addr; + base -= mr->alias_offset; + render_memory_region(view, mr->alias, base, clip); + return; + } + + /* Render subregions in priority order. */ + QTAILQ_FOREACH(subregion, &mr->subregions, subregions_link) { + render_memory_region(view, subregion, base, clip); + } + + if (!mr->has_ram_addr) { + return; + } + + offset_in_region = clip.start - base; + base = clip.start; + remain = clip.size; + + /* Render the region itself into any gaps left by the current view. */ + for (i = 0; i < view->nr && remain; ++i) { + if (base >= addrrange_end(view->ranges[i].addr)) { + continue; + } + if (base < view->ranges[i].addr.start) { + now = MIN(remain, view->ranges[i].addr.start - base); + fr.mr = mr; + fr.offset_in_region = offset_in_region; + fr.addr = addrrange_make(base, now); + flatview_insert(view, i, &fr); + ++i; + base += now; + offset_in_region += now; + remain -= now; + } + if (base == view->ranges[i].addr.start) { + now = MIN(remain, view->ranges[i].addr.size); + base += now; + offset_in_region += now; + remain -= now; + } + } + if (remain) { + fr.mr = mr; + fr.offset_in_region = offset_in_region; + fr.addr = addrrange_make(base, remain); + flatview_insert(view, i, &fr); + } +} + +/* Render a memory topology into a list of disjoint absolute ranges. */ +static FlatView generate_memory_topology(MemoryRegion *mr) +{ + FlatView view; + + flatview_init(&view); + + render_memory_region(&view, mr, 0, addrrange_make(0, UINT64_MAX)); + + return view; +} + +static void memory_region_update_topology(void) +{ + FlatView old_view = current_memory_map; + FlatView new_view = generate_memory_topology(root_memory_region); + unsigned iold, inew; + FlatRange *frold, *frnew; + ram_addr_t phys_offset, region_offset; + + /* Generate a symmetric difference of the old and new memory maps. + * Kill ranges in the old map, and instantiate ranges in the new map. + */ + iold = inew = 0; + while (iold < old_view.nr || inew < new_view.nr) { + if (iold < old_view.nr) { + frold = &old_view.ranges[iold]; + } else { + frold = NULL; + } + if (inew < new_view.nr) { + frnew = &new_view.ranges[inew]; + } else { + frnew = NULL; + } + + if (frold + && (!frnew + || frold->addr.start < frnew->addr.start + || (frold->addr.start == frnew->addr.start + && !flatrange_equal(frold, frnew)))) { + /* In old, but (not in new, or in new but attributes changed). */ + + cpu_register_physical_memory(frold->addr.start, frold->addr.size, + IO_MEM_UNASSIGNED); + ++iold; + } else if (frold && frnew && flatrange_equal(frold, frnew)) { + /* In both (logging may have changed) */ + + ++iold; + ++inew; + /* FIXME: dirty logging */ + } else { + /* In new */ + + phys_offset = frnew->mr->ram_addr; + region_offset = frnew->offset_in_region; + /* cpu_register_physical_memory_log() wants region_offset for + * mmio, but prefers offseting phys_offset for RAM. Humour it. + */ + if ((phys_offset & ~TARGET_PAGE_MASK) <= IO_MEM_ROM) { + phys_offset += region_offset; + region_offset = 0; + } + + cpu_register_physical_memory_log(frnew->addr.start, + frnew->addr.size, + phys_offset, + region_offset, + 0); + ++inew; + } + } + current_memory_map = new_view; + flatview_destroy(&old_view); +} + +void memory_region_init(MemoryRegion *mr, + const char *name, + uint64_t size) +{ + mr->ops = NULL; + mr->parent = NULL; + mr->size = size; + mr->addr = 0; + mr->offset = 0; + mr->has_ram_addr = false; + mr->priority = 0; + mr->may_overlap = false; + mr->alias = NULL; + QTAILQ_INIT(&mr->subregions); + memset(&mr->subregions_link, 0, sizeof mr->subregions_link); + QTAILQ_INIT(&mr->coalesced); + mr->name = qemu_strdup(name); +} + +static bool memory_region_access_valid(MemoryRegion *mr, + target_phys_addr_t addr, + unsigned size) +{ + if (!mr->ops->valid.unaligned && (addr & (size - 1))) { + return false; + } + + /* Treat zero as compatibility all valid */ + if (!mr->ops->valid.max_access_size) { + return true; + } + + if (size > mr->ops->valid.max_access_size + || size < mr->ops->valid.min_access_size) { + return false; + } + return true; +} + +static uint32_t memory_region_read_thunk_n(void *_mr, + target_phys_addr_t addr, + unsigned size) +{ + MemoryRegion *mr = _mr; + unsigned access_size, access_size_min, access_size_max; + uint64_t access_mask; + uint32_t data = 0, tmp; + unsigned i; + + if (!memory_region_access_valid(mr, addr, size)) { + return -1U; /* FIXME: better signalling */ + } + + /* FIXME: support unaligned access */ + + access_size_min = mr->ops->impl.min_access_size; + if (!access_size_min) { + access_size_min = 1; + } + access_size_max = mr->ops->impl.max_access_size; + if (!access_size_max) { + access_size_max = 4; + } + access_size = MAX(MIN(size, access_size_max), access_size_min); + access_mask = -1ULL >> (64 - access_size * 8); + addr += mr->offset; + for (i = 0; i < size; i += access_size) { + /* FIXME: big-endian support */ + tmp = mr->ops->read(mr->opaque, addr + i, access_size); + data |= (tmp & access_mask) << (i * 8); + } + + return data; +} + +static void memory_region_write_thunk_n(void *_mr, + target_phys_addr_t addr, + unsigned size, + uint64_t data) +{ + MemoryRegion *mr = _mr; + unsigned access_size, access_size_min, access_size_max; + uint64_t access_mask; + unsigned i; + + if (!memory_region_access_valid(mr, addr, size)) { + return; /* FIXME: better signalling */ + } + + /* FIXME: support unaligned access */ + + access_size_min = mr->ops->impl.min_access_size; + if (!access_size_min) { + access_size_min = 1; + } + access_size_max = mr->ops->impl.max_access_size; + if (!access_size_max) { + access_size_max = 4; + } + access_size = MAX(MIN(size, access_size_max), access_size_min); + access_mask = -1ULL >> (64 - access_size * 8); + addr += mr->offset; + for (i = 0; i < size; i += access_size) { + /* FIXME: big-endian support */ + mr->ops->write(mr->opaque, addr + i, (data >> (i * 8)) & access_mask, + access_size); + } +} + +static uint32_t memory_region_read_thunk_b(void *mr, target_phys_addr_t addr) +{ + return memory_region_read_thunk_n(mr, addr, 1); +} + +static uint32_t memory_region_read_thunk_w(void *mr, target_phys_addr_t addr) +{ + return memory_region_read_thunk_n(mr, addr, 2); +} + +static uint32_t memory_region_read_thunk_l(void *mr, target_phys_addr_t addr) +{ + return memory_region_read_thunk_n(mr, addr, 4); +} + +static void memory_region_write_thunk_b(void *mr, target_phys_addr_t addr, + uint32_t data) +{ + memory_region_write_thunk_n(mr, addr, 1, data); +} + +static void memory_region_write_thunk_w(void *mr, target_phys_addr_t addr, + uint32_t data) +{ + memory_region_write_thunk_n(mr, addr, 2, data); +} + +static void memory_region_write_thunk_l(void *mr, target_phys_addr_t addr, + uint32_t data) +{ + memory_region_write_thunk_n(mr, addr, 4, data); +} + +static CPUReadMemoryFunc * const memory_region_read_thunk[] = { + memory_region_read_thunk_b, + memory_region_read_thunk_w, + memory_region_read_thunk_l, +}; + +static CPUWriteMemoryFunc * const memory_region_write_thunk[] = { + memory_region_write_thunk_b, + memory_region_write_thunk_w, + memory_region_write_thunk_l, +}; + +void memory_region_init_io(MemoryRegion *mr, + const MemoryRegionOps *ops, + void *opaque, + const char *name, + uint64_t size) +{ + memory_region_init(mr, name, size); + mr->ops = ops; + mr->opaque = opaque; + mr->has_ram_addr = true; + mr->ram_addr = cpu_register_io_memory(memory_region_read_thunk, + memory_region_write_thunk, + mr, + mr->ops->endianness); +} + +void memory_region_init_ram(MemoryRegion *mr, + DeviceState *dev, + const char *name, + uint64_t size) +{ + memory_region_init(mr, name, size); + mr->has_ram_addr = true; + mr->ram_addr = qemu_ram_alloc(dev, name, size); +} + +void memory_region_init_ram_ptr(MemoryRegion *mr, + DeviceState *dev, + const char *name, + uint64_t size, + void *ptr) +{ + memory_region_init(mr, name, size); + mr->has_ram_addr = true; + mr->ram_addr = qemu_ram_alloc_from_ptr(dev, name, size, ptr); +} + +void memory_region_init_alias(MemoryRegion *mr, + const char *name, + MemoryRegion *orig, + target_phys_addr_t offset, + uint64_t size) +{ + memory_region_init(mr, name, size); + mr->alias = orig; + mr->alias_offset = offset; +} + +void memory_region_destroy(MemoryRegion *mr) +{ + assert(QTAILQ_EMPTY(&mr->subregions)); + memory_region_clear_coalescing(mr); + qemu_free((char *)mr->name); +} + +uint64_t memory_region_size(MemoryRegion *mr) +{ + return mr->size; +} + +void memory_region_set_offset(MemoryRegion *mr, target_phys_addr_t offset) +{ + mr->offset = offset; +} + +void memory_region_set_log(MemoryRegion *mr, bool log, unsigned client) +{ + /* FIXME */ +} + +bool memory_region_get_dirty(MemoryRegion *mr, target_phys_addr_t addr, + unsigned client) +{ + /* FIXME */ + return true; +} + +void memory_region_set_dirty(MemoryRegion *mr, target_phys_addr_t addr) +{ + /* FIXME */ +} + +void memory_region_sync_dirty_bitmap(MemoryRegion *mr) +{ + /* FIXME */ +} + +void memory_region_set_readonly(MemoryRegion *mr, bool readonly) +{ + /* FIXME */ +} + +void memory_region_reset_dirty(MemoryRegion *mr, target_phys_addr_t addr, + target_phys_addr_t size, unsigned client) +{ + /* FIXME */ +} + +void *memory_region_get_ram_ptr(MemoryRegion *mr) +{ + if (mr->alias) { + return memory_region_get_ram_ptr(mr->alias) + mr->alias_offset; + } + + assert(mr->has_ram_addr); + + return qemu_get_ram_ptr(mr->ram_addr); +} + +static void memory_region_update_coalesced_range(MemoryRegion *mr) +{ + FlatRange *fr; + CoalescedMemoryRange *cmr; + AddrRange tmp; + + FOR_EACH_FLAT_RANGE(fr, ¤t_memory_map) { + if (fr->mr == mr) { + qemu_unregister_coalesced_mmio(fr->addr.start, fr->addr.size); + QTAILQ_FOREACH(cmr, &mr->coalesced, link) { + tmp = addrrange_shift(cmr->addr, + fr->addr.start - fr->offset_in_region); + if (!addrrange_intersects(tmp, fr->addr)) { + continue; + } + tmp = addrrange_intersection(tmp, fr->addr); + qemu_register_coalesced_mmio(tmp.start, tmp.size); + } + } + } +} + +void memory_region_set_coalescing(MemoryRegion *mr) +{ + memory_region_clear_coalescing(mr); + memory_region_add_coalescing(mr, 0, mr->size); +} + +void memory_region_add_coalescing(MemoryRegion *mr, + target_phys_addr_t offset, + uint64_t size) +{ + CoalescedMemoryRange *cmr = qemu_malloc(sizeof(*cmr)); + + cmr->addr = addrrange_make(offset, size); + QTAILQ_INSERT_TAIL(&mr->coalesced, cmr, link); + memory_region_update_coalesced_range(mr); +} + +void memory_region_clear_coalescing(MemoryRegion *mr) +{ + CoalescedMemoryRange *cmr; + + while (!QTAILQ_EMPTY(&mr->coalesced)) { + cmr = QTAILQ_FIRST(&mr->coalesced); + QTAILQ_REMOVE(&mr->coalesced, cmr, link); + qemu_free(cmr); + } + memory_region_update_coalesced_range(mr); +} + +static void memory_region_add_subregion_common(MemoryRegion *mr, + target_phys_addr_t offset, + MemoryRegion *subregion) +{ + MemoryRegion *other; + + assert(!subregion->parent); + subregion->parent = mr; + subregion->addr = offset; + QTAILQ_FOREACH(other, &mr->subregions, subregions_link) { + if (subregion->may_overlap || other->may_overlap) { + continue; + } + if (offset >= other->offset + other->size + || offset + subregion->size <= other->offset) { + continue; + } + printf("warning: subregion collision %llx/%llx vs %llx/%llx\n", + (unsigned long long)offset, + (unsigned long long)subregion->size, + (unsigned long long)other->offset, + (unsigned long long)other->size); + } + QTAILQ_FOREACH(other, &mr->subregions, subregions_link) { + if (subregion->priority >= other->priority) { + QTAILQ_INSERT_BEFORE(other, subregion, subregions_link); + goto done; + } + } + QTAILQ_INSERT_TAIL(&mr->subregions, subregion, subregions_link); +done: + memory_region_update_topology(); +} + + +void memory_region_add_subregion(MemoryRegion *mr, + target_phys_addr_t offset, + MemoryRegion *subregion) +{ + subregion->may_overlap = false; + subregion->priority = 0; + memory_region_add_subregion_common(mr, offset, subregion); +} + +void memory_region_add_subregion_overlap(MemoryRegion *mr, + target_phys_addr_t offset, + MemoryRegion *subregion, + unsigned priority) +{ + subregion->may_overlap = true; + subregion->priority = priority; + memory_region_add_subregion_common(mr, offset, subregion); +} + +void memory_region_del_subregion(MemoryRegion *mr, + MemoryRegion *subregion) +{ + assert(subregion->parent == mr); + subregion->parent = NULL; + QTAILQ_REMOVE(&mr->subregions, subregion, subregions_link); + memory_region_update_topology(); +} diff --git a/memory.h b/memory.h new file mode 100644 index 0000000000..a4c94bda42 --- /dev/null +++ b/memory.h @@ -0,0 +1,385 @@ +/* + * Physical memory management API + * + * Copyright 2011 Red Hat, Inc. and/or its affiliates + * + * Authors: + * Avi Kivity + * + * This work is licensed under the terms of the GNU GPL, version 2. See + * the COPYING file in the top-level directory. + * + */ + +#ifndef MEMORY_H +#define MEMORY_H + +#ifndef CONFIG_USER_ONLY + +#include +#include +#include "qemu-common.h" +#include "cpu-common.h" +#include "targphys.h" +#include "qemu-queue.h" + +typedef struct MemoryRegionOps MemoryRegionOps; +typedef struct MemoryRegion MemoryRegion; + +/* Must match *_DIRTY_FLAGS in cpu-all.h. To be replaced with dynamic + * registration. + */ +#define DIRTY_MEMORY_VGA 0 +#define DIRTY_MEMORY_CODE 1 +#define DIRTY_MEMORY_MIGRATION 3 + +/* + * Memory region callbacks + */ +struct MemoryRegionOps { + /* Read from the memory region. @addr is relative to @mr; @size is + * in bytes. */ + uint64_t (*read)(void *opaque, + target_phys_addr_t addr, + unsigned size); + /* Write to the memory region. @addr is relative to @mr; @size is + * in bytes. */ + void (*write)(void *opaque, + target_phys_addr_t addr, + uint64_t data, + unsigned size); + + enum device_endian endianness; + /* Guest-visible constraints: */ + struct { + /* If nonzero, specify bounds on access sizes beyond which a machine + * check is thrown. + */ + unsigned min_access_size; + unsigned max_access_size; + /* If true, unaligned accesses are supported. Otherwise unaligned + * accesses throw machine checks. + */ + bool unaligned; + } valid; + /* Internal implementation constraints: */ + struct { + /* If nonzero, specifies the minimum size implemented. Smaller sizes + * will be rounded upwards and a partial result will be returned. + */ + unsigned min_access_size; + /* If nonzero, specifies the maximum size implemented. Larger sizes + * will be done as a series of accesses with smaller sizes. + */ + unsigned max_access_size; + /* If true, unaligned accesses are supported. Otherwise all accesses + * are converted to (possibly multiple) naturally aligned accesses. + */ + bool unaligned; + } impl; +}; + +typedef struct CoalescedMemoryRange CoalescedMemoryRange; + +struct MemoryRegion { + /* All fields are private - violators will be prosecuted */ + const MemoryRegionOps *ops; + void *opaque; + MemoryRegion *parent; + uint64_t size; + target_phys_addr_t addr; + target_phys_addr_t offset; + ram_addr_t ram_addr; + bool has_ram_addr; + MemoryRegion *alias; + target_phys_addr_t alias_offset; + unsigned priority; + bool may_overlap; + QTAILQ_HEAD(subregions, MemoryRegion) subregions; + QTAILQ_ENTRY(MemoryRegion) subregions_link; + QTAILQ_HEAD(coalesced_ranges, CoalescedMemoryRange) coalesced; + const char *name; +}; + +/** + * memory_region_init: Initialize a memory region + * + * The region typically acts as a container for other memory regions. Us + * memory_region_add_subregion() to add subregions. + * + * @mr: the #MemoryRegion to be initialized + * @name: used for debugging; not visible to the user or ABI + * @size: size of the region; any subregions beyond this size will be clipped + */ +void memory_region_init(MemoryRegion *mr, + const char *name, + uint64_t size); +/** + * memory_region_init_io: Initialize an I/O memory region. + * + * Accesses into the region will be cause the callbacks in @ops to be called. + * if @size is nonzero, subregions will be clipped to @size. + * + * @mr: the #MemoryRegion to be initialized. + * @ops: a structure containing read and write callbacks to be used when + * I/O is performed on the region. + * @opaque: passed to to the read and write callbacks of the @ops structure. + * @name: used for debugging; not visible to the user or ABI + * @size: size of the region. + */ +void memory_region_init_io(MemoryRegion *mr, + const MemoryRegionOps *ops, + void *opaque, + const char *name, + uint64_t size); + +/** + * memory_region_init_ram: Initialize RAM memory region. Accesses into the + * region will be modify memory directly. + * + * @mr: the #MemoryRegion to be initialized. + * @dev: a device associated with the region; may be %NULL. + * @name: the name of the region; the pair (@dev, @name) must be globally + * unique. The name is part of the save/restore ABI and so cannot be + * changed. + * @size: size of the region. + */ +void memory_region_init_ram(MemoryRegion *mr, + DeviceState *dev, /* FIXME: layering violation */ + const char *name, + uint64_t size); + +/** + * memory_region_init_ram: Initialize RAM memory region from a user-provided. + * pointer. Accesses into the region will be modify + * memory directly. + * + * @mr: the #MemoryRegion to be initialized. + * @dev: a device associated with the region; may be %NULL. + * @name: the name of the region; the pair (@dev, @name) must be globally + * unique. The name is part of the save/restore ABI and so cannot be + * changed. + * @size: size of the region. + * @ptr: memory to be mapped; must contain at least @size bytes. + */ +void memory_region_init_ram_ptr(MemoryRegion *mr, + DeviceState *dev, /* FIXME: layering violation */ + const char *name, + uint64_t size, + void *ptr); + +/** + * memory_region_init_alias: Initialize a memory region that aliases all or a + * part of another memory region. + * + * @mr: the #MemoryRegion to be initialized. + * @name: used for debugging; not visible to the user or ABI + * @orig: the region to be referenced; @mr will be equivalent to + * @orig between @offset and @offset + @size - 1. + * @offset: start of the section in @orig to be referenced. + * @size: size of the region. + */ +void memory_region_init_alias(MemoryRegion *mr, + const char *name, + MemoryRegion *orig, + target_phys_addr_t offset, + uint64_t size); +/** + * memory_region_destroy: Destroy a memory region and relaim all resources. + * + * @mr: the region to be destroyed. May not currently be a subregion + * (see memory_region_add_subregion()) or referenced in an alias + * (see memory_region_init_alias()). + */ +void memory_region_destroy(MemoryRegion *mr); + +/** + * memory_region_size: get a memory region's size. + * + * @mr: the memory region being queried. + */ +uint64_t memory_region_size(MemoryRegion *mr); + +/** + * memory_region_get_ram_ptr: Get a pointer into a RAM memory region. + * + * Returns a host pointer to a RAM memory region (created with + * memory_region_init_ram() or memory_region_init_ram_ptr()). Use with + * care. + * + * @mr: the memory region being queried. + */ +void *memory_region_get_ram_ptr(MemoryRegion *mr); + +/** + * memory_region_set_offset: Sets an offset to be added to MemoryRegionOps + * callbacks. + * + * This function is deprecated and should not be used in new code. + */ +void memory_region_set_offset(MemoryRegion *mr, target_phys_addr_t offset); + +/** + * memory_region_set_log: Turn dirty logging on or off for a region. + * + * Turns dirty logging on or off for a specified client (display, migration). + * Only meaningful for RAM regions. + * + * @mr: the memory region being updated. + * @log: whether dirty logging is to be enabled or disabled. + * @client: the user of the logging information; %DIRTY_MEMORY_MIGRATION or + * %DIRTY_MEMORY_VGA. + */ +void memory_region_set_log(MemoryRegion *mr, bool log, unsigned client); + +/** + * memory_region_get_dirty: Check whether a page is dirty for a specified + * client. + * + * Checks whether a page has been written to since the last + * call to memory_region_reset_dirty() with the same @client. Dirty logging + * must be enabled. + * + * @mr: the memory region being queried. + * @addr: the address (relative to the start of the region) being queried. + * @client: the user of the logging information; %DIRTY_MEMORY_MIGRATION or + * %DIRTY_MEMORY_VGA. + */ +bool memory_region_get_dirty(MemoryRegion *mr, target_phys_addr_t addr, + unsigned client); + +/** + * memory_region_set_dirty: Mark a page as dirty in a memory region. + * + * Marks a page as dirty, after it has been dirtied outside guest code. + * + * @mr: the memory region being queried. + * @addr: the address (relative to the start of the region) being dirtied. + */ +void memory_region_set_dirty(MemoryRegion *mr, target_phys_addr_t addr); + +/** + * memory_region_sync_dirty_bitmap: Synchronize a region's dirty bitmap with + * any external TLBs (e.g. kvm) + * + * Flushes dirty information from accelerators such as kvm and vhost-net + * and makes it available to users of the memory API. + * + * @mr: the region being flushed. + */ +void memory_region_sync_dirty_bitmap(MemoryRegion *mr); + +/** + * memory_region_reset_dirty: Mark a range of pages as clean, for a specified + * client. + * + * Marks a range of pages as no longer dirty. + * + * @mr: the region being updated. + * @addr: the start of the subrange being cleaned. + * @size: the size of the subrange being cleaned. + * @client: the user of the logging information; %DIRTY_MEMORY_MIGRATION or + * %DIRTY_MEMORY_VGA. + */ +void memory_region_reset_dirty(MemoryRegion *mr, target_phys_addr_t addr, + target_phys_addr_t size, unsigned client); + +/** + * memory_region_set_readonly: Turn a memory region read-only (or read-write) + * + * Allows a memory region to be marked as read-only (turning it into a ROM). + * only useful on RAM regions. + * + * @mr: the region being updated. + * @readonly: whether rhe region is to be ROM or RAM. + */ +void memory_region_set_readonly(MemoryRegion *mr, bool readonly); + +/** + * memory_region_set_coalescing: Enable memory coalescing for the region. + * + * Enabled writes to a region to be queued for later processing. MMIO ->write + * callbacks may be delayed until a non-coalesced MMIO is issued. + * Only useful for IO regions. Roughly similar to write-combining hardware. + * + * @mr: the memory region to be write coalesced + */ +void memory_region_set_coalescing(MemoryRegion *mr); + +/** + * memory_region_add_coalescing: Enable memory coalescing for a sub-range of + * a region. + * + * Like memory_region_set_coalescing(), but works on a sub-range of a region. + * Multiple calls can be issued coalesced disjoint ranges. + * + * @mr: the memory region to be updated. + * @offset: the start of the range within the region to be coalesced. + * @size: the size of the subrange to be coalesced. + */ +void memory_region_add_coalescing(MemoryRegion *mr, + target_phys_addr_t offset, + uint64_t size); + +/** + * memory_region_clear_coalescing: Disable MMIO coalescing for the region. + * + * Disables any coalescing caused by memory_region_set_coalescing() or + * memory_region_add_coalescing(). Roughly equivalent to uncacheble memory + * hardware. + * + * @mr: the memory region to be updated. + */ +void memory_region_clear_coalescing(MemoryRegion *mr); + +/** + * memory_region_add_subregion: Add a sub-region to a container. + * + * Adds a sub-region at @offset. The sub-region may not overlap with other + * subregions (except for those explicitly marked as overlapping). A region + * may only be added once as a subregion (unless removed with + * memory_region_del_subregion()); use memory_region_init_alias() if you + * want a region to be a subregion in multiple locations. + * + * @mr: the region to contain the new subregion; must be a container + * initialized with memory_region_init(). + * @offset: the offset relative to @mr where @subregion is added. + * @subregion: the subregion to be added. + */ +void memory_region_add_subregion(MemoryRegion *mr, + target_phys_addr_t offset, + MemoryRegion *subregion); +/** + * memory_region_add_subregion: Add a sub-region to a container, with overlap. + * + * Adds a sub-region at @offset. The sub-region may overlap with other + * subregions. Conflicts are resolved by having a higher @priority hide a + * lower @priority. Subregions without priority are taken as @priority 0. + * A region may only be added once as a subregion (unless removed with + * memory_region_del_subregion()); use memory_region_init_alias() if you + * want a region to be a subregion in multiple locations. + * + * @mr: the region to contain the new subregion; must be a container + * initialized with memory_region_init(). + * @offset: the offset relative to @mr where @subregion is added. + * @subregion: the subregion to be added. + * @priority: used for resolving overlaps; highest priority wins. + */ +void memory_region_add_subregion_overlap(MemoryRegion *mr, + target_phys_addr_t offset, + MemoryRegion *subregion, + unsigned priority); +/** + * memory_region_del_subregion: Remove a subregion. + * + * Removes a subregion from its container. + * + * @mr: the container to be updated. + * @subregion: the region being removed; must be a current subregion of @mr. + */ +void memory_region_del_subregion(MemoryRegion *mr, + MemoryRegion *subregion); + +#endif + +#endif From 5a583347697413f964d886b074860cb888c0e306 Mon Sep 17 00:00:00 2001 From: Avi Kivity Date: Tue, 26 Jul 2011 14:26:02 +0300 Subject: [PATCH 084/230] memory: implement dirty tracking Currently dirty tracking is implemented by passing through all calls to the underlying cpu_physical_memory_*() calls. Reviewed-by: Anthony Liguori Signed-off-by: Avi Kivity Signed-off-by: Anthony Liguori --- memory.c | 39 +++++++++++++++++++++++++++++++-------- memory.h | 1 + 2 files changed, 32 insertions(+), 8 deletions(-) diff --git a/memory.c b/memory.c index a9cf3176e2..d858b47739 100644 --- a/memory.c +++ b/memory.c @@ -69,6 +69,7 @@ struct FlatRange { MemoryRegion *mr; target_phys_addr_t offset_in_region; AddrRange addr; + uint8_t dirty_log_mask; }; /* Flattened global view of current active memory hierarchy. Kept in sorted @@ -177,6 +178,7 @@ static void render_memory_region(FlatView *view, fr.mr = mr; fr.offset_in_region = offset_in_region; fr.addr = addrrange_make(base, now); + fr.dirty_log_mask = mr->dirty_log_mask; flatview_insert(view, i, &fr); ++i; base += now; @@ -194,6 +196,7 @@ static void render_memory_region(FlatView *view, fr.mr = mr; fr.offset_in_region = offset_in_region; fr.addr = addrrange_make(base, remain); + fr.dirty_log_mask = mr->dirty_log_mask; flatview_insert(view, i, &fr); } } @@ -247,9 +250,14 @@ static void memory_region_update_topology(void) } else if (frold && frnew && flatrange_equal(frold, frnew)) { /* In both (logging may have changed) */ + if (frold->dirty_log_mask && !frnew->dirty_log_mask) { + cpu_physical_log_stop(frnew->addr.start, frnew->addr.size); + } else if (frnew->dirty_log_mask && !frold->dirty_log_mask) { + cpu_physical_log_start(frnew->addr.start, frnew->addr.size); + } + ++iold; ++inew; - /* FIXME: dirty logging */ } else { /* In new */ @@ -267,7 +275,7 @@ static void memory_region_update_topology(void) frnew->addr.size, phys_offset, region_offset, - 0); + frnew->dirty_log_mask); ++inew; } } @@ -292,6 +300,7 @@ void memory_region_init(MemoryRegion *mr, memset(&mr->subregions_link, 0, sizeof mr->subregions_link); QTAILQ_INIT(&mr->coalesced); mr->name = qemu_strdup(name); + mr->dirty_log_mask = 0; } static bool memory_region_access_valid(MemoryRegion *mr, @@ -496,24 +505,35 @@ void memory_region_set_offset(MemoryRegion *mr, target_phys_addr_t offset) void memory_region_set_log(MemoryRegion *mr, bool log, unsigned client) { - /* FIXME */ + uint8_t mask = 1 << client; + + mr->dirty_log_mask = (mr->dirty_log_mask & ~mask) | (log * mask); + memory_region_update_topology(); } bool memory_region_get_dirty(MemoryRegion *mr, target_phys_addr_t addr, unsigned client) { - /* FIXME */ - return true; + assert(mr->has_ram_addr); + return cpu_physical_memory_get_dirty(mr->ram_addr + addr, 1 << client); } void memory_region_set_dirty(MemoryRegion *mr, target_phys_addr_t addr) { - /* FIXME */ + assert(mr->has_ram_addr); + return cpu_physical_memory_set_dirty(mr->ram_addr + addr); } void memory_region_sync_dirty_bitmap(MemoryRegion *mr) { - /* FIXME */ + FlatRange *fr; + + FOR_EACH_FLAT_RANGE(fr, ¤t_memory_map) { + if (fr->mr == mr) { + cpu_physical_sync_dirty_bitmap(fr->addr.start, + fr->addr.start + fr->addr.size); + } + } } void memory_region_set_readonly(MemoryRegion *mr, bool readonly) @@ -524,7 +544,10 @@ void memory_region_set_readonly(MemoryRegion *mr, bool readonly) void memory_region_reset_dirty(MemoryRegion *mr, target_phys_addr_t addr, target_phys_addr_t size, unsigned client) { - /* FIXME */ + assert(mr->has_ram_addr); + cpu_physical_memory_reset_dirty(mr->ram_addr + addr, + mr->ram_addr + addr + size, + 1 << client); } void *memory_region_get_ram_ptr(MemoryRegion *mr) diff --git a/memory.h b/memory.h index a4c94bda42..d441bd8139 100644 --- a/memory.h +++ b/memory.h @@ -99,6 +99,7 @@ struct MemoryRegion { QTAILQ_ENTRY(MemoryRegion) subregions_link; QTAILQ_HEAD(coalesced_ranges, CoalescedMemoryRange) coalesced; const char *name; + uint8_t dirty_log_mask; }; /** From 3d8e6bf97781a8415fd08ba1770269e1332c200c Mon Sep 17 00:00:00 2001 From: Avi Kivity Date: Tue, 26 Jul 2011 14:26:03 +0300 Subject: [PATCH 085/230] memory: merge adjacent segments of a single memory region Simple implementations of memory routers, for example the Cirrus VGA memory banks or the 440FX PAM registers can generate adjacent memory regions which are contiguous. Detect these and merge them; this saves kvm memory slots and shortens lookup times. Signed-off-by: Avi Kivity Signed-off-by: Anthony Liguori --- memory.c | 29 +++++++++++++++++++++++++++++ 1 file changed, 29 insertions(+) diff --git a/memory.c b/memory.c index d858b47739..121f9e144e 100644 --- a/memory.c +++ b/memory.c @@ -122,6 +122,34 @@ static void flatview_destroy(FlatView *view) qemu_free(view->ranges); } +static bool can_merge(FlatRange *r1, FlatRange *r2) +{ + return addrrange_end(r1->addr) == r2->addr.start + && r1->mr == r2->mr + && r1->offset_in_region + r1->addr.size == r2->offset_in_region + && r1->dirty_log_mask == r2->dirty_log_mask; +} + +/* Attempt to simplify a view by merging ajacent ranges */ +static void flatview_simplify(FlatView *view) +{ + unsigned i, j; + + i = 0; + while (i < view->nr) { + j = i + 1; + while (j < view->nr + && can_merge(&view->ranges[j-1], &view->ranges[j])) { + view->ranges[i].addr.size += view->ranges[j].addr.size; + ++j; + } + ++i; + memmove(&view->ranges[i], &view->ranges[j], + (view->nr - j) * sizeof(view->ranges[j])); + view->nr -= j - i; + } +} + /* Render a memory region into the global view. Ranges in @view obscure * ranges in @mr. */ @@ -209,6 +237,7 @@ static FlatView generate_memory_topology(MemoryRegion *mr) flatview_init(&view); render_memory_region(&view, mr, 0, addrrange_make(0, UINT64_MAX)); + flatview_simplify(&view); return view; } From 1c0ffa58afab4d8496795d29b70d5e9e67e9341e Mon Sep 17 00:00:00 2001 From: Avi Kivity Date: Tue, 26 Jul 2011 14:26:04 +0300 Subject: [PATCH 086/230] Internal interfaces for memory API get_system_memory() provides the root of the memory hierarchy. This interface is intended to be private between memory.c and exec.c. If this file is included elsewhere, it should be regarded as a bug (or TODO item). However, it will be temporarily needed for the conversion to hierarchical memory routing. Reviewed-by: Anthony Liguori Signed-off-by: Avi Kivity Signed-off-by: Anthony Liguori --- exec-memory.h | 36 ++++++++++++++++++++++++++++++++++++ memory.c | 7 +++++++ 2 files changed, 43 insertions(+) create mode 100644 exec-memory.h diff --git a/exec-memory.h b/exec-memory.h new file mode 100644 index 0000000000..aea1b45654 --- /dev/null +++ b/exec-memory.h @@ -0,0 +1,36 @@ +/* + * Internal memory managment interfaces + * + * Copyright 2011 Red Hat, Inc. and/or its affiliates + * + * Authors: + * Avi Kivity + * + * This work is licensed under the terms of the GNU GPL, version 2. See + * the COPYING file in the top-level directory. + * + */ + +#ifndef EXEC_MEMORY_H +#define EXEC_MEMORY_H + +/* + * Internal interfaces between memory.c/exec.c/vl.c. Do not #include unless + * you're one of them. + */ + +#include "memory.h" + +#ifndef CONFIG_USER_ONLY + +/* Get the root memory region. This interface should only be used temporarily + * until a proper bus interface is available. + */ +MemoryRegion *get_system_memory(void); + +/* Set the root memory region. This region is the system memory map. */ +void set_system_memory_map(MemoryRegion *mr); + +#endif + +#endif diff --git a/memory.c b/memory.c index 121f9e144e..fcb612e367 100644 --- a/memory.c +++ b/memory.c @@ -12,6 +12,7 @@ */ #include "memory.h" +#include "exec-memory.h" #include typedef struct AddrRange AddrRange; @@ -703,3 +704,9 @@ void memory_region_del_subregion(MemoryRegion *mr, QTAILQ_REMOVE(&mr->subregions, subregion, subregions_link); memory_region_update_topology(); } + +void set_system_memory_map(MemoryRegion *mr) +{ + root_memory_region = mr; + memory_region_update_topology(); +} From cc31e6e7b3a96d99493049ec9f4fe8eaefacbaa8 Mon Sep 17 00:00:00 2001 From: Avi Kivity Date: Tue, 26 Jul 2011 14:26:05 +0300 Subject: [PATCH 087/230] memory: abstract address space operations Prepare for multiple address space support by abstracting away the details of registering a memory range with qemu's flat representation into an AddressSpace object. Note operations which are memory specific are not abstracted, since they will never be called on I/O address spaces anyway. Reviewed-by: Anthony Liguori Signed-off-by: Avi Kivity Signed-off-by: Anthony Liguori --- memory.c | 111 ++++++++++++++++++++++++++++++++++++++++--------------- 1 file changed, 81 insertions(+), 30 deletions(-) diff --git a/memory.c b/memory.c index fcb612e367..bae7765fcf 100644 --- a/memory.c +++ b/memory.c @@ -82,12 +82,26 @@ struct FlatView { unsigned nr_allocated; }; +typedef struct AddressSpace AddressSpace; +typedef struct AddressSpaceOps AddressSpaceOps; + +/* A system address space - I/O, memory, etc. */ +struct AddressSpace { + const AddressSpaceOps *ops; + MemoryRegion *root; + FlatView current_map; +}; + +struct AddressSpaceOps { + void (*range_add)(AddressSpace *as, FlatRange *fr); + void (*range_del)(AddressSpace *as, FlatRange *fr); + void (*log_start)(AddressSpace *as, FlatRange *fr); + void (*log_stop)(AddressSpace *as, FlatRange *fr); +}; + #define FOR_EACH_FLAT_RANGE(var, view) \ for (var = (view)->ranges; var < (view)->ranges + (view)->nr; ++var) -static FlatView current_memory_map; -static MemoryRegion *root_memory_region; - static bool flatrange_equal(FlatRange *a, FlatRange *b) { return a->mr == b->mr @@ -151,6 +165,54 @@ static void flatview_simplify(FlatView *view) } } +static void as_memory_range_add(AddressSpace *as, FlatRange *fr) +{ + ram_addr_t phys_offset, region_offset; + + phys_offset = fr->mr->ram_addr; + region_offset = fr->offset_in_region; + /* cpu_register_physical_memory_log() wants region_offset for + * mmio, but prefers offseting phys_offset for RAM. Humour it. + */ + if ((phys_offset & ~TARGET_PAGE_MASK) <= IO_MEM_ROM) { + phys_offset += region_offset; + region_offset = 0; + } + + cpu_register_physical_memory_log(fr->addr.start, + fr->addr.size, + phys_offset, + region_offset, + fr->dirty_log_mask); +} + +static void as_memory_range_del(AddressSpace *as, FlatRange *fr) +{ + cpu_register_physical_memory(fr->addr.start, fr->addr.size, + IO_MEM_UNASSIGNED); +} + +static void as_memory_log_start(AddressSpace *as, FlatRange *fr) +{ + cpu_physical_log_start(fr->addr.start, fr->addr.size); +} + +static void as_memory_log_stop(AddressSpace *as, FlatRange *fr) +{ + cpu_physical_log_stop(fr->addr.start, fr->addr.size); +} + +static const AddressSpaceOps address_space_ops_memory = { + .range_add = as_memory_range_add, + .range_del = as_memory_range_del, + .log_start = as_memory_log_start, + .log_stop = as_memory_log_stop, +}; + +static AddressSpace address_space_memory = { + .ops = &address_space_ops_memory, +}; + /* Render a memory region into the global view. Ranges in @view obscure * ranges in @mr. */ @@ -243,13 +305,12 @@ static FlatView generate_memory_topology(MemoryRegion *mr) return view; } -static void memory_region_update_topology(void) +static void address_space_update_topology(AddressSpace *as) { - FlatView old_view = current_memory_map; - FlatView new_view = generate_memory_topology(root_memory_region); + FlatView old_view = as->current_map; + FlatView new_view = generate_memory_topology(as->root); unsigned iold, inew; FlatRange *frold, *frnew; - ram_addr_t phys_offset, region_offset; /* Generate a symmetric difference of the old and new memory maps. * Kill ranges in the old map, and instantiate ranges in the new map. @@ -274,16 +335,15 @@ static void memory_region_update_topology(void) && !flatrange_equal(frold, frnew)))) { /* In old, but (not in new, or in new but attributes changed). */ - cpu_register_physical_memory(frold->addr.start, frold->addr.size, - IO_MEM_UNASSIGNED); + as->ops->range_del(as, frold); ++iold; } else if (frold && frnew && flatrange_equal(frold, frnew)) { /* In both (logging may have changed) */ if (frold->dirty_log_mask && !frnew->dirty_log_mask) { - cpu_physical_log_stop(frnew->addr.start, frnew->addr.size); + as->ops->log_stop(as, frnew); } else if (frnew->dirty_log_mask && !frold->dirty_log_mask) { - cpu_physical_log_start(frnew->addr.start, frnew->addr.size); + as->ops->log_start(as, frnew); } ++iold; @@ -291,28 +351,19 @@ static void memory_region_update_topology(void) } else { /* In new */ - phys_offset = frnew->mr->ram_addr; - region_offset = frnew->offset_in_region; - /* cpu_register_physical_memory_log() wants region_offset for - * mmio, but prefers offseting phys_offset for RAM. Humour it. - */ - if ((phys_offset & ~TARGET_PAGE_MASK) <= IO_MEM_ROM) { - phys_offset += region_offset; - region_offset = 0; - } - - cpu_register_physical_memory_log(frnew->addr.start, - frnew->addr.size, - phys_offset, - region_offset, - frnew->dirty_log_mask); + as->ops->range_add(as, frnew); ++inew; } } - current_memory_map = new_view; + as->current_map = new_view; flatview_destroy(&old_view); } +static void memory_region_update_topology(void) +{ + address_space_update_topology(&address_space_memory); +} + void memory_region_init(MemoryRegion *mr, const char *name, uint64_t size) @@ -558,7 +609,7 @@ void memory_region_sync_dirty_bitmap(MemoryRegion *mr) { FlatRange *fr; - FOR_EACH_FLAT_RANGE(fr, ¤t_memory_map) { + FOR_EACH_FLAT_RANGE(fr, &address_space_memory.current_map) { if (fr->mr == mr) { cpu_physical_sync_dirty_bitmap(fr->addr.start, fr->addr.start + fr->addr.size); @@ -597,7 +648,7 @@ static void memory_region_update_coalesced_range(MemoryRegion *mr) CoalescedMemoryRange *cmr; AddrRange tmp; - FOR_EACH_FLAT_RANGE(fr, ¤t_memory_map) { + FOR_EACH_FLAT_RANGE(fr, &address_space_memory.current_map) { if (fr->mr == mr) { qemu_unregister_coalesced_mmio(fr->addr.start, fr->addr.size); QTAILQ_FOREACH(cmr, &mr->coalesced, link) { @@ -707,6 +758,6 @@ void memory_region_del_subregion(MemoryRegion *mr, void set_system_memory_map(MemoryRegion *mr) { - root_memory_region = mr; + address_space_memory.root = mr; memory_region_update_topology(); } From 14a3c10ac890e1982e55bffa37aaca764b4b525b Mon Sep 17 00:00:00 2001 From: Avi Kivity Date: Tue, 26 Jul 2011 14:26:06 +0300 Subject: [PATCH 088/230] memory: rename MemoryRegion::has_ram_addr to ::terminates I/O regions will not have ram_addrs, so this is a better name. Reviewed-by: Anthony Liguori Signed-off-by: Avi Kivity Signed-off-by: Anthony Liguori --- memory.c | 18 +++++++++--------- memory.h | 2 +- 2 files changed, 10 insertions(+), 10 deletions(-) diff --git a/memory.c b/memory.c index bae7765fcf..9e1a838a89 100644 --- a/memory.c +++ b/memory.c @@ -251,7 +251,7 @@ static void render_memory_region(FlatView *view, render_memory_region(view, subregion, base, clip); } - if (!mr->has_ram_addr) { + if (!mr->terminates) { return; } @@ -373,7 +373,7 @@ void memory_region_init(MemoryRegion *mr, mr->size = size; mr->addr = 0; mr->offset = 0; - mr->has_ram_addr = false; + mr->terminates = false; mr->priority = 0; mr->may_overlap = false; mr->alias = NULL; @@ -528,7 +528,7 @@ void memory_region_init_io(MemoryRegion *mr, memory_region_init(mr, name, size); mr->ops = ops; mr->opaque = opaque; - mr->has_ram_addr = true; + mr->terminates = true; mr->ram_addr = cpu_register_io_memory(memory_region_read_thunk, memory_region_write_thunk, mr, @@ -541,7 +541,7 @@ void memory_region_init_ram(MemoryRegion *mr, uint64_t size) { memory_region_init(mr, name, size); - mr->has_ram_addr = true; + mr->terminates = true; mr->ram_addr = qemu_ram_alloc(dev, name, size); } @@ -552,7 +552,7 @@ void memory_region_init_ram_ptr(MemoryRegion *mr, void *ptr) { memory_region_init(mr, name, size); - mr->has_ram_addr = true; + mr->terminates = true; mr->ram_addr = qemu_ram_alloc_from_ptr(dev, name, size, ptr); } @@ -595,13 +595,13 @@ void memory_region_set_log(MemoryRegion *mr, bool log, unsigned client) bool memory_region_get_dirty(MemoryRegion *mr, target_phys_addr_t addr, unsigned client) { - assert(mr->has_ram_addr); + assert(mr->terminates); return cpu_physical_memory_get_dirty(mr->ram_addr + addr, 1 << client); } void memory_region_set_dirty(MemoryRegion *mr, target_phys_addr_t addr) { - assert(mr->has_ram_addr); + assert(mr->terminates); return cpu_physical_memory_set_dirty(mr->ram_addr + addr); } @@ -625,7 +625,7 @@ void memory_region_set_readonly(MemoryRegion *mr, bool readonly) void memory_region_reset_dirty(MemoryRegion *mr, target_phys_addr_t addr, target_phys_addr_t size, unsigned client) { - assert(mr->has_ram_addr); + assert(mr->terminates); cpu_physical_memory_reset_dirty(mr->ram_addr + addr, mr->ram_addr + addr + size, 1 << client); @@ -637,7 +637,7 @@ void *memory_region_get_ram_ptr(MemoryRegion *mr) return memory_region_get_ram_ptr(mr->alias) + mr->alias_offset; } - assert(mr->has_ram_addr); + assert(mr->terminates); return qemu_get_ram_ptr(mr->ram_addr); } diff --git a/memory.h b/memory.h index d441bd8139..47d6b9db2d 100644 --- a/memory.h +++ b/memory.h @@ -90,7 +90,7 @@ struct MemoryRegion { target_phys_addr_t addr; target_phys_addr_t offset; ram_addr_t ram_addr; - bool has_ram_addr; + bool terminates; MemoryRegion *alias; target_phys_addr_t alias_offset; unsigned priority; From 16ef61c9e56657d39760e5ad6b9f5361f00b2083 Mon Sep 17 00:00:00 2001 From: Avi Kivity Date: Tue, 26 Jul 2011 14:26:07 +0300 Subject: [PATCH 089/230] memory: late initialization of ram_addr For non-RAM memory regions, we cannot tell whether this is an I/O region or an MMIO region. Since the qemu backing registration is different for the two, we have to defer initialization until we know which address space we are in. These shenanigans will be removed once the backing registration is unified with the memory API. Signed-off-by: Avi Kivity Signed-off-by: Anthony Liguori --- memory.c | 24 ++++++++++++++++++++---- memory.h | 1 + 2 files changed, 21 insertions(+), 4 deletions(-) diff --git a/memory.c b/memory.c index 9e1a838a89..e839c9ebdb 100644 --- a/memory.c +++ b/memory.c @@ -165,10 +165,14 @@ static void flatview_simplify(FlatView *view) } } +static void memory_region_prepare_ram_addr(MemoryRegion *mr); + static void as_memory_range_add(AddressSpace *as, FlatRange *fr) { ram_addr_t phys_offset, region_offset; + memory_region_prepare_ram_addr(fr->mr); + phys_offset = fr->mr->ram_addr; region_offset = fr->offset_in_region; /* cpu_register_physical_memory_log() wants region_offset for @@ -519,6 +523,19 @@ static CPUWriteMemoryFunc * const memory_region_write_thunk[] = { memory_region_write_thunk_l, }; +static void memory_region_prepare_ram_addr(MemoryRegion *mr) +{ + if (mr->backend_registered) { + return; + } + + mr->ram_addr = cpu_register_io_memory(memory_region_read_thunk, + memory_region_write_thunk, + mr, + mr->ops->endianness); + mr->backend_registered = true; +} + void memory_region_init_io(MemoryRegion *mr, const MemoryRegionOps *ops, void *opaque, @@ -529,10 +546,7 @@ void memory_region_init_io(MemoryRegion *mr, mr->ops = ops; mr->opaque = opaque; mr->terminates = true; - mr->ram_addr = cpu_register_io_memory(memory_region_read_thunk, - memory_region_write_thunk, - mr, - mr->ops->endianness); + mr->backend_registered = false; } void memory_region_init_ram(MemoryRegion *mr, @@ -543,6 +557,7 @@ void memory_region_init_ram(MemoryRegion *mr, memory_region_init(mr, name, size); mr->terminates = true; mr->ram_addr = qemu_ram_alloc(dev, name, size); + mr->backend_registered = true; } void memory_region_init_ram_ptr(MemoryRegion *mr, @@ -554,6 +569,7 @@ void memory_region_init_ram_ptr(MemoryRegion *mr, memory_region_init(mr, name, size); mr->terminates = true; mr->ram_addr = qemu_ram_alloc_from_ptr(dev, name, size, ptr); + mr->backend_registered = true; } void memory_region_init_alias(MemoryRegion *mr, diff --git a/memory.h b/memory.h index 47d6b9db2d..c481038f2e 100644 --- a/memory.h +++ b/memory.h @@ -89,6 +89,7 @@ struct MemoryRegion { uint64_t size; target_phys_addr_t addr; target_phys_addr_t offset; + bool backend_registered; ram_addr_t ram_addr; bool terminates; MemoryRegion *alias; From 658b2224017b5c5fdc60969fa2f0798781b0cb3f Mon Sep 17 00:00:00 2001 From: Avi Kivity Date: Tue, 26 Jul 2011 14:26:08 +0300 Subject: [PATCH 090/230] memory: I/O address space support Allow registering I/O ports via the same mechanism as mmio ranges. Reviewed-by: Anthony Liguori Signed-off-by: Avi Kivity Signed-off-by: Anthony Liguori --- exec-memory.h | 3 +++ memory.c | 60 ++++++++++++++++++++++++++++++++++++++++++++++++++- memory.h | 2 ++ 3 files changed, 64 insertions(+), 1 deletion(-) diff --git a/exec-memory.h b/exec-memory.h index aea1b45654..c439aba3d1 100644 --- a/exec-memory.h +++ b/exec-memory.h @@ -31,6 +31,9 @@ MemoryRegion *get_system_memory(void); /* Set the root memory region. This region is the system memory map. */ void set_system_memory_map(MemoryRegion *mr); +/* Set the I/O memory region. This region is the I/O memory map. */ +void set_system_io_map(MemoryRegion *mr); + #endif #endif diff --git a/memory.c b/memory.c index e839c9ebdb..df0ed0e48a 100644 --- a/memory.c +++ b/memory.c @@ -13,6 +13,7 @@ #include "memory.h" #include "exec-memory.h" +#include "ioport.h" #include typedef struct AddrRange AddrRange; @@ -217,6 +218,52 @@ static AddressSpace address_space_memory = { .ops = &address_space_ops_memory, }; +static void memory_region_iorange_read(IORange *iorange, + uint64_t offset, + unsigned width, + uint64_t *data) +{ + MemoryRegion *mr = container_of(iorange, MemoryRegion, iorange); + + *data = mr->ops->read(mr->opaque, offset, width); +} + +static void memory_region_iorange_write(IORange *iorange, + uint64_t offset, + unsigned width, + uint64_t data) +{ + MemoryRegion *mr = container_of(iorange, MemoryRegion, iorange); + + mr->ops->write(mr->opaque, offset, data, width); +} + +static const IORangeOps memory_region_iorange_ops = { + .read = memory_region_iorange_read, + .write = memory_region_iorange_write, +}; + +static void as_io_range_add(AddressSpace *as, FlatRange *fr) +{ + iorange_init(&fr->mr->iorange, &memory_region_iorange_ops, + fr->addr.start,fr->addr.size); + ioport_register(&fr->mr->iorange); +} + +static void as_io_range_del(AddressSpace *as, FlatRange *fr) +{ + isa_unassign_ioport(fr->addr.start, fr->addr.size); +} + +static const AddressSpaceOps address_space_ops_io = { + .range_add = as_io_range_add, + .range_del = as_io_range_del, +}; + +static AddressSpace address_space_io = { + .ops = &address_space_ops_io, +}; + /* Render a memory region into the global view. Ranges in @view obscure * ranges in @mr. */ @@ -365,7 +412,12 @@ static void address_space_update_topology(AddressSpace *as) static void memory_region_update_topology(void) { - address_space_update_topology(&address_space_memory); + if (address_space_memory.root) { + address_space_update_topology(&address_space_memory); + } + if (address_space_io.root) { + address_space_update_topology(&address_space_io); + } } void memory_region_init(MemoryRegion *mr, @@ -777,3 +829,9 @@ void set_system_memory_map(MemoryRegion *mr) address_space_memory.root = mr; memory_region_update_topology(); } + +void set_system_io_map(MemoryRegion *mr) +{ + address_space_io.root = mr; + memory_region_update_topology(); +} diff --git a/memory.h b/memory.h index c481038f2e..88ba428b7c 100644 --- a/memory.h +++ b/memory.h @@ -22,6 +22,7 @@ #include "cpu-common.h" #include "targphys.h" #include "qemu-queue.h" +#include "iorange.h" typedef struct MemoryRegionOps MemoryRegionOps; typedef struct MemoryRegion MemoryRegion; @@ -91,6 +92,7 @@ struct MemoryRegion { target_phys_addr_t offset; bool backend_registered; ram_addr_t ram_addr; + IORange iorange; bool terminates; MemoryRegion *alias; target_phys_addr_t alias_offset; From 627a0e90dc6b53504d6b9539b8e29210d82ecf9d Mon Sep 17 00:00:00 2001 From: Avi Kivity Date: Tue, 26 Jul 2011 14:26:09 +0300 Subject: [PATCH 091/230] memory: add backward compatibility for old portio registration Reviewed-by: Anthony Liguori Signed-off-by: Avi Kivity Signed-off-by: Anthony Liguori --- memory.c | 32 ++++++++++++++++++++++++++++++++ memory.h | 17 +++++++++++++++++ 2 files changed, 49 insertions(+) diff --git a/memory.c b/memory.c index df0ed0e48a..5f2c9efa2a 100644 --- a/memory.c +++ b/memory.c @@ -218,6 +218,21 @@ static AddressSpace address_space_memory = { .ops = &address_space_ops_memory, }; +static const MemoryRegionPortio *find_portio(MemoryRegion *mr, uint64_t offset, + unsigned width, bool write) +{ + const MemoryRegionPortio *mrp; + + for (mrp = mr->ops->old_portio; mrp->size; ++mrp) { + if (offset >= mrp->offset && offset < mrp->offset + mrp->len + && width == mrp->size + && (write ? (bool)mrp->write : (bool)mrp->read)) { + return mrp; + } + } + return NULL; +} + static void memory_region_iorange_read(IORange *iorange, uint64_t offset, unsigned width, @@ -225,6 +240,15 @@ static void memory_region_iorange_read(IORange *iorange, { MemoryRegion *mr = container_of(iorange, MemoryRegion, iorange); + if (mr->ops->old_portio) { + const MemoryRegionPortio *mrp = find_portio(mr, offset, width, false); + + *data = ((uint64_t)1 << (width * 8)) - 1; + if (mrp) { + *data = mrp->read(mr->opaque, offset - mrp->offset); + } + return; + } *data = mr->ops->read(mr->opaque, offset, width); } @@ -235,6 +259,14 @@ static void memory_region_iorange_write(IORange *iorange, { MemoryRegion *mr = container_of(iorange, MemoryRegion, iorange); + if (mr->ops->old_portio) { + const MemoryRegionPortio *mrp = find_portio(mr, offset, width, true); + + if (mrp) { + mrp->write(mr->opaque, offset - mrp->offset, data); + } + return; + } mr->ops->write(mr->opaque, offset, data, width); } diff --git a/memory.h b/memory.h index 88ba428b7c..40ab95a98c 100644 --- a/memory.h +++ b/memory.h @@ -23,9 +23,11 @@ #include "targphys.h" #include "qemu-queue.h" #include "iorange.h" +#include "ioport.h" typedef struct MemoryRegionOps MemoryRegionOps; typedef struct MemoryRegion MemoryRegion; +typedef struct MemoryRegionPortio MemoryRegionPortio; /* Must match *_DIRTY_FLAGS in cpu-all.h. To be replaced with dynamic * registration. @@ -78,6 +80,11 @@ struct MemoryRegionOps { */ bool unaligned; } impl; + + /* If .read and .write are not present, old_portio may be used for + * backwards compatibility with old portio registration + */ + const MemoryRegionPortio *old_portio; }; typedef struct CoalescedMemoryRange CoalescedMemoryRange; @@ -105,6 +112,16 @@ struct MemoryRegion { uint8_t dirty_log_mask; }; +struct MemoryRegionPortio { + uint32_t offset; + uint32_t len; + unsigned size; + IOPortReadFunc *read; + IOPortWriteFunc *write; +}; + +#define PORTIO_END { } + /** * memory_region_init: Initialize a memory region * From 74901c3bd06a02b54f23172cb870127b49390bd0 Mon Sep 17 00:00:00 2001 From: Avi Kivity Date: Tue, 26 Jul 2011 14:26:10 +0300 Subject: [PATCH 092/230] memory: add backward compatibility for old mmio registration This eases the transition to the new API. Reviewed-by: Anthony Liguori Signed-off-by: Avi Kivity Signed-off-by: Anthony Liguori --- memory.c | 10 ++++++++++ memory.h | 10 ++++++++++ 2 files changed, 20 insertions(+) diff --git a/memory.c b/memory.c index 5f2c9efa2a..7dd7cacf04 100644 --- a/memory.c +++ b/memory.c @@ -14,6 +14,7 @@ #include "memory.h" #include "exec-memory.h" #include "ioport.h" +#include "bitops.h" #include typedef struct AddrRange AddrRange; @@ -506,6 +507,10 @@ static uint32_t memory_region_read_thunk_n(void *_mr, return -1U; /* FIXME: better signalling */ } + if (!mr->ops->read) { + return mr->ops->old_mmio.read[bitops_ffsl(size)](mr->opaque, addr); + } + /* FIXME: support unaligned access */ access_size_min = mr->ops->impl.min_access_size; @@ -542,6 +547,11 @@ static void memory_region_write_thunk_n(void *_mr, return; /* FIXME: better signalling */ } + if (!mr->ops->write) { + mr->ops->old_mmio.write[bitops_ffsl(size)](mr->opaque, addr, data); + return; + } + /* FIXME: support unaligned access */ access_size_min = mr->ops->impl.min_access_size; diff --git a/memory.h b/memory.h index 40ab95a98c..003c999219 100644 --- a/memory.h +++ b/memory.h @@ -28,6 +28,7 @@ typedef struct MemoryRegionOps MemoryRegionOps; typedef struct MemoryRegion MemoryRegion; typedef struct MemoryRegionPortio MemoryRegionPortio; +typedef struct MemoryRegionMmio MemoryRegionMmio; /* Must match *_DIRTY_FLAGS in cpu-all.h. To be replaced with dynamic * registration. @@ -36,6 +37,11 @@ typedef struct MemoryRegionPortio MemoryRegionPortio; #define DIRTY_MEMORY_CODE 1 #define DIRTY_MEMORY_MIGRATION 3 +struct MemoryRegionMmio { + CPUReadMemoryFunc *read[3]; + CPUWriteMemoryFunc *write[3]; +}; + /* * Memory region callbacks */ @@ -85,6 +91,10 @@ struct MemoryRegionOps { * backwards compatibility with old portio registration */ const MemoryRegionPortio *old_portio; + /* If .read and .write are not present, old_mmio may be used for + * backwards compatibility with old mmio registration + */ + const MemoryRegionMmio old_mmio; }; typedef struct CoalescedMemoryRange CoalescedMemoryRange; From 3e9d69e737025e987be3ce804f667ffeb07e4c53 Mon Sep 17 00:00:00 2001 From: Avi Kivity Date: Tue, 26 Jul 2011 14:26:11 +0300 Subject: [PATCH 093/230] memory: add ioeventfd support As with the rest of the memory API, the caller associates an eventfd with an address, and the memory API takes care of registering or unregistering when the address is made visible or invisible to the guest. Signed-off-by: Avi Kivity Signed-off-by: Anthony Liguori --- memory.c | 224 +++++++++++++++++++++++++++++++++++++++++++++++++++++++ memory.h | 45 +++++++++++ 2 files changed, 269 insertions(+) diff --git a/memory.c b/memory.c index 7dd7cacf04..686bbf2e6f 100644 --- a/memory.c +++ b/memory.c @@ -15,6 +15,7 @@ #include "exec-memory.h" #include "ioport.h" #include "bitops.h" +#include "kvm.h" #include typedef struct AddrRange AddrRange; @@ -64,6 +65,50 @@ struct CoalescedMemoryRange { QTAILQ_ENTRY(CoalescedMemoryRange) link; }; +struct MemoryRegionIoeventfd { + AddrRange addr; + bool match_data; + uint64_t data; + int fd; +}; + +static bool memory_region_ioeventfd_before(MemoryRegionIoeventfd a, + MemoryRegionIoeventfd b) +{ + if (a.addr.start < b.addr.start) { + return true; + } else if (a.addr.start > b.addr.start) { + return false; + } else if (a.addr.size < b.addr.size) { + return true; + } else if (a.addr.size > b.addr.size) { + return false; + } else if (a.match_data < b.match_data) { + return true; + } else if (a.match_data > b.match_data) { + return false; + } else if (a.match_data) { + if (a.data < b.data) { + return true; + } else if (a.data > b.data) { + return false; + } + } + if (a.fd < b.fd) { + return true; + } else if (a.fd > b.fd) { + return false; + } + return false; +} + +static bool memory_region_ioeventfd_equal(MemoryRegionIoeventfd a, + MemoryRegionIoeventfd b) +{ + return !memory_region_ioeventfd_before(a, b) + && !memory_region_ioeventfd_before(b, a); +} + typedef struct FlatRange FlatRange; typedef struct FlatView FlatView; @@ -92,6 +137,8 @@ struct AddressSpace { const AddressSpaceOps *ops; MemoryRegion *root; FlatView current_map; + int ioeventfd_nb; + MemoryRegionIoeventfd *ioeventfds; }; struct AddressSpaceOps { @@ -99,6 +146,8 @@ struct AddressSpaceOps { void (*range_del)(AddressSpace *as, FlatRange *fr); void (*log_start)(AddressSpace *as, FlatRange *fr); void (*log_stop)(AddressSpace *as, FlatRange *fr); + void (*ioeventfd_add)(AddressSpace *as, MemoryRegionIoeventfd *fd); + void (*ioeventfd_del)(AddressSpace *as, MemoryRegionIoeventfd *fd); }; #define FOR_EACH_FLAT_RANGE(var, view) \ @@ -208,11 +257,35 @@ static void as_memory_log_stop(AddressSpace *as, FlatRange *fr) cpu_physical_log_stop(fr->addr.start, fr->addr.size); } +static void as_memory_ioeventfd_add(AddressSpace *as, MemoryRegionIoeventfd *fd) +{ + int r; + + assert(fd->match_data && fd->addr.size == 4); + + r = kvm_set_ioeventfd_mmio_long(fd->fd, fd->addr.start, fd->data, true); + if (r < 0) { + abort(); + } +} + +static void as_memory_ioeventfd_del(AddressSpace *as, MemoryRegionIoeventfd *fd) +{ + int r; + + r = kvm_set_ioeventfd_mmio_long(fd->fd, fd->addr.start, fd->data, false); + if (r < 0) { + abort(); + } +} + static const AddressSpaceOps address_space_ops_memory = { .range_add = as_memory_range_add, .range_del = as_memory_range_del, .log_start = as_memory_log_start, .log_stop = as_memory_log_stop, + .ioeventfd_add = as_memory_ioeventfd_add, + .ioeventfd_del = as_memory_ioeventfd_del, }; static AddressSpace address_space_memory = { @@ -288,9 +361,33 @@ static void as_io_range_del(AddressSpace *as, FlatRange *fr) isa_unassign_ioport(fr->addr.start, fr->addr.size); } +static void as_io_ioeventfd_add(AddressSpace *as, MemoryRegionIoeventfd *fd) +{ + int r; + + assert(fd->match_data && fd->addr.size == 2); + + r = kvm_set_ioeventfd_pio_word(fd->fd, fd->addr.start, fd->data, true); + if (r < 0) { + abort(); + } +} + +static void as_io_ioeventfd_del(AddressSpace *as, MemoryRegionIoeventfd *fd) +{ + int r; + + r = kvm_set_ioeventfd_pio_word(fd->fd, fd->addr.start, fd->data, false); + if (r < 0) { + abort(); + } +} + static const AddressSpaceOps address_space_ops_io = { .range_add = as_io_range_add, .range_del = as_io_range_del, + .ioeventfd_add = as_io_ioeventfd_add, + .ioeventfd_del = as_io_ioeventfd_del, }; static AddressSpace address_space_io = { @@ -389,6 +486,69 @@ static FlatView generate_memory_topology(MemoryRegion *mr) return view; } +static void address_space_add_del_ioeventfds(AddressSpace *as, + MemoryRegionIoeventfd *fds_new, + unsigned fds_new_nb, + MemoryRegionIoeventfd *fds_old, + unsigned fds_old_nb) +{ + unsigned iold, inew; + + /* Generate a symmetric difference of the old and new fd sets, adding + * and deleting as necessary. + */ + + iold = inew = 0; + while (iold < fds_old_nb || inew < fds_new_nb) { + if (iold < fds_old_nb + && (inew == fds_new_nb + || memory_region_ioeventfd_before(fds_old[iold], + fds_new[inew]))) { + as->ops->ioeventfd_del(as, &fds_old[iold]); + ++iold; + } else if (inew < fds_new_nb + && (iold == fds_old_nb + || memory_region_ioeventfd_before(fds_new[inew], + fds_old[iold]))) { + as->ops->ioeventfd_add(as, &fds_new[inew]); + ++inew; + } else { + ++iold; + ++inew; + } + } +} + +static void address_space_update_ioeventfds(AddressSpace *as) +{ + FlatRange *fr; + unsigned ioeventfd_nb = 0; + MemoryRegionIoeventfd *ioeventfds = NULL; + AddrRange tmp; + unsigned i; + + FOR_EACH_FLAT_RANGE(fr, &as->current_map) { + for (i = 0; i < fr->mr->ioeventfd_nb; ++i) { + tmp = addrrange_shift(fr->mr->ioeventfds[i].addr, + fr->addr.start - fr->offset_in_region); + if (addrrange_intersects(fr->addr, tmp)) { + ++ioeventfd_nb; + ioeventfds = qemu_realloc(ioeventfds, + ioeventfd_nb * sizeof(*ioeventfds)); + ioeventfds[ioeventfd_nb-1] = fr->mr->ioeventfds[i]; + ioeventfds[ioeventfd_nb-1].addr = tmp; + } + } + } + + address_space_add_del_ioeventfds(as, ioeventfds, ioeventfd_nb, + as->ioeventfds, as->ioeventfd_nb); + + qemu_free(as->ioeventfds); + as->ioeventfds = ioeventfds; + as->ioeventfd_nb = ioeventfd_nb; +} + static void address_space_update_topology(AddressSpace *as) { FlatView old_view = as->current_map; @@ -441,6 +601,7 @@ static void address_space_update_topology(AddressSpace *as) } as->current_map = new_view; flatview_destroy(&old_view); + address_space_update_ioeventfds(as); } static void memory_region_update_topology(void) @@ -471,6 +632,8 @@ void memory_region_init(MemoryRegion *mr, QTAILQ_INIT(&mr->coalesced); mr->name = qemu_strdup(name); mr->dirty_log_mask = 0; + mr->ioeventfd_nb = 0; + mr->ioeventfds = NULL; } static bool memory_region_access_valid(MemoryRegion *mr, @@ -682,6 +845,7 @@ void memory_region_destroy(MemoryRegion *mr) assert(QTAILQ_EMPTY(&mr->subregions)); memory_region_clear_coalescing(mr); qemu_free((char *)mr->name); + qemu_free(mr->ioeventfds); } uint64_t memory_region_size(MemoryRegion *mr) @@ -803,6 +967,66 @@ void memory_region_clear_coalescing(MemoryRegion *mr) memory_region_update_coalesced_range(mr); } +void memory_region_add_eventfd(MemoryRegion *mr, + target_phys_addr_t addr, + unsigned size, + bool match_data, + uint64_t data, + int fd) +{ + MemoryRegionIoeventfd mrfd = { + .addr.start = addr, + .addr.size = size, + .match_data = match_data, + .data = data, + .fd = fd, + }; + unsigned i; + + for (i = 0; i < mr->ioeventfd_nb; ++i) { + if (memory_region_ioeventfd_before(mrfd, mr->ioeventfds[i])) { + break; + } + } + ++mr->ioeventfd_nb; + mr->ioeventfds = qemu_realloc(mr->ioeventfds, + sizeof(*mr->ioeventfds) * mr->ioeventfd_nb); + memmove(&mr->ioeventfds[i+1], &mr->ioeventfds[i], + sizeof(*mr->ioeventfds) * (mr->ioeventfd_nb-1 - i)); + mr->ioeventfds[i] = mrfd; + memory_region_update_topology(); +} + +void memory_region_del_eventfd(MemoryRegion *mr, + target_phys_addr_t addr, + unsigned size, + bool match_data, + uint64_t data, + int fd) +{ + MemoryRegionIoeventfd mrfd = { + .addr.start = addr, + .addr.size = size, + .match_data = match_data, + .data = data, + .fd = fd, + }; + unsigned i; + + for (i = 0; i < mr->ioeventfd_nb; ++i) { + if (memory_region_ioeventfd_equal(mrfd, mr->ioeventfds[i])) { + break; + } + } + assert(i != mr->ioeventfd_nb); + memmove(&mr->ioeventfds[i], &mr->ioeventfds[i+1], + sizeof(*mr->ioeventfds) * (mr->ioeventfd_nb - (i+1))); + --mr->ioeventfd_nb; + mr->ioeventfds = qemu_realloc(mr->ioeventfds, + sizeof(*mr->ioeventfds)*mr->ioeventfd_nb + 1); + memory_region_update_topology(); +} + static void memory_region_add_subregion_common(MemoryRegion *mr, target_phys_addr_t offset, MemoryRegion *subregion) diff --git a/memory.h b/memory.h index 003c999219..c280a39d2c 100644 --- a/memory.h +++ b/memory.h @@ -98,6 +98,7 @@ struct MemoryRegionOps { }; typedef struct CoalescedMemoryRange CoalescedMemoryRange; +typedef struct MemoryRegionIoeventfd MemoryRegionIoeventfd; struct MemoryRegion { /* All fields are private - violators will be prosecuted */ @@ -120,6 +121,8 @@ struct MemoryRegion { QTAILQ_HEAD(coalesced_ranges, CoalescedMemoryRange) coalesced; const char *name; uint8_t dirty_log_mask; + unsigned ioeventfd_nb; + MemoryRegionIoeventfd *ioeventfds; }; struct MemoryRegionPortio { @@ -363,6 +366,48 @@ void memory_region_add_coalescing(MemoryRegion *mr, */ void memory_region_clear_coalescing(MemoryRegion *mr); +/** + * memory_region_add_eventfd: Request an eventfd to be triggered when a word + * is written to a location. + * + * Marks a word in an IO region (initialized with memory_region_init_io()) + * as a trigger for an eventfd event. The I/O callback will not be called. + * The caller must be prepared to handle failure (hat is, take the required + * action if the callback _is_ called). + * + * @mr: the memory region being updated. + * @addr: the address within @mr that is to be monitored + * @size: the size of the access to trigger the eventfd + * @match_data: whether to match against @data, instead of just @addr + * @data: the data to match against the guest write + * @fd: the eventfd to be triggered when @addr, @size, and @data all match. + **/ +void memory_region_add_eventfd(MemoryRegion *mr, + target_phys_addr_t addr, + unsigned size, + bool match_data, + uint64_t data, + int fd); + +/** + * memory_region_del_eventfd: Cancel and eventfd. + * + * Cancels an eventfd trigger request by a previous memory_region_add_eventfd() + * call. + * + * @mr: the memory region being updated. + * @addr: the address within @mr that is to be monitored + * @size: the size of the access to trigger the eventfd + * @match_data: whether to match against @data, instead of just @addr + * @data: the data to match against the guest write + * @fd: the eventfd to be triggered when @addr, @size, and @data all match. + */ +void memory_region_del_eventfd(MemoryRegion *mr, + target_phys_addr_t addr, + unsigned size, + bool match_data, + uint64_t data, + int fd); /** * memory_region_add_subregion: Add a sub-region to a container. * From b8af1afbfbc157e058f27ab5382527350b814ee7 Mon Sep 17 00:00:00 2001 From: Avi Kivity Date: Tue, 26 Jul 2011 14:26:12 +0300 Subject: [PATCH 094/230] memory: separate building the final memory map into two steps Instead of adding and deleting regions in one pass, do a delete pass followed by an add pass. This fixes the following case: from: 0x0000-0x0fff ram (a1) 0x1000-0x1fff mmio (a2) 0x2000-0x2fff ram (a3) to: 0x0000-0x2fff ram (b1) The single pass algorithm removed a1, added b2, then removed a2 and a3, which caused the wrong memory map to be built. The two pass algorithm removes a1, a2, and a3, then adds b1. Signed-off-by: Avi Kivity Signed-off-by: Anthony Liguori --- memory.c | 38 +++++++++++++++++++++++++++++--------- 1 file changed, 29 insertions(+), 9 deletions(-) diff --git a/memory.c b/memory.c index 686bbf2e6f..7a5670e2a9 100644 --- a/memory.c +++ b/memory.c @@ -549,10 +549,11 @@ static void address_space_update_ioeventfds(AddressSpace *as) as->ioeventfd_nb = ioeventfd_nb; } -static void address_space_update_topology(AddressSpace *as) +static void address_space_update_topology_pass(AddressSpace *as, + FlatView old_view, + FlatView new_view, + bool adding) { - FlatView old_view = as->current_map; - FlatView new_view = generate_memory_topology(as->root); unsigned iold, inew; FlatRange *frold, *frnew; @@ -579,15 +580,20 @@ static void address_space_update_topology(AddressSpace *as) && !flatrange_equal(frold, frnew)))) { /* In old, but (not in new, or in new but attributes changed). */ - as->ops->range_del(as, frold); + if (!adding) { + as->ops->range_del(as, frold); + } + ++iold; } else if (frold && frnew && flatrange_equal(frold, frnew)) { /* In both (logging may have changed) */ - if (frold->dirty_log_mask && !frnew->dirty_log_mask) { - as->ops->log_stop(as, frnew); - } else if (frnew->dirty_log_mask && !frold->dirty_log_mask) { - as->ops->log_start(as, frnew); + if (adding) { + if (frold->dirty_log_mask && !frnew->dirty_log_mask) { + as->ops->log_stop(as, frnew); + } else if (frnew->dirty_log_mask && !frold->dirty_log_mask) { + as->ops->log_start(as, frnew); + } } ++iold; @@ -595,10 +601,24 @@ static void address_space_update_topology(AddressSpace *as) } else { /* In new */ - as->ops->range_add(as, frnew); + if (adding) { + as->ops->range_add(as, frnew); + } + ++inew; } } +} + + +static void address_space_update_topology(AddressSpace *as) +{ + FlatView old_view = as->current_map; + FlatView new_view = generate_memory_topology(as->root); + + address_space_update_topology_pass(as, old_view, new_view, false); + address_space_update_topology_pass(as, old_view, new_view, true); + as->current_map = new_view; flatview_destroy(&old_view); address_space_update_ioeventfds(as); From 4ef4db860362ce9852c20b343e9813897ecdefce Mon Sep 17 00:00:00 2001 From: Avi Kivity Date: Tue, 26 Jul 2011 14:26:13 +0300 Subject: [PATCH 095/230] memory: transaction API Allow changes to the memory hierarchy to be accumulated and made visible all at once. This reduces computational effort, especially when an accelerator (e.g. kvm) is involved. Useful when a single register update causes multiple changes to an address space. Signed-off-by: Avi Kivity Signed-off-by: Anthony Liguori --- memory.c | 18 ++++++++++++++++++ memory.h | 8 ++++++++ 2 files changed, 26 insertions(+) diff --git a/memory.c b/memory.c index 7a5670e2a9..5c6e63df3f 100644 --- a/memory.c +++ b/memory.c @@ -18,6 +18,8 @@ #include "kvm.h" #include +unsigned memory_region_transaction_depth = 0; + typedef struct AddrRange AddrRange; struct AddrRange { @@ -626,6 +628,10 @@ static void address_space_update_topology(AddressSpace *as) static void memory_region_update_topology(void) { + if (memory_region_transaction_depth) { + return; + } + if (address_space_memory.root) { address_space_update_topology(&address_space_memory); } @@ -634,6 +640,18 @@ static void memory_region_update_topology(void) } } +void memory_region_transaction_begin(void) +{ + ++memory_region_transaction_depth; +} + +void memory_region_transaction_commit(void) +{ + assert(memory_region_transaction_depth); + --memory_region_transaction_depth; + memory_region_update_topology(); +} + void memory_region_init(MemoryRegion *mr, const char *name, uint64_t size) diff --git a/memory.h b/memory.h index c280a39d2c..4e518b2a1b 100644 --- a/memory.h +++ b/memory.h @@ -456,6 +456,14 @@ void memory_region_add_subregion_overlap(MemoryRegion *mr, void memory_region_del_subregion(MemoryRegion *mr, MemoryRegion *subregion); +/* Start a transaction; changes will be accumulated and made visible only + * when the transaction ends. + */ +void memory_region_transaction_begin(void); +/* Commit a transaction and make changes visible to the guest. + */ +void memory_region_transaction_commit(void); + #endif #endif From 62152b8a014609ac94da2de440aedb3246c1204a Mon Sep 17 00:00:00 2001 From: Avi Kivity Date: Tue, 26 Jul 2011 14:26:14 +0300 Subject: [PATCH 096/230] exec.c: initialize memory map Allocate the root memory region and initialize it. Signed-off-by: Avi Kivity Signed-off-by: Anthony Liguori --- exec.c | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) diff --git a/exec.c b/exec.c index 2160ded401..d51502f2cb 100644 --- a/exec.c +++ b/exec.c @@ -33,6 +33,8 @@ #include "kvm.h" #include "hw/xen.h" #include "qemu-timer.h" +#include "memory.h" +#include "exec-memory.h" #if defined(CONFIG_USER_ONLY) #include #if defined(__FreeBSD__) || defined(__FreeBSD_kernel__) @@ -109,6 +111,9 @@ int phys_ram_fd; static int in_migration; RAMList ram_list = { .blocks = QLIST_HEAD_INITIALIZER(ram_list) }; + +static MemoryRegion *system_memory; + #endif CPUState *first_cpu; @@ -197,6 +202,7 @@ typedef struct PhysPageDesc { static void *l1_phys_map[P_L1_SIZE]; static void io_mem_init(void); +static void memory_map_init(void); /* io memory support */ CPUWriteMemoryFunc *io_mem_write[IO_MEM_NB_ENTRIES][4]; @@ -571,6 +577,7 @@ void cpu_exec_init_all(unsigned long tb_size) code_gen_ptr = code_gen_buffer; page_init(); #if !defined(CONFIG_USER_ONLY) + memory_map_init(); io_mem_init(); #endif #if !defined(CONFIG_USER_ONLY) || !defined(CONFIG_USE_GUEST_BASE) @@ -3807,6 +3814,18 @@ static void io_mem_init(void) DEVICE_NATIVE_ENDIAN); } +static void memory_map_init(void) +{ + system_memory = qemu_malloc(sizeof(*system_memory)); + memory_region_init(system_memory, "system", UINT64_MAX); + set_system_memory_map(system_memory); +} + +MemoryRegion *get_system_memory(void) +{ + return system_memory; +} + #endif /* !defined(CONFIG_USER_ONLY) */ /* physical memory access (slow version, mainly for debug) */ From bf3fb0e12aeb9fea08383c145f874ed7acc29e31 Mon Sep 17 00:00:00 2001 From: Avi Kivity Date: Tue, 26 Jul 2011 14:26:15 +0300 Subject: [PATCH 097/230] ioport: register ranges by byte aligned addresses always The I/O port space is byte addressable, even for word and long accesses. An example is the VMware svga card, which has long ports on offsets 0, 1, and 2. Reviewed-by: Anthony Liguori Signed-off-by: Avi Kivity Signed-off-by: Anthony Liguori --- ioport.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/ioport.c b/ioport.c index 0d2611d142..a32483ba84 100644 --- a/ioport.c +++ b/ioport.c @@ -146,7 +146,7 @@ int register_ioport_read(pio_addr_t start, int length, int size, hw_error("register_ioport_read: invalid size"); return -1; } - for(i = start; i < start + length; i += size) { + for(i = start; i < start + length; ++i) { ioport_read_table[bsize][i] = func; if (ioport_opaque[i] != NULL && ioport_opaque[i] != opaque) hw_error("register_ioport_read: invalid opaque for address 0x%x", @@ -166,7 +166,7 @@ int register_ioport_write(pio_addr_t start, int length, int size, hw_error("register_ioport_write: invalid size"); return -1; } - for(i = start; i < start + length; i += size) { + for(i = start; i < start + length; ++i) { ioport_write_table[bsize][i] = func; if (ioport_opaque[i] != NULL && ioport_opaque[i] != opaque) hw_error("register_ioport_write: invalid opaque for address 0x%x", From 4aa63af14969a50f9c3d2324127daff0f0199c61 Mon Sep 17 00:00:00 2001 From: Avi Kivity Date: Tue, 26 Jul 2011 14:26:16 +0300 Subject: [PATCH 098/230] pc: grab system_memory While eventually this should come from the machine initialization function, take a short cut to avoid converting all machines now. Reviewed-by: Anthony Liguori Signed-off-by: Avi Kivity Signed-off-by: Anthony Liguori --- hw/pc.c | 3 ++- hw/pc.h | 4 +++- hw/pc_piix.c | 8 +++++++- 3 files changed, 12 insertions(+), 3 deletions(-) diff --git a/hw/pc.c b/hw/pc.c index a3e8539dc6..369566a5c0 100644 --- a/hw/pc.c +++ b/hw/pc.c @@ -957,7 +957,8 @@ void pc_cpus_init(const char *cpu_model) } } -void pc_memory_init(const char *kernel_filename, +void pc_memory_init(MemoryRegion *system_memory, + const char *kernel_filename, const char *kernel_cmdline, const char *initrd_filename, ram_addr_t below_4g_mem_size, diff --git a/hw/pc.h b/hw/pc.h index 6d5730b26b..fa575831d0 100644 --- a/hw/pc.h +++ b/hw/pc.h @@ -6,6 +6,7 @@ #include "isa.h" #include "fdc.h" #include "net.h" +#include "memory.h" /* PC-style peripherals (also used by other machines). */ @@ -129,7 +130,8 @@ void pc_cmos_set_s3_resume(void *opaque, int irq, int level); void pc_acpi_smi_interrupt(void *opaque, int irq, int level); void pc_cpus_init(const char *cpu_model); -void pc_memory_init(const char *kernel_filename, +void pc_memory_init(MemoryRegion *system_memory, + const char *kernel_filename, const char *kernel_cmdline, const char *initrd_filename, ram_addr_t below_4g_mem_size, diff --git a/hw/pc_piix.c b/hw/pc_piix.c index c5c16b4571..d83854c4aa 100644 --- a/hw/pc_piix.c +++ b/hw/pc_piix.c @@ -39,6 +39,8 @@ #include "blockdev.h" #include "smbus.h" #include "xen.h" +#include "memory.h" +#include "exec-memory.h" #ifdef CONFIG_XEN # include #endif @@ -89,6 +91,9 @@ static void pc_init1(ram_addr_t ram_size, DriveInfo *hd[MAX_IDE_BUS * MAX_IDE_DEVS]; BusState *idebus[MAX_IDE_BUS]; ISADevice *rtc_state; + MemoryRegion *system_memory; + + system_memory = get_system_memory(); pc_cpus_init(cpu_model); @@ -106,7 +111,8 @@ static void pc_init1(ram_addr_t ram_size, /* allocate ram and load rom/bios */ if (!xen_enabled()) { - pc_memory_init(kernel_filename, kernel_cmdline, initrd_filename, + pc_memory_init(system_memory, + kernel_filename, kernel_cmdline, initrd_filename, below_4g_mem_size, above_4g_mem_size); } From 00cb2a99f5e7f73c4fff54ae16c7b6acf463ab5c Mon Sep 17 00:00:00 2001 From: Avi Kivity Date: Tue, 26 Jul 2011 14:26:17 +0300 Subject: [PATCH 099/230] pc: convert pc_memory_init() to memory API Reviewed-by: Anthony Liguori Signed-off-by: Avi Kivity Signed-off-by: Anthony Liguori --- hw/pc.c | 57 +++++++++++++++++++++++++++++++++++++++------------------ hw/pc.h | 1 + 2 files changed, 40 insertions(+), 18 deletions(-) diff --git a/hw/pc.c b/hw/pc.c index 369566a5c0..1c9d89a4b8 100644 --- a/hw/pc.c +++ b/hw/pc.c @@ -41,6 +41,7 @@ #include "sysemu.h" #include "blockdev.h" #include "ui/qemu-spice.h" +#include "memory.h" /* output Bochs bios info messages */ //#define DEBUG_BIOS @@ -966,22 +967,30 @@ void pc_memory_init(MemoryRegion *system_memory, { char *filename; int ret, linux_boot, i; - ram_addr_t ram_addr, bios_offset, option_rom_offset; + MemoryRegion *ram, *bios, *isa_bios, *option_rom_mr; + MemoryRegion *ram_below_4g, *ram_above_4g; int bios_size, isa_bios_size; void *fw_cfg; linux_boot = (kernel_filename != NULL); - /* allocate RAM */ - ram_addr = qemu_ram_alloc(NULL, "pc.ram", - below_4g_mem_size + above_4g_mem_size); - cpu_register_physical_memory(0, 0xa0000, ram_addr); - cpu_register_physical_memory(0x100000, - below_4g_mem_size - 0x100000, - ram_addr + 0x100000); + /* Allocate RAM. We allocate it as a single memory region and use + * aliases to address portions of it, mostly for backwards compatiblity + * with older qemus that used qemu_ram_alloc(). + */ + ram = qemu_malloc(sizeof(*ram)); + memory_region_init_ram(ram, NULL, "pc.ram", + below_4g_mem_size + above_4g_mem_size); + ram_below_4g = qemu_malloc(sizeof(*ram_below_4g)); + memory_region_init_alias(ram_below_4g, "ram-below-4g", ram, + 0, below_4g_mem_size); + memory_region_add_subregion(system_memory, 0, ram_below_4g); if (above_4g_mem_size > 0) { - cpu_register_physical_memory(0x100000000ULL, above_4g_mem_size, - ram_addr + below_4g_mem_size); + ram_above_4g = qemu_malloc(sizeof(*ram_above_4g)); + memory_region_init_alias(ram_above_4g, "ram-above-4g", ram, + below_4g_mem_size, above_4g_mem_size); + memory_region_add_subregion(system_memory, 0x100000000ULL, + ram_above_4g); } /* BIOS load */ @@ -997,7 +1006,9 @@ void pc_memory_init(MemoryRegion *system_memory, (bios_size % 65536) != 0) { goto bios_error; } - bios_offset = qemu_ram_alloc(NULL, "pc.bios", bios_size); + bios = qemu_malloc(sizeof(*bios)); + memory_region_init_ram(bios, NULL, "pc.bios", bios_size); + memory_region_set_readonly(bios, true); ret = rom_add_file_fixed(bios_name, (uint32_t)(-bios_size), -1); if (ret != 0) { bios_error: @@ -1011,16 +1022,26 @@ void pc_memory_init(MemoryRegion *system_memory, isa_bios_size = bios_size; if (isa_bios_size > (128 * 1024)) isa_bios_size = 128 * 1024; - cpu_register_physical_memory(0x100000 - isa_bios_size, - isa_bios_size, - (bios_offset + bios_size - isa_bios_size) | IO_MEM_ROM); + isa_bios = qemu_malloc(sizeof(*isa_bios)); + memory_region_init_alias(isa_bios, "isa-bios", bios, + bios_size - isa_bios_size, isa_bios_size); + memory_region_add_subregion_overlap(system_memory, + 0x100000 - isa_bios_size, + isa_bios, + 1); + memory_region_set_readonly(isa_bios, true); - option_rom_offset = qemu_ram_alloc(NULL, "pc.rom", PC_ROM_SIZE); - cpu_register_physical_memory(PC_ROM_MIN_VGA, PC_ROM_SIZE, option_rom_offset); + option_rom_mr = qemu_malloc(sizeof(*option_rom_mr)); + memory_region_init_ram(option_rom_mr, NULL, "pc.rom", PC_ROM_SIZE); + memory_region_add_subregion_overlap(system_memory, + PC_ROM_MIN_VGA, + option_rom_mr, + 1); /* map all the bios at the top of memory */ - cpu_register_physical_memory((uint32_t)(-bios_size), - bios_size, bios_offset | IO_MEM_ROM); + memory_region_add_subregion(system_memory, + (uint32_t)(-bios_size), + bios); fw_cfg = bochs_bios_init(); rom_set_fw(fw_cfg); diff --git a/hw/pc.h b/hw/pc.h index fa575831d0..40684f44d6 100644 --- a/hw/pc.h +++ b/hw/pc.h @@ -2,6 +2,7 @@ #define HW_PC_H #include "qemu-common.h" +#include "memory.h" #include "ioport.h" #include "isa.h" #include "fdc.h" From 6bd105151ac5529605a478de3b6c3aceed5995e9 Mon Sep 17 00:00:00 2001 From: Avi Kivity Date: Tue, 26 Jul 2011 14:26:18 +0300 Subject: [PATCH 100/230] pc: move global memory map out of pc_init1() and into its callers Signed-off-by: Avi Kivity Signed-off-by: Anthony Liguori --- hw/pc_piix.c | 15 ++++++++------- 1 file changed, 8 insertions(+), 7 deletions(-) diff --git a/hw/pc_piix.c b/hw/pc_piix.c index d83854c4aa..f2d0476758 100644 --- a/hw/pc_piix.c +++ b/hw/pc_piix.c @@ -68,7 +68,8 @@ static void ioapic_init(IsaIrqState *isa_irq_state) } /* PC hardware initialisation */ -static void pc_init1(ram_addr_t ram_size, +static void pc_init1(MemoryRegion *system_memory, + ram_addr_t ram_size, const char *boot_device, const char *kernel_filename, const char *kernel_cmdline, @@ -91,9 +92,6 @@ static void pc_init1(ram_addr_t ram_size, DriveInfo *hd[MAX_IDE_BUS * MAX_IDE_DEVS]; BusState *idebus[MAX_IDE_BUS]; ISADevice *rtc_state; - MemoryRegion *system_memory; - - system_memory = get_system_memory(); pc_cpus_init(cpu_model); @@ -214,7 +212,8 @@ static void pc_init_pci(ram_addr_t ram_size, const char *initrd_filename, const char *cpu_model) { - pc_init1(ram_size, boot_device, + pc_init1(get_system_memory(), + ram_size, boot_device, kernel_filename, kernel_cmdline, initrd_filename, cpu_model, 1, 1); } @@ -226,7 +225,8 @@ static void pc_init_pci_no_kvmclock(ram_addr_t ram_size, const char *initrd_filename, const char *cpu_model) { - pc_init1(ram_size, boot_device, + pc_init1(get_system_memory(), + ram_size, boot_device, kernel_filename, kernel_cmdline, initrd_filename, cpu_model, 1, 0); } @@ -240,7 +240,8 @@ static void pc_init_isa(ram_addr_t ram_size, { if (cpu_model == NULL) cpu_model = "486"; - pc_init1(ram_size, boot_device, + pc_init1(get_system_memory(), + ram_size, boot_device, kernel_filename, kernel_cmdline, initrd_filename, cpu_model, 0, 1); } From 1e39101c649a008462db1ac1d027c62870454d1f Mon Sep 17 00:00:00 2001 From: Avi Kivity Date: Tue, 26 Jul 2011 14:26:19 +0300 Subject: [PATCH 101/230] pci: pass address space to pci bus when created This is now done sloppily, via get_system_memory(). Eventually callers will be converted to stop using that. Reviewed-by: Anthony Liguori Signed-off-by: Avi Kivity Signed-off-by: Anthony Liguori --- hw/apb_pci.c | 2 ++ hw/bonito.c | 4 +++- hw/grackle_pci.c | 5 +++-- hw/gt64xxx.c | 4 +++- hw/pc.h | 4 +++- hw/pc_piix.c | 3 ++- hw/pci.c | 16 +++++++++++----- hw/pci.h | 12 +++++++++--- hw/pci_host.h | 1 + hw/pci_internals.h | 1 + hw/piix_pci.c | 13 +++++++++---- hw/ppc4xx_pci.c | 5 ++++- hw/ppc_mac.h | 9 ++++++--- hw/ppc_newworld.c | 5 +++-- hw/ppc_oldworld.c | 3 ++- hw/ppc_prep.c | 3 ++- hw/ppce500_pci.c | 6 +++++- hw/prep_pci.c | 5 +++-- hw/prep_pci.h | 3 ++- hw/sh_pci.c | 4 +++- hw/unin_pci.c | 10 ++++++---- hw/versatile_pci.c | 2 ++ 22 files changed, 85 insertions(+), 35 deletions(-) diff --git a/hw/apb_pci.c b/hw/apb_pci.c index 974c87a8ce..8b9939c06a 100644 --- a/hw/apb_pci.c +++ b/hw/apb_pci.c @@ -34,6 +34,7 @@ #include "rwhandler.h" #include "apb_pci.h" #include "sysemu.h" +#include "exec-memory.h" /* debug APB */ //#define DEBUG_APB @@ -346,6 +347,7 @@ PCIBus *pci_apb_init(target_phys_addr_t special_base, d->bus = pci_register_bus(&d->busdev.qdev, "pci", pci_apb_set_irq, pci_pbm_map_irq, d, + get_system_memory(), 0, 32); pci_bus_set_mem_base(d->bus, mem_base); diff --git a/hw/bonito.c b/hw/bonito.c index e8c57a36ff..5f62dda6e2 100644 --- a/hw/bonito.c +++ b/hw/bonito.c @@ -42,6 +42,7 @@ #include "mips.h" #include "pci_host.h" #include "sysemu.h" +#include "exec-memory.h" //#define DEBUG_BONITO @@ -773,7 +774,8 @@ PCIBus *bonito_init(qemu_irq *pic) dev = qdev_create(NULL, "Bonito-pcihost"); pcihost = FROM_SYSBUS(BonitoState, sysbus_from_qdev(dev)); b = pci_register_bus(&pcihost->busdev.qdev, "pci", pci_bonito_set_irq, - pci_bonito_map_irq, pic, 0x28, 32); + pci_bonito_map_irq, pic, get_system_memory(), + 0x28, 32); pcihost->bus = b; qdev_init_nofail(dev); diff --git a/hw/grackle_pci.c b/hw/grackle_pci.c index cee07e06c7..da67cf9b38 100644 --- a/hw/grackle_pci.c +++ b/hw/grackle_pci.c @@ -61,7 +61,8 @@ static void pci_grackle_reset(void *opaque) { } -PCIBus *pci_grackle_init(uint32_t base, qemu_irq *pic) +PCIBus *pci_grackle_init(uint32_t base, qemu_irq *pic, + MemoryRegion *address_space) { DeviceState *dev; SysBusDevice *s; @@ -74,7 +75,7 @@ PCIBus *pci_grackle_init(uint32_t base, qemu_irq *pic) d->host_state.bus = pci_register_bus(&d->busdev.qdev, "pci", pci_grackle_set_irq, pci_grackle_map_irq, - pic, 0, 4); + pic, address_space, 0, 4); pci_create_simple(d->host_state.bus, 0, "grackle"); diff --git a/hw/gt64xxx.c b/hw/gt64xxx.c index 8e1f6a069d..65e63ddab8 100644 --- a/hw/gt64xxx.c +++ b/hw/gt64xxx.c @@ -27,6 +27,7 @@ #include "pci.h" #include "pci_host.h" #include "pc.h" +#include "exec-memory.h" //#define DEBUG @@ -1092,7 +1093,8 @@ PCIBus *gt64120_register(qemu_irq *pic) d = FROM_SYSBUS(GT64120State, s); d->pci.bus = pci_register_bus(&d->busdev.qdev, "pci", gt64120_pci_set_irq, gt64120_pci_map_irq, - pic, PCI_DEVFN(18, 0), 4); + pic, get_system_memory(), + PCI_DEVFN(18, 0), 4); d->ISD_handle = cpu_register_io_memory(gt64120_read, gt64120_write, d, DEVICE_NATIVE_ENDIAN); diff --git a/hw/pc.h b/hw/pc.h index 40684f44d6..a2de0fecfa 100644 --- a/hw/pc.h +++ b/hw/pc.h @@ -178,7 +178,9 @@ int pcspk_audio_init(qemu_irq *pic); struct PCII440FXState; typedef struct PCII440FXState PCII440FXState; -PCIBus *i440fx_init(PCII440FXState **pi440fx_state, int *piix_devfn, qemu_irq *pic, ram_addr_t ram_size); +PCIBus *i440fx_init(PCII440FXState **pi440fx_state, int *piix_devfn, + qemu_irq *pic, MemoryRegion *address_space, + ram_addr_t ram_size); void i440fx_init_memory_mappings(PCII440FXState *d); /* piix4.c */ diff --git a/hw/pc_piix.c b/hw/pc_piix.c index f2d0476758..2b9c2b113c 100644 --- a/hw/pc_piix.c +++ b/hw/pc_piix.c @@ -128,7 +128,8 @@ static void pc_init1(MemoryRegion *system_memory, isa_irq = qemu_allocate_irqs(isa_irq_handler, isa_irq_state, 24); if (pci_enabled) { - pci_bus = i440fx_init(&i440fx_state, &piix3_devfn, isa_irq, ram_size); + pci_bus = i440fx_init(&i440fx_state, &piix3_devfn, isa_irq, + system_memory, ram_size); } else { pci_bus = NULL; i440fx_state = NULL; diff --git a/hw/pci.c b/hw/pci.c index b904a4ecb6..cf16f3b204 100644 --- a/hw/pci.c +++ b/hw/pci.c @@ -263,11 +263,14 @@ int pci_find_domain(const PCIBus *bus) } void pci_bus_new_inplace(PCIBus *bus, DeviceState *parent, - const char *name, uint8_t devfn_min) + const char *name, + MemoryRegion *address_space, + uint8_t devfn_min) { qbus_create_inplace(&bus->qbus, &pci_bus_info, parent, name); assert(PCI_FUNC(devfn_min) == 0); bus->devfn_min = devfn_min; + bus->address_space = address_space; /* host bridge */ QLIST_INIT(&bus->child); @@ -276,13 +279,14 @@ void pci_bus_new_inplace(PCIBus *bus, DeviceState *parent, vmstate_register(NULL, -1, &vmstate_pcibus, bus); } -PCIBus *pci_bus_new(DeviceState *parent, const char *name, uint8_t devfn_min) +PCIBus *pci_bus_new(DeviceState *parent, const char *name, + MemoryRegion *address_space, uint8_t devfn_min) { PCIBus *bus; bus = qemu_mallocz(sizeof(*bus)); bus->qbus.qdev_allocated = 1; - pci_bus_new_inplace(bus, parent, name, devfn_min); + pci_bus_new_inplace(bus, parent, name, address_space, devfn_min); return bus; } @@ -310,11 +314,13 @@ void pci_bus_set_mem_base(PCIBus *bus, target_phys_addr_t base) PCIBus *pci_register_bus(DeviceState *parent, const char *name, pci_set_irq_fn set_irq, pci_map_irq_fn map_irq, - void *irq_opaque, uint8_t devfn_min, int nirq) + void *irq_opaque, + MemoryRegion *address_space, + uint8_t devfn_min, int nirq) { PCIBus *bus; - bus = pci_bus_new(parent, name, devfn_min); + bus = pci_bus_new(parent, name, address_space, devfn_min); pci_bus_irqs(bus, set_irq, map_irq, irq_opaque, nirq); return bus; } diff --git a/hw/pci.h b/hw/pci.h index c220745c98..cfeb042219 100644 --- a/hw/pci.h +++ b/hw/pci.h @@ -5,6 +5,7 @@ #include "qobject.h" #include "qdev.h" +#include "memory.h" /* PCI includes legacy ISA access. */ #include "isa.h" @@ -233,15 +234,20 @@ typedef enum { typedef int (*pci_hotplug_fn)(DeviceState *qdev, PCIDevice *pci_dev, PCIHotplugState state); void pci_bus_new_inplace(PCIBus *bus, DeviceState *parent, - const char *name, uint8_t devfn_min); -PCIBus *pci_bus_new(DeviceState *parent, const char *name, uint8_t devfn_min); + const char *name, + MemoryRegion *address_space, + uint8_t devfn_min); +PCIBus *pci_bus_new(DeviceState *parent, const char *name, + MemoryRegion *address_space, uint8_t devfn_min); void pci_bus_irqs(PCIBus *bus, pci_set_irq_fn set_irq, pci_map_irq_fn map_irq, void *irq_opaque, int nirq); int pci_bus_get_irq_level(PCIBus *bus, int irq_num); void pci_bus_hotplug(PCIBus *bus, pci_hotplug_fn hotplug, DeviceState *dev); PCIBus *pci_register_bus(DeviceState *parent, const char *name, pci_set_irq_fn set_irq, pci_map_irq_fn map_irq, - void *irq_opaque, uint8_t devfn_min, int nirq); + void *irq_opaque, + MemoryRegion *address_space, + uint8_t devfn_min, int nirq); void pci_device_reset(PCIDevice *dev); void pci_bus_reset(PCIBus *bus); diff --git a/hw/pci_host.h b/hw/pci_host.h index 0a585951e0..05dcb662c6 100644 --- a/hw/pci_host.h +++ b/hw/pci_host.h @@ -35,6 +35,7 @@ struct PCIHostState { SysBusDevice busdev; ReadWriteHandler conf_handler; ReadWriteHandler data_handler; + MemoryRegion *address_space; uint32_t config_reg; PCIBus *bus; }; diff --git a/hw/pci_internals.h b/hw/pci_internals.h index fbe1866808..c3a463a703 100644 --- a/hw/pci_internals.h +++ b/hw/pci_internals.h @@ -25,6 +25,7 @@ struct PCIBus { PCIDevice *devices[PCI_SLOT_MAX * PCI_FUNC_MAX]; PCIDevice *parent_dev; target_phys_addr_t mem_base; + MemoryRegion *address_space; QLIST_HEAD(, PCIBus) child; /* this will be replaced by qdev later */ QLIST_ENTRY(PCIBus) sibling;/* this will be replaced by qdev later */ diff --git a/hw/piix_pci.c b/hw/piix_pci.c index d08b31a266..80d6665350 100644 --- a/hw/piix_pci.c +++ b/hw/piix_pci.c @@ -241,7 +241,9 @@ static int i440fx_initfn(PCIDevice *dev) static PCIBus *i440fx_common_init(const char *device_name, PCII440FXState **pi440fx_state, int *piix3_devfn, - qemu_irq *pic, ram_addr_t ram_size) + qemu_irq *pic, + MemoryRegion *address_space, + ram_addr_t ram_size) { DeviceState *dev; PCIBus *b; @@ -251,7 +253,8 @@ static PCIBus *i440fx_common_init(const char *device_name, dev = qdev_create(NULL, "i440FX-pcihost"); s = FROM_SYSBUS(I440FXState, sysbus_from_qdev(dev)); - b = pci_bus_new(&s->busdev.qdev, NULL, 0); + s->address_space = address_space; + b = pci_bus_new(&s->busdev.qdev, NULL, s->address_space, 0); s->bus = b; qdev_init_nofail(dev); @@ -288,11 +291,13 @@ static PCIBus *i440fx_common_init(const char *device_name, } PCIBus *i440fx_init(PCII440FXState **pi440fx_state, int *piix3_devfn, - qemu_irq *pic, ram_addr_t ram_size) + qemu_irq *pic, MemoryRegion *address_space, + ram_addr_t ram_size) { PCIBus *b; - b = i440fx_common_init("i440FX", pi440fx_state, piix3_devfn, pic, ram_size); + b = i440fx_common_init("i440FX", pi440fx_state, piix3_devfn, pic, + address_space, ram_size); return b; } diff --git a/hw/ppc4xx_pci.c b/hw/ppc4xx_pci.c index 299473c4b5..15c24f6e7a 100644 --- a/hw/ppc4xx_pci.c +++ b/hw/ppc4xx_pci.c @@ -24,6 +24,7 @@ #include "ppc4xx.h" #include "pci.h" #include "pci_host.h" +#include "exec-memory.h" #undef DEBUG #ifdef DEBUG @@ -345,7 +346,9 @@ PCIBus *ppc4xx_pci_init(CPUState *env, qemu_irq pci_irqs[4], controller->pci_state.bus = pci_register_bus(NULL, "pci", ppc4xx_pci_set_irq, ppc4xx_pci_map_irq, - pci_irqs, 0, 4); + pci_irqs, + get_system_memory(), + 0, 4); controller->pci_dev = pci_register_device(controller->pci_state.bus, "host bridge", sizeof(PCIDevice), diff --git a/hw/ppc_mac.h b/hw/ppc_mac.h index 68dade7e40..6fad20a745 100644 --- a/hw/ppc_mac.h +++ b/hw/ppc_mac.h @@ -25,6 +25,8 @@ #if !defined(__PPC_MAC_H__) #define __PPC_MAC_H__ +#include "memory.h" + /* SMP is not enabled, for now */ #define MAX_CPUS 1 @@ -52,11 +54,12 @@ qemu_irq *heathrow_pic_init(int *pmem_index, int nb_cpus, qemu_irq **irqs); /* Grackle PCI */ -PCIBus *pci_grackle_init(uint32_t base, qemu_irq *pic); +PCIBus *pci_grackle_init(uint32_t base, qemu_irq *pic, + MemoryRegion *address_space); /* UniNorth PCI */ -PCIBus *pci_pmac_init(qemu_irq *pic); -PCIBus *pci_pmac_u3_init(qemu_irq *pic); +PCIBus *pci_pmac_init(qemu_irq *pic, MemoryRegion *address_space); +PCIBus *pci_pmac_u3_init(qemu_irq *pic, MemoryRegion *address_space); /* Mac NVRAM */ typedef struct MacIONVRAMState MacIONVRAMState; diff --git a/hw/ppc_newworld.c b/hw/ppc_newworld.c index 5bce709bab..2c0fae8ef3 100644 --- a/hw/ppc_newworld.c +++ b/hw/ppc_newworld.c @@ -67,6 +67,7 @@ #include "kvm_ppc.h" #include "hw/usb.h" #include "blockdev.h" +#include "exec-memory.h" #define MAX_IDE_BUS 2 #define CFG_ADDR 0xf0000510 @@ -317,10 +318,10 @@ static void ppc_core99_init (ram_addr_t ram_size, pic = openpic_init(NULL, &pic_mem_index, smp_cpus, openpic_irqs, NULL); if (PPC_INPUT(env) == PPC_FLAGS_INPUT_970) { /* 970 gets a U3 bus */ - pci_bus = pci_pmac_u3_init(pic); + pci_bus = pci_pmac_u3_init(pic, get_system_memory()); machine_arch = ARCH_MAC99_U3; } else { - pci_bus = pci_pmac_init(pic); + pci_bus = pci_pmac_init(pic, get_system_memory()); machine_arch = ARCH_MAC99; } /* init basic PC hardware */ diff --git a/hw/ppc_oldworld.c b/hw/ppc_oldworld.c index 20cd8e1a8d..585afd6c4b 100644 --- a/hw/ppc_oldworld.c +++ b/hw/ppc_oldworld.c @@ -43,6 +43,7 @@ #include "kvm.h" #include "kvm_ppc.h" #include "blockdev.h" +#include "exec-memory.h" #define MAX_IDE_BUS 2 #define CFG_ADDR 0xf0000510 @@ -233,7 +234,7 @@ static void ppc_heathrow_init (ram_addr_t ram_size, hw_error("Only 6xx bus is supported on heathrow machine\n"); } pic = heathrow_pic_init(&pic_mem_index, 1, heathrow_irqs); - pci_bus = pci_grackle_init(0xfec00000, pic); + pci_bus = pci_grackle_init(0xfec00000, pic, get_system_memory()); pci_vga_init(pci_bus); escc_mem_index = escc_init(0x80013000, pic[0x0f], pic[0x10], serial_hds[0], diff --git a/hw/ppc_prep.c b/hw/ppc_prep.c index 0e9cfc24cd..91ebe07dcd 100644 --- a/hw/ppc_prep.c +++ b/hw/ppc_prep.c @@ -38,6 +38,7 @@ #include "loader.h" #include "mc146818rtc.h" #include "blockdev.h" +#include "exec-memory.h" //#define HARD_DEBUG_PPC_IO //#define DEBUG_PPC_IO @@ -648,7 +649,7 @@ static void ppc_prep_init (ram_addr_t ram_size, hw_error("Only 6xx bus is supported on PREP machine\n"); } i8259 = i8259_init(first_cpu->irq_inputs[PPC6xx_INPUT_INT]); - pci_bus = pci_prep_init(i8259); + pci_bus = pci_prep_init(i8259, get_system_memory()); /* Hmm, prep has no pci-isa bridge ??? */ isa_bus_new(NULL); isa_bus_irqs(i8259); diff --git a/hw/ppce500_pci.c b/hw/ppce500_pci.c index fc11af4374..134453965b 100644 --- a/hw/ppce500_pci.c +++ b/hw/ppce500_pci.c @@ -274,12 +274,15 @@ static void e500_pci_map(SysBusDevice *dev, target_phys_addr_t base) s->reg); } +#include "exec-memory.h" + static int e500_pcihost_initfn(SysBusDevice *dev) { PCIHostState *h; PPCE500PCIState *s; PCIBus *b; int i; + MemoryRegion *address_space = get_system_memory(); h = FROM_SYSBUS(PCIHostState, sysbus_from_qdev(dev)); s = DO_UPCAST(PPCE500PCIState, pci_state, h); @@ -289,7 +292,8 @@ static int e500_pcihost_initfn(SysBusDevice *dev) } b = pci_register_bus(&s->pci_state.busdev.qdev, NULL, mpc85xx_pci_set_irq, - mpc85xx_pci_map_irq, s->irq, PCI_DEVFN(0x11, 0), 4); + mpc85xx_pci_map_irq, s->irq, address_space, + PCI_DEVFN(0x11, 0), 4); s->pci_state.bus = b; pci_create_simple(b, 0, "e500-host-bridge"); diff --git a/hw/prep_pci.c b/hw/prep_pci.c index f88b8254c2..da02f0ea8e 100644 --- a/hw/prep_pci.c +++ b/hw/prep_pci.c @@ -110,7 +110,7 @@ static void prep_set_irq(void *opaque, int irq_num, int level) qemu_set_irq(pic[(irq_num & 1) ? 11 : 9] , level); } -PCIBus *pci_prep_init(qemu_irq *pic) +PCIBus *pci_prep_init(qemu_irq *pic, MemoryRegion *address_space) { PREPPCIState *s; PCIDevice *d; @@ -118,7 +118,8 @@ PCIBus *pci_prep_init(qemu_irq *pic) s = qemu_mallocz(sizeof(PREPPCIState)); s->bus = pci_register_bus(NULL, "pci", - prep_set_irq, prep_map_irq, pic, 0, 4); + prep_set_irq, prep_map_irq, pic, + address_space, 0, 4); pci_host_conf_register_ioport(0xcf8, s); diff --git a/hw/prep_pci.h b/hw/prep_pci.h index cd6851288c..a27368b99b 100644 --- a/hw/prep_pci.h +++ b/hw/prep_pci.h @@ -2,7 +2,8 @@ #define QEMU_PREP_PCI_H #include "qemu-common.h" +#include "memory.h" -PCIBus *pci_prep_init(qemu_irq *pic); +PCIBus *pci_prep_init(qemu_irq *pic, MemoryRegion *address_space); #endif diff --git a/hw/sh_pci.c b/hw/sh_pci.c index a076cf2ff0..0ef93a062e 100644 --- a/hw/sh_pci.c +++ b/hw/sh_pci.c @@ -26,6 +26,7 @@ #include "pci.h" #include "pci_host.h" #include "bswap.h" +#include "exec-memory.h" typedef struct SHPCIState { SysBusDevice busdev; @@ -127,7 +128,8 @@ static int sh_pci_init_device(SysBusDevice *dev) } s->bus = pci_register_bus(&s->busdev.qdev, "pci", sh_pci_set_irq, sh_pci_map_irq, - s->irq, PCI_DEVFN(0, 0), 4); + s->irq, get_system_memory(), + PCI_DEVFN(0, 0), 4); s->memconfig = cpu_register_io_memory(sh_pci_reg.r, sh_pci_reg.w, s, DEVICE_NATIVE_ENDIAN); sysbus_init_mmio_cb(dev, 0x224, sh_pci_map); diff --git a/hw/unin_pci.c b/hw/unin_pci.c index d364daa53a..b499523c93 100644 --- a/hw/unin_pci.c +++ b/hw/unin_pci.c @@ -201,7 +201,7 @@ static int pci_unin_internal_init_device(SysBusDevice *dev) return 0; } -PCIBus *pci_pmac_init(qemu_irq *pic) +PCIBus *pci_pmac_init(qemu_irq *pic, MemoryRegion *address_space) { DeviceState *dev; SysBusDevice *s; @@ -215,7 +215,8 @@ PCIBus *pci_pmac_init(qemu_irq *pic) d = FROM_SYSBUS(UNINState, s); d->host_state.bus = pci_register_bus(&d->busdev.qdev, "pci", pci_unin_set_irq, pci_unin_map_irq, - pic, PCI_DEVFN(11, 0), 4); + pic, address_space, + PCI_DEVFN(11, 0), 4); #if 0 pci_create_simple(d->host_state.bus, PCI_DEVFN(11, 0), "uni-north"); @@ -252,7 +253,7 @@ PCIBus *pci_pmac_init(qemu_irq *pic) return d->host_state.bus; } -PCIBus *pci_pmac_u3_init(qemu_irq *pic) +PCIBus *pci_pmac_u3_init(qemu_irq *pic, MemoryRegion *address_space) { DeviceState *dev; SysBusDevice *s; @@ -267,7 +268,8 @@ PCIBus *pci_pmac_u3_init(qemu_irq *pic) d->host_state.bus = pci_register_bus(&d->busdev.qdev, "pci", pci_unin_set_irq, pci_unin_map_irq, - pic, PCI_DEVFN(11, 0), 4); + pic, address_space, + PCI_DEVFN(11, 0), 4); sysbus_mmio_map(s, 0, 0xf0800000); sysbus_mmio_map(s, 1, 0xf0c00000); diff --git a/hw/versatile_pci.c b/hw/versatile_pci.c index 290a9009b2..cffe387187 100644 --- a/hw/versatile_pci.c +++ b/hw/versatile_pci.c @@ -10,6 +10,7 @@ #include "sysbus.h" #include "pci.h" #include "pci_host.h" +#include "exec-memory.h" typedef struct { SysBusDevice busdev; @@ -111,6 +112,7 @@ static int pci_vpb_init(SysBusDevice *dev) } bus = pci_register_bus(&dev->qdev, "pci", pci_vpb_set_irq, pci_vpb_map_irq, s->irq, + get_system_memory(), PCI_DEVFN(11, 0), 4); /* ??? Register memory space. */ From 79ff8cb0df5f3f7ec818690f7ad5bdc03859525d Mon Sep 17 00:00:00 2001 From: Avi Kivity Date: Tue, 26 Jul 2011 14:26:20 +0300 Subject: [PATCH 102/230] pci: add MemoryRegion based BAR management API Allow registering a BAR using a MemoryRegion. Once all users are converted, pci_register_bar() and pci_register_bar_simple() will be removed. Reviewed-by: Anthony Liguori Signed-off-by: Avi Kivity Signed-off-by: Anthony Liguori --- hw/pci.c | 47 +++++++++++++++++++++++++++++++++++++++-------- hw/pci.h | 3 +++ 2 files changed, 42 insertions(+), 8 deletions(-) diff --git a/hw/pci.c b/hw/pci.c index cf16f3b204..36db58be76 100644 --- a/hw/pci.c +++ b/hw/pci.c @@ -844,10 +844,15 @@ static void pci_unregister_io_regions(PCIDevice *pci_dev) if (r->type == PCI_BASE_ADDRESS_SPACE_IO) { isa_unassign_ioport(r->addr, r->filtered_size); } else { - cpu_register_physical_memory(pci_to_cpu_addr(pci_dev->bus, - r->addr), - r->filtered_size, - IO_MEM_UNASSIGNED); + if (r->memory) { + memory_region_del_subregion(pci_dev->bus->address_space, + r->memory); + } else { + cpu_register_physical_memory(pci_to_cpu_addr(pci_dev->bus, + r->addr), + r->filtered_size, + IO_MEM_UNASSIGNED); + } } } } @@ -893,6 +898,7 @@ void pci_register_bar(PCIDevice *pci_dev, int region_num, r->type = type; r->map_func = map_func; r->ram_addr = IO_MEM_UNASSIGNED; + r->memory = NULL; wmask = ~(size - 1); addr = pci_bar(pci_dev, region_num); @@ -918,6 +924,16 @@ static void pci_simple_bar_mapfunc(PCIDevice *pci_dev, int region_num, pci_dev->io_regions[region_num].ram_addr); } +static void pci_simple_bar_mapfunc_region(PCIDevice *pci_dev, int region_num, + pcibus_t addr, pcibus_t size, + int type) +{ + memory_region_add_subregion_overlap(pci_dev->bus->address_space, + addr, + pci_dev->io_regions[region_num].memory, + 1); +} + void pci_register_bar_simple(PCIDevice *pci_dev, int region_num, pcibus_t size, uint8_t attr, ram_addr_t ram_addr) { @@ -927,6 +943,15 @@ void pci_register_bar_simple(PCIDevice *pci_dev, int region_num, pci_dev->io_regions[region_num].ram_addr = ram_addr; } +void pci_register_bar_region(PCIDevice *pci_dev, int region_num, + uint8_t attr, MemoryRegion *memory) +{ + pci_register_bar(pci_dev, region_num, memory_region_size(memory), + PCI_BASE_ADDRESS_SPACE_MEMORY | attr, + pci_simple_bar_mapfunc_region); + pci_dev->io_regions[region_num].memory = memory; +} + static void pci_bridge_filter(PCIDevice *d, pcibus_t *addr, pcibus_t *size, uint8_t type) { @@ -1065,10 +1090,16 @@ static void pci_update_mappings(PCIDevice *d) isa_unassign_ioport(r->addr, r->filtered_size); } } else { - cpu_register_physical_memory(pci_to_cpu_addr(d->bus, r->addr), - r->filtered_size, - IO_MEM_UNASSIGNED); - qemu_unregister_coalesced_mmio(r->addr, r->filtered_size); + if (r->memory) { + memory_region_del_subregion(d->bus->address_space, + r->memory); + } else { + cpu_register_physical_memory(pci_to_cpu_addr(d->bus, + r->addr), + r->filtered_size, + IO_MEM_UNASSIGNED); + qemu_unregister_coalesced_mmio(r->addr, r->filtered_size); + } } } r->addr = new_addr; diff --git a/hw/pci.h b/hw/pci.h index cfeb042219..c51156d21c 100644 --- a/hw/pci.h +++ b/hw/pci.h @@ -94,6 +94,7 @@ typedef struct PCIIORegion { uint8_t type; PCIMapIORegionFunc *map_func; ram_addr_t ram_addr; + MemoryRegion *memory; } PCIIORegion; #define PCI_ROM_SLOT 6 @@ -204,6 +205,8 @@ void pci_register_bar(PCIDevice *pci_dev, int region_num, PCIMapIORegionFunc *map_func); void pci_register_bar_simple(PCIDevice *pci_dev, int region_num, pcibus_t size, uint8_t attr, ram_addr_t ram_addr); +void pci_register_bar_region(PCIDevice *pci_dev, int region_num, + uint8_t attr, MemoryRegion *memory); int pci_add_capability(PCIDevice *pdev, uint8_t cap_id, uint8_t offset, uint8_t size); From ec3bb837a21a7d32d3dcb010e955991f5784c1e8 Mon Sep 17 00:00:00 2001 From: Avi Kivity Date: Tue, 26 Jul 2011 14:26:21 +0300 Subject: [PATCH 103/230] sysbus: add MemoryRegion based memory management API Allow registering sysbus device memory using a MemoryRegion. Once all users are converted, sysbus_init_mmio() and sysbus_init_mmio_cb() will be removed. Reviewed-by: Anthony Liguori Signed-off-by: Avi Kivity Signed-off-by: Anthony Liguori --- hw/sysbus.c | 27 ++++++++++++++++++++++++--- hw/sysbus.h | 3 +++ 2 files changed, 27 insertions(+), 3 deletions(-) diff --git a/hw/sysbus.c b/hw/sysbus.c index 2e22be7b25..ea442acb50 100644 --- a/hw/sysbus.c +++ b/hw/sysbus.c @@ -19,6 +19,7 @@ #include "sysbus.h" #include "monitor.h" +#include "exec-memory.h" static void sysbus_dev_print(Monitor *mon, DeviceState *dev, int indent); static char *sysbus_get_fw_dev_path(DeviceState *dev); @@ -49,11 +50,20 @@ void sysbus_mmio_map(SysBusDevice *dev, int n, target_phys_addr_t addr) } if (dev->mmio[n].addr != (target_phys_addr_t)-1) { /* Unregister previous mapping. */ - cpu_register_physical_memory(dev->mmio[n].addr, dev->mmio[n].size, - IO_MEM_UNASSIGNED); + if (dev->mmio[n].memory) { + memory_region_del_subregion(get_system_memory(), + dev->mmio[n].memory); + } else { + cpu_register_physical_memory(dev->mmio[n].addr, dev->mmio[n].size, + IO_MEM_UNASSIGNED); + } } dev->mmio[n].addr = addr; - if (dev->mmio[n].cb) { + if (dev->mmio[n].memory) { + memory_region_add_subregion(get_system_memory(), + addr, + dev->mmio[n].memory); + } else if (dev->mmio[n].cb) { dev->mmio[n].cb(dev, addr); } else { cpu_register_physical_memory(addr, dev->mmio[n].size, @@ -107,6 +117,17 @@ void sysbus_init_mmio_cb(SysBusDevice *dev, target_phys_addr_t size, dev->mmio[n].cb = cb; } +void sysbus_init_mmio_region(SysBusDevice *dev, MemoryRegion *memory) +{ + int n; + + assert(dev->num_mmio < QDEV_MAX_MMIO); + n = dev->num_mmio++; + dev->mmio[n].addr = -1; + dev->mmio[n].size = memory_region_size(memory); + dev->mmio[n].memory = memory; +} + void sysbus_init_ioports(SysBusDevice *dev, pio_addr_t ioport, pio_addr_t size) { pio_addr_t i; diff --git a/hw/sysbus.h b/hw/sysbus.h index 4e8cb16d42..5f62e2da31 100644 --- a/hw/sysbus.h +++ b/hw/sysbus.h @@ -4,6 +4,7 @@ /* Devices attached directly to the main system bus. */ #include "qdev.h" +#include "memory.h" #define QDEV_MAX_MMIO 32 #define QDEV_MAX_PIO 32 @@ -23,6 +24,7 @@ struct SysBusDevice { target_phys_addr_t size; mmio_mapfunc cb; ram_addr_t iofunc; + MemoryRegion *memory; } mmio[QDEV_MAX_MMIO]; int num_pio; pio_addr_t pio[QDEV_MAX_PIO]; @@ -46,6 +48,7 @@ void sysbus_init_mmio(SysBusDevice *dev, target_phys_addr_t size, ram_addr_t iofunc); void sysbus_init_mmio_cb(SysBusDevice *dev, target_phys_addr_t size, mmio_mapfunc cb); +void sysbus_init_mmio_region(SysBusDevice *dev, MemoryRegion *memory); void sysbus_init_irq(SysBusDevice *dev, qemu_irq *p); void sysbus_pass_irq(SysBusDevice *dev, SysBusDevice *target); void sysbus_init_ioports(SysBusDevice *dev, pio_addr_t ioport, pio_addr_t size); From 6da48311bbd884da4d0a4ae03b0fc7a78981a5f4 Mon Sep 17 00:00:00 2001 From: Avi Kivity Date: Tue, 26 Jul 2011 14:26:22 +0300 Subject: [PATCH 104/230] usb-ohci: convert to MemoryRegion Reviewed-by: Anthony Liguori Signed-off-by: Avi Kivity Signed-off-by: Anthony Liguori --- hw/usb-ohci.c | 42 +++++++++++++++++------------------------- 1 file changed, 17 insertions(+), 25 deletions(-) diff --git a/hw/usb-ohci.c b/hw/usb-ohci.c index 8491d59928..337b250261 100644 --- a/hw/usb-ohci.c +++ b/hw/usb-ohci.c @@ -62,7 +62,7 @@ typedef struct OHCIPort { typedef struct { USBBus bus; qemu_irq irq; - int mem; + MemoryRegion mem; int num_ports; const char *name; @@ -1440,13 +1440,13 @@ static void ohci_port_set_status(OHCIState *ohci, int portnum, uint32_t val) return; } -static uint32_t ohci_mem_read(void *ptr, target_phys_addr_t addr) +static uint64_t ohci_mem_read(void *opaque, + target_phys_addr_t addr, + unsigned size) { - OHCIState *ohci = ptr; + OHCIState *ohci = opaque; uint32_t retval; - addr &= 0xff; - /* Only aligned reads are allowed on OHCI */ if (addr & 3) { fprintf(stderr, "usb-ohci: Mis-aligned read\n"); @@ -1563,11 +1563,12 @@ static uint32_t ohci_mem_read(void *ptr, target_phys_addr_t addr) return retval; } -static void ohci_mem_write(void *ptr, target_phys_addr_t addr, uint32_t val) +static void ohci_mem_write(void *opaque, + target_phys_addr_t addr, + uint64_t val, + unsigned size) { - OHCIState *ohci = ptr; - - addr &= 0xff; + OHCIState *ohci = opaque; /* Only aligned reads are allowed on OHCI */ if (addr & 3) { @@ -1697,18 +1698,10 @@ static void ohci_async_cancel_device(OHCIState *ohci, USBDevice *dev) } } -/* Only dword reads are defined on OHCI register space */ -static CPUReadMemoryFunc * const ohci_readfn[3]={ - ohci_mem_read, - ohci_mem_read, - ohci_mem_read -}; - -/* Only dword writes are defined on OHCI register space */ -static CPUWriteMemoryFunc * const ohci_writefn[3]={ - ohci_mem_write, - ohci_mem_write, - ohci_mem_write +static const MemoryRegionOps ohci_mem_ops = { + .read = ohci_mem_read, + .write = ohci_mem_write, + .endianness = DEVICE_LITTLE_ENDIAN, }; static USBPortOps ohci_port_ops = { @@ -1764,8 +1757,7 @@ static int usb_ohci_init(OHCIState *ohci, DeviceState *dev, } } - ohci->mem = cpu_register_io_memory(ohci_readfn, ohci_writefn, ohci, - DEVICE_LITTLE_ENDIAN); + memory_region_init_io(&ohci->mem, &ohci_mem_ops, ohci, "ohci", 256); ohci->localmem_base = localmem_base; ohci->name = dev->info->name; @@ -1799,7 +1791,7 @@ static int usb_ohci_initfn_pci(struct PCIDevice *dev) ohci->state.irq = ohci->pci_dev.irq[0]; /* TODO: avoid cast below by using dev */ - pci_register_bar_simple(&ohci->pci_dev, 0, 256, 0, ohci->state.mem); + pci_register_bar_region(&ohci->pci_dev, 0, 0, &ohci->state.mem); return 0; } @@ -1822,7 +1814,7 @@ static int ohci_init_pxa(SysBusDevice *dev) /* Cannot fail as we pass NULL for masterbus */ usb_ohci_init(&s->ohci, &dev->qdev, s->num_ports, s->dma_offset, NULL, 0); sysbus_init_irq(dev, &s->ohci.irq); - sysbus_init_mmio(dev, 0x1000, s->ohci.mem); + sysbus_init_mmio_region(dev, &s->ohci.mem); return 0; } From 3d3b8303c6f83b9b245bc774af530a6403cc4ce6 Mon Sep 17 00:00:00 2001 From: wayne Date: Wed, 27 Jul 2011 18:04:55 +0800 Subject: [PATCH 105/230] showing a splash picture when start Added options to let qemu transfer two configuration files to bios: "bootsplash.bmp" and "etc/boot-menu-wait", which could be specified by command -boot splash=P,splash-time=T P is jpg/bmp file name or an absolute path, T have a max value of 0xffff, unit is ms. With these two options, if user invoke qemu with menu=on option, then a splash picture would be showed in a given time. For example: qemu -boot menu=on,splash=/root/boot.bmp,splash-time=5000 would make boot.bmp shown as a brand with 5 seconds in the booting up process. This feature need the new seabios's support, which could be got from git. Signed-off-by: Wayne Xia Signed-off-by: Anthony Liguori --- hw/fw_cfg.c | 140 +++++++++++++++++++++++++++++++++++++++++++++++- qemu-config.c | 27 ++++++++++ qemu-options.hx | 16 +++++- sysemu.h | 3 ++ vl.c | 17 +++++- 5 files changed, 199 insertions(+), 4 deletions(-) diff --git a/hw/fw_cfg.c b/hw/fw_cfg.c index 34e7526d59..a29db9055d 100644 --- a/hw/fw_cfg.c +++ b/hw/fw_cfg.c @@ -26,6 +26,7 @@ #include "isa.h" #include "fw_cfg.h" #include "sysbus.h" +#include "qemu-error.h" /* debug firmware config */ //#define DEBUG_FW_CFG @@ -56,6 +57,143 @@ struct FWCfgState { Notifier machine_ready; }; +#define JPG_FILE 0 +#define BMP_FILE 1 + +static FILE *probe_splashfile(char *filename, int *file_sizep, int *file_typep) +{ + FILE *fp = NULL; + int fop_ret; + int file_size; + int file_type = -1; + unsigned char buf[2] = {0, 0}; + unsigned int filehead_value = 0; + int bmp_bpp; + + fp = fopen(filename, "rb"); + if (fp == NULL) { + error_report("failed to open file '%s'.", filename); + return fp; + } + /* check file size */ + fseek(fp, 0L, SEEK_END); + file_size = ftell(fp); + if (file_size < 2) { + error_report("file size is less than 2 bytes '%s'.", filename); + fclose(fp); + fp = NULL; + return fp; + } + /* check magic ID */ + fseek(fp, 0L, SEEK_SET); + fop_ret = fread(buf, 1, 2, fp); + filehead_value = (buf[0] + (buf[1] << 8)) & 0xffff; + if (filehead_value == 0xd8ff) { + file_type = JPG_FILE; + } else { + if (filehead_value == 0x4d42) { + file_type = BMP_FILE; + } + } + if (file_type < 0) { + error_report("'%s' not jpg/bmp file,head:0x%x.", + filename, filehead_value); + fclose(fp); + fp = NULL; + return fp; + } + /* check BMP bpp */ + if (file_type == BMP_FILE) { + fseek(fp, 28, SEEK_SET); + fop_ret = fread(buf, 1, 2, fp); + bmp_bpp = (buf[0] + (buf[1] << 8)) & 0xffff; + if (bmp_bpp != 24) { + error_report("only 24bpp bmp file is supported."); + fclose(fp); + fp = NULL; + return fp; + } + } + /* return values */ + *file_sizep = file_size; + *file_typep = file_type; + return fp; +} + +static void fw_cfg_bootsplash(FWCfgState *s) +{ + int boot_splash_time = -1; + const char *boot_splash_filename = NULL; + char *p; + char *filename; + FILE *fp; + int fop_ret; + int file_size; + int file_type = -1; + const char *temp; + + /* get user configuration */ + QemuOptsList *plist = qemu_find_opts("boot-opts"); + QemuOpts *opts = QTAILQ_FIRST(&plist->head); + if (opts != NULL) { + temp = qemu_opt_get(opts, "splash"); + if (temp != NULL) { + boot_splash_filename = temp; + } + temp = qemu_opt_get(opts, "splash-time"); + if (temp != NULL) { + p = (char *)temp; + boot_splash_time = strtol(p, (char **)&p, 10); + } + } + + /* insert splash time if user configurated */ + if (boot_splash_time >= 0) { + /* validate the input */ + if (boot_splash_time > 0xffff) { + error_report("splash time is big than 65535, force it to 65535."); + boot_splash_time = 0xffff; + } + /* use little endian format */ + qemu_extra_params_fw[0] = (uint8_t)(boot_splash_time & 0xff); + qemu_extra_params_fw[1] = (uint8_t)((boot_splash_time >> 8) & 0xff); + fw_cfg_add_file(s, "etc/boot-menu-wait", qemu_extra_params_fw, 2); + } + + /* insert splash file if user configurated */ + if (boot_splash_filename != NULL) { + filename = qemu_find_file(QEMU_FILE_TYPE_BIOS, boot_splash_filename); + if (filename == NULL) { + error_report("failed to find file '%s'.", boot_splash_filename); + return; + } + /* probing the file */ + fp = probe_splashfile(filename, &file_size, &file_type); + if (fp == NULL) { + qemu_free(filename); + return; + } + /* loading file data */ + if (boot_splash_filedata != NULL) { + qemu_free(boot_splash_filedata); + } + boot_splash_filedata = qemu_malloc(file_size); + boot_splash_filedata_size = file_size; + fseek(fp, 0L, SEEK_SET); + fop_ret = fread(boot_splash_filedata, 1, file_size, fp); + fclose(fp); + /* insert data */ + if (file_type == JPG_FILE) { + fw_cfg_add_file(s, "bootsplash.jpg", + boot_splash_filedata, boot_splash_filedata_size); + } else { + fw_cfg_add_file(s, "bootsplash.bmp", + boot_splash_filedata, boot_splash_filedata_size); + } + qemu_free(filename); + } +} + static void fw_cfg_write(FWCfgState *s, uint8_t value) { int arch = !!(s->cur_entry & FW_CFG_ARCH_LOCAL); @@ -352,7 +490,7 @@ FWCfgState *fw_cfg_init(uint32_t ctl_port, uint32_t data_port, fw_cfg_add_i16(s, FW_CFG_NB_CPUS, (uint16_t)smp_cpus); fw_cfg_add_i16(s, FW_CFG_MAX_CPUS, (uint16_t)max_cpus); fw_cfg_add_i16(s, FW_CFG_BOOT_MENU, (uint16_t)boot_menu); - + fw_cfg_bootsplash(s); s->machine_ready.notify = fw_cfg_machine_ready; qemu_add_machine_init_done_notifier(&s->machine_ready); diff --git a/qemu-config.c b/qemu-config.c index b2ec40bd66..1eb6b9a709 100644 --- a/qemu-config.c +++ b/qemu-config.c @@ -480,6 +480,32 @@ static QemuOptsList qemu_machine_opts = { }, }; +QemuOptsList qemu_boot_opts = { + .name = "boot-opts", + .head = QTAILQ_HEAD_INITIALIZER(qemu_boot_opts.head), + .desc = { + /* the three names below are not used now */ + { + .name = "order", + .type = QEMU_OPT_STRING, + }, { + .name = "once", + .type = QEMU_OPT_STRING, + }, { + .name = "menu", + .type = QEMU_OPT_STRING, + /* following are really used */ + }, { + .name = "splash", + .type = QEMU_OPT_STRING, + }, { + .name = "splash-time", + .type = QEMU_OPT_STRING, + }, + { /*End of list */ } + }, +}; + static QemuOptsList *vm_config_groups[32] = { &qemu_drive_opts, &qemu_chardev_opts, @@ -495,6 +521,7 @@ static QemuOptsList *vm_config_groups[32] = { #endif &qemu_option_rom_opts, &qemu_machine_opts, + &qemu_boot_opts, NULL, }; diff --git a/qemu-options.hx b/qemu-options.hx index 1d57f64888..c77f868d40 100644 --- a/qemu-options.hx +++ b/qemu-options.hx @@ -303,10 +303,13 @@ ETEXI DEF("boot", HAS_ARG, QEMU_OPTION_boot, "-boot [order=drives][,once=drives][,menu=on|off]\n" - " 'drives': floppy (a), hard disk (c), CD-ROM (d), network (n)\n", + " [,splash=sp_name][,splash-time=sp_time]\n" + " 'drives': floppy (a), hard disk (c), CD-ROM (d), network (n)\n" + " 'sp_name': the file's name that would be passed to bios as logo picture, if menu=on\n" + " 'sp_time': the period that splash picture last if menu=on, unit is ms\n", QEMU_ARCH_ALL) STEXI -@item -boot [order=@var{drives}][,once=@var{drives}][,menu=on|off] +@item -boot [order=@var{drives}][,once=@var{drives}][,menu=on|off][,splash=@var{sp_name}][,splash-time=@var{sp_time}] @findex -boot Specify boot order @var{drives} as a string of drive letters. Valid drive letters depend on the target achitecture. The x86 PC uses: a, b @@ -318,11 +321,20 @@ particular boot order only on the first startup, specify it via Interactive boot menus/prompts can be enabled via @option{menu=on} as far as firmware/BIOS supports them. The default is non-interactive boot. +A splash picture could be passed to bios, enabling user to show it as logo, +when option splash=@var{sp_name} is given and menu=on, If firmware/BIOS +supports them. Currently Seabios for X86 system support it. +limitation: The splash file could be a jpeg file or a BMP file in 24 BPP +format(true color). The resolution should be supported by the SVGA mode, so +the recommended is 320x240, 640x480, 800x640. + @example # try to boot from network first, then from hard disk qemu -boot order=nc # boot from CD-ROM first, switch back to default order after reboot qemu -boot once=d +# boot with a splash picture for 5 seconds. +qemu -boot menu=on,splash=/root/boot.bmp,splash-time=5000 @end example Note: The legacy format '-boot @var{drives}' is still supported but its diff --git a/sysemu.h b/sysemu.h index d3013f5cc4..bd830e5149 100644 --- a/sysemu.h +++ b/sysemu.h @@ -123,6 +123,9 @@ extern int no_shutdown; extern int semihosting_enabled; extern int old_param; extern int boot_menu; +extern uint8_t *boot_splash_filedata; +extern int boot_splash_filedata_size; +extern uint8_t qemu_extra_params_fw[2]; extern QEMUClock *rtc_clock; #define MAX_NODES 64 diff --git a/vl.c b/vl.c index 4b6688b553..a3d8d77bf8 100644 --- a/vl.c +++ b/vl.c @@ -228,6 +228,9 @@ int ctrl_grab = 0; unsigned int nb_prom_envs = 0; const char *prom_envs[MAX_PROM_ENVS]; int boot_menu; +uint8_t *boot_splash_filedata; +int boot_splash_filedata_size; +uint8_t qemu_extra_params_fw[2]; typedef struct FWBootEntry FWBootEntry; @@ -293,6 +296,14 @@ static struct { { .driver = "qxl-vga", .flag = &default_vga }, }; +static void res_free(void) +{ + if (boot_splash_filedata != NULL) { + qemu_free(boot_splash_filedata); + boot_splash_filedata = NULL; + } +} + static int default_driver_check(QemuOpts *opts, void *opaque) { const char *driver = qemu_opt_get(opts, "driver"); @@ -2330,7 +2341,8 @@ int main(int argc, char **argv, char **envp) case QEMU_OPTION_boot: { static const char * const params[] = { - "order", "once", "menu", NULL + "order", "once", "menu", + "splash", "splash-time", NULL }; char buf[sizeof(boot_devices)]; char *standard_boot_devices; @@ -2373,6 +2385,8 @@ int main(int argc, char **argv, char **envp) exit(1); } } + qemu_opts_parse(qemu_find_opts("boot-opts"), + optarg, 0); } } break; @@ -3335,6 +3349,7 @@ int main(int argc, char **argv, char **envp) main_loop(); quit_timers(); net_cleanup(); + res_free(); return 0; } From 332ae28dad2d4f155e1ad82bf89a605c2b2710ba Mon Sep 17 00:00:00 2001 From: Paolo Bonzini Date: Thu, 28 Jul 2011 12:10:28 +0200 Subject: [PATCH 106/230] move WORDS_ALIGNED to qemu-common.h This is not a CPU interface, and a configure test would not be too precise. So just add it to qemu-common.h. Signed-off-by: Paolo Bonzini Signed-off-by: Anthony Liguori --- cpu-common.h | 4 ---- qemu-common.h | 4 ++++ 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/cpu-common.h b/cpu-common.h index 44b04b3839..16c9f4f487 100644 --- a/cpu-common.h +++ b/cpu-common.h @@ -3,10 +3,6 @@ /* CPU interfaces that are target indpendent. */ -#if defined(__arm__) || defined(__sparc__) || defined(__mips__) || defined(__hppa__) || defined(__ia64__) -#define WORDS_ALIGNED -#endif - #ifdef TARGET_PHYS_ADDR_BITS #include "targphys.h" #endif diff --git a/qemu-common.h b/qemu-common.h index 391fadda56..1e3c66511e 100644 --- a/qemu-common.h +++ b/qemu-common.h @@ -5,6 +5,10 @@ #include "compiler.h" #include "config-host.h" +#if defined(__arm__) || defined(__sparc__) || defined(__mips__) || defined(__hppa__) || defined(__ia64__) +#define WORDS_ALIGNED +#endif + #define TFR(expr) do { if ((expr) != -1) break; } while (errno == EINTR) typedef struct QEMUTimer QEMUTimer; From 789ec7ce20f34b175b3983707e077e8d67385126 Mon Sep 17 00:00:00 2001 From: Paolo Bonzini Date: Thu, 28 Jul 2011 12:10:29 +0200 Subject: [PATCH 107/230] softfloat: change default nan definitions to variables Most definitions in softfloat.h are really target-independent, but the file is not because it includes definitions of the default NaN values. Change those to variables to allow including softfloat.h from files that are not compiled per-target. By making them const, the compiler is allowed to optimize them into softfloat functions that use them. Signed-off-by: Paolo Bonzini Signed-off-by: Anthony Liguori --- fpu/softfloat-specialize.h | 72 ++++++++++++++++++++++++++++++++++++++ fpu/softfloat.h | 60 +++++-------------------------- 2 files changed, 81 insertions(+), 51 deletions(-) diff --git a/fpu/softfloat-specialize.h b/fpu/softfloat-specialize.h index c7d35a161d..c165205a49 100644 --- a/fpu/softfloat-specialize.h +++ b/fpu/softfloat-specialize.h @@ -35,6 +35,78 @@ these four paragraphs for those parts of this code that are retained. =============================================================================*/ +#if defined(TARGET_MIPS) || defined(TARGET_SH4) || defined(TARGET_UNICORE32) +#define SNAN_BIT_IS_ONE 1 +#else +#define SNAN_BIT_IS_ONE 0 +#endif + +/*---------------------------------------------------------------------------- +| The pattern for a default generated half-precision NaN. +*----------------------------------------------------------------------------*/ +#if defined(TARGET_ARM) +const float16 float16_default_nan = const_float16(0x7E00); +#elif SNAN_BIT_IS_ONE +const float16 float16_default_nan = const_float16(0x7DFF); +#else +const float16 float16_default_nan = const_float16(0xFE00); +#endif + +/*---------------------------------------------------------------------------- +| The pattern for a default generated single-precision NaN. +*----------------------------------------------------------------------------*/ +#if defined(TARGET_SPARC) +const float32 float32_default_nan = const_float32(0x7FFFFFFF); +#elif defined(TARGET_PPC) || defined(TARGET_ARM) || defined(TARGET_ALPHA) +const float32 float32_default_nan = const_float32(0x7FC00000); +#elif SNAN_BIT_IS_ONE +const float32 float32_default_nan = const_float32(0x7FBFFFFF); +#else +const float32 float32_default_nan = const_float32(0xFFC00000); +#endif + +/*---------------------------------------------------------------------------- +| The pattern for a default generated double-precision NaN. +*----------------------------------------------------------------------------*/ +#if defined(TARGET_SPARC) +const float64 float64_default_nan = const_float64(LIT64( 0x7FFFFFFFFFFFFFFF )); +#elif defined(TARGET_PPC) || defined(TARGET_ARM) || defined(TARGET_ALPHA) +const float64 float64_default_nan = const_float64(LIT64( 0x7FF8000000000000 )); +#elif SNAN_BIT_IS_ONE +const float64 float64_default_nan = const_float64(LIT64( 0x7FF7FFFFFFFFFFFF )); +#else +const float64 float64_default_nan = const_float64(LIT64( 0xFFF8000000000000 )); +#endif + +/*---------------------------------------------------------------------------- +| The pattern for a default generated extended double-precision NaN. +*----------------------------------------------------------------------------*/ +#if SNAN_BIT_IS_ONE +#define floatx80_default_nan_high 0x7FFF +#define floatx80_default_nan_low LIT64( 0xBFFFFFFFFFFFFFFF ) +#else +#define floatx80_default_nan_high 0xFFFF +#define floatx80_default_nan_low LIT64( 0xC000000000000000 ) +#endif + +const floatx80 floatx80_default_nan = make_floatx80(floatx80_default_nan_high, + floatx80_default_nan_low); + +/*---------------------------------------------------------------------------- +| The pattern for a default generated quadruple-precision NaN. The `high' and +| `low' values hold the most- and least-significant bits, respectively. +*----------------------------------------------------------------------------*/ +#if SNAN_BIT_IS_ONE +#define float128_default_nan_high LIT64( 0x7FFF7FFFFFFFFFFF ) +#define float128_default_nan_low LIT64( 0xFFFFFFFFFFFFFFFF ) +#else +#define float128_default_nan_high LIT64( 0xFFFF800000000000 ) +#define float128_default_nan_low LIT64( 0x0000000000000000 ) +#endif + +const float128 float128_default_nan = make_float128(float128_default_nan_high, + float128_default_nan_low); + /*---------------------------------------------------------------------------- | Raises the exceptions specified by `flags'. Floating-point traps can be | defined here if desired. It is currently not possible for such a trap diff --git a/fpu/softfloat.h b/fpu/softfloat.h index bde250087b..3bb7d8fa6d 100644 --- a/fpu/softfloat.h +++ b/fpu/softfloat.h @@ -43,7 +43,7 @@ these four paragraphs for those parts of this code that are retained. #endif #include -#include "config.h" +#include "config-host.h" /*---------------------------------------------------------------------------- | Each of the following `typedef's defines the most convenient type that holds @@ -68,12 +68,6 @@ typedef int64_t int64; #define LIT64( a ) a##LL #define INLINE static inline -#if defined(TARGET_MIPS) || defined(TARGET_SH4) || defined(TARGET_UNICORE32) -#define SNAN_BIT_IS_ONE 1 -#else -#define SNAN_BIT_IS_ONE 0 -#endif - #define STATUS_PARAM , float_status *status #define STATUS(field) status->field #define STATUS_VAR , status @@ -142,6 +136,7 @@ typedef struct { uint64_t low, high; #endif } float128; +#define make_float128(high_, low_) ((float128) { .high = high_, .low = low_ }) /*---------------------------------------------------------------------------- | Software IEC/IEEE floating-point underflow tininess-detection mode. @@ -248,13 +243,7 @@ float16 float16_maybe_silence_nan( float16 ); /*---------------------------------------------------------------------------- | The pattern for a default generated half-precision NaN. *----------------------------------------------------------------------------*/ -#if defined(TARGET_ARM) -#define float16_default_nan make_float16(0x7E00) -#elif SNAN_BIT_IS_ONE -#define float16_default_nan make_float16(0x7DFF) -#else -#define float16_default_nan make_float16(0xFE00) -#endif +extern const float16 float16_default_nan; /*---------------------------------------------------------------------------- | Software IEC/IEEE single-precision conversion routines. @@ -357,15 +346,7 @@ INLINE float32 float32_set_sign(float32 a, int sign) /*---------------------------------------------------------------------------- | The pattern for a default generated single-precision NaN. *----------------------------------------------------------------------------*/ -#if defined(TARGET_SPARC) -#define float32_default_nan make_float32(0x7FFFFFFF) -#elif defined(TARGET_PPC) || defined(TARGET_ARM) || defined(TARGET_ALPHA) -#define float32_default_nan make_float32(0x7FC00000) -#elif SNAN_BIT_IS_ONE -#define float32_default_nan make_float32(0x7FBFFFFF) -#else -#define float32_default_nan make_float32(0xFFC00000) -#endif +extern const float32 float32_default_nan; /*---------------------------------------------------------------------------- | Software IEC/IEEE double-precision conversion routines. @@ -470,15 +451,7 @@ INLINE float64 float64_set_sign(float64 a, int sign) /*---------------------------------------------------------------------------- | The pattern for a default generated double-precision NaN. *----------------------------------------------------------------------------*/ -#if defined(TARGET_SPARC) -#define float64_default_nan make_float64(LIT64( 0x7FFFFFFFFFFFFFFF )) -#elif defined(TARGET_PPC) || defined(TARGET_ARM) || defined(TARGET_ALPHA) -#define float64_default_nan make_float64(LIT64( 0x7FF8000000000000 )) -#elif SNAN_BIT_IS_ONE -#define float64_default_nan make_float64(LIT64( 0x7FF7FFFFFFFFFFFF )) -#else -#define float64_default_nan make_float64(LIT64( 0xFFF8000000000000 )) -#endif +extern const float64 float64_default_nan; /*---------------------------------------------------------------------------- | Software IEC/IEEE extended double-precision conversion routines. @@ -561,17 +534,9 @@ INLINE int floatx80_is_any_nan(floatx80 a) #define floatx80_infinity make_floatx80(0x7fff, 0x8000000000000000LL) /*---------------------------------------------------------------------------- -| The pattern for a default generated extended double-precision NaN. The -| `high' and `low' values hold the most- and least-significant bits, -| respectively. +| The pattern for a default generated extended double-precision NaN. *----------------------------------------------------------------------------*/ -#if SNAN_BIT_IS_ONE -#define floatx80_default_nan_high 0x7FFF -#define floatx80_default_nan_low LIT64( 0xBFFFFFFFFFFFFFFF ) -#else -#define floatx80_default_nan_high 0xFFFF -#define floatx80_default_nan_low LIT64( 0xC000000000000000 ) -#endif +extern const floatx80 floatx80_default_nan; /*---------------------------------------------------------------------------- | Software IEC/IEEE quadruple-precision conversion routines. @@ -648,15 +613,8 @@ INLINE int float128_is_any_nan(float128 a) } /*---------------------------------------------------------------------------- -| The pattern for a default generated quadruple-precision NaN. The `high' and -| `low' values hold the most- and least-significant bits, respectively. +| The pattern for a default generated quadruple-precision NaN. *----------------------------------------------------------------------------*/ -#if SNAN_BIT_IS_ONE -#define float128_default_nan_high LIT64( 0x7FFF7FFFFFFFFFFF ) -#define float128_default_nan_low LIT64( 0xFFFFFFFFFFFFFFFF ) -#else -#define float128_default_nan_high LIT64( 0xFFFF800000000000 ) -#define float128_default_nan_low LIT64( 0x0000000000000000 ) -#endif +extern const float128 float128_default_nan; #endif /* !SOFTFLOAT_H */ From cbbab9226da9572346837466a8770c117e7e65a2 Mon Sep 17 00:00:00 2001 From: Paolo Bonzini Date: Thu, 28 Jul 2011 12:10:30 +0200 Subject: [PATCH 108/230] move unaligned memory access functions to bswap.h This is just code movement, and moving the fpu/ include path from target-dependent to target-independent Make variables. Signed-off-by: Paolo Bonzini Signed-off-by: Anthony Liguori --- Makefile.hw | 2 +- bswap.h | 474 ++++++++++++++++++++++++++++++++++++++++++++++++++++ configure | 3 +- cpu-all.h | 446 +----------------------------------------------- 4 files changed, 477 insertions(+), 448 deletions(-) diff --git a/Makefile.hw b/Makefile.hw index b9181ab122..659e441992 100644 --- a/Makefile.hw +++ b/Makefile.hw @@ -9,7 +9,7 @@ include $(SRC_PATH)/rules.mak $(call set-vpath, $(SRC_PATH):$(SRC_PATH)/hw) -QEMU_CFLAGS+=-I.. -I$(SRC_PATH)/fpu +QEMU_CFLAGS+=-I.. include $(SRC_PATH)/Makefile.objs diff --git a/bswap.h b/bswap.h index 82a79517db..f41bebed83 100644 --- a/bswap.h +++ b/bswap.h @@ -11,6 +11,8 @@ #include #else +#include "softfloat.h" + #ifdef CONFIG_BYTESWAP_H #include #else @@ -237,4 +239,476 @@ static inline uint32_t qemu_bswap_len(uint32_t value, int len) return bswap32(value) >> (32 - 8 * len); } +typedef union { + float32 f; + uint32_t l; +} CPU_FloatU; + +typedef union { + float64 d; +#if defined(HOST_WORDS_BIGENDIAN) + struct { + uint32_t upper; + uint32_t lower; + } l; +#else + struct { + uint32_t lower; + uint32_t upper; + } l; +#endif + uint64_t ll; +} CPU_DoubleU; + +typedef union { + floatx80 d; + struct { + uint64_t lower; + uint16_t upper; + } l; +} CPU_LDoubleU; + +typedef union { + float128 q; +#if defined(HOST_WORDS_BIGENDIAN) + struct { + uint32_t upmost; + uint32_t upper; + uint32_t lower; + uint32_t lowest; + } l; + struct { + uint64_t upper; + uint64_t lower; + } ll; +#else + struct { + uint32_t lowest; + uint32_t lower; + uint32_t upper; + uint32_t upmost; + } l; + struct { + uint64_t lower; + uint64_t upper; + } ll; +#endif +} CPU_QuadU; + +/* unaligned/endian-independent pointer access */ + +/* + * the generic syntax is: + * + * load: ld{type}{sign}{size}{endian}_p(ptr) + * + * store: st{type}{size}{endian}_p(ptr, val) + * + * Note there are small differences with the softmmu access API! + * + * type is: + * (empty): integer access + * f : float access + * + * sign is: + * (empty): for floats or 32 bit size + * u : unsigned + * s : signed + * + * size is: + * b: 8 bits + * w: 16 bits + * l: 32 bits + * q: 64 bits + * + * endian is: + * (empty): 8 bit access + * be : big endian + * le : little endian + */ +static inline int ldub_p(const void *ptr) +{ + return *(uint8_t *)ptr; +} + +static inline int ldsb_p(const void *ptr) +{ + return *(int8_t *)ptr; +} + +static inline void stb_p(void *ptr, int v) +{ + *(uint8_t *)ptr = v; +} + +/* NOTE: on arm, putting 2 in /proc/sys/debug/alignment so that the + kernel handles unaligned load/stores may give better results, but + it is a system wide setting : bad */ +#if defined(HOST_WORDS_BIGENDIAN) || defined(WORDS_ALIGNED) + +/* conservative code for little endian unaligned accesses */ +static inline int lduw_le_p(const void *ptr) +{ +#ifdef _ARCH_PPC + int val; + __asm__ __volatile__ ("lhbrx %0,0,%1" : "=r" (val) : "r" (ptr)); + return val; +#else + const uint8_t *p = ptr; + return p[0] | (p[1] << 8); +#endif +} + +static inline int ldsw_le_p(const void *ptr) +{ +#ifdef _ARCH_PPC + int val; + __asm__ __volatile__ ("lhbrx %0,0,%1" : "=r" (val) : "r" (ptr)); + return (int16_t)val; +#else + const uint8_t *p = ptr; + return (int16_t)(p[0] | (p[1] << 8)); +#endif +} + +static inline int ldl_le_p(const void *ptr) +{ +#ifdef _ARCH_PPC + int val; + __asm__ __volatile__ ("lwbrx %0,0,%1" : "=r" (val) : "r" (ptr)); + return val; +#else + const uint8_t *p = ptr; + return p[0] | (p[1] << 8) | (p[2] << 16) | (p[3] << 24); +#endif +} + +static inline uint64_t ldq_le_p(const void *ptr) +{ + const uint8_t *p = ptr; + uint32_t v1, v2; + v1 = ldl_le_p(p); + v2 = ldl_le_p(p + 4); + return v1 | ((uint64_t)v2 << 32); +} + +static inline void stw_le_p(void *ptr, int v) +{ +#ifdef _ARCH_PPC + __asm__ __volatile__ ("sthbrx %1,0,%2" : "=m" (*(uint16_t *)ptr) : "r" (v), "r" (ptr)); +#else + uint8_t *p = ptr; + p[0] = v; + p[1] = v >> 8; +#endif +} + +static inline void stl_le_p(void *ptr, int v) +{ +#ifdef _ARCH_PPC + __asm__ __volatile__ ("stwbrx %1,0,%2" : "=m" (*(uint32_t *)ptr) : "r" (v), "r" (ptr)); +#else + uint8_t *p = ptr; + p[0] = v; + p[1] = v >> 8; + p[2] = v >> 16; + p[3] = v >> 24; +#endif +} + +static inline void stq_le_p(void *ptr, uint64_t v) +{ + uint8_t *p = ptr; + stl_le_p(p, (uint32_t)v); + stl_le_p(p + 4, v >> 32); +} + +/* float access */ + +static inline float32 ldfl_le_p(const void *ptr) +{ + union { + float32 f; + uint32_t i; + } u; + u.i = ldl_le_p(ptr); + return u.f; +} + +static inline void stfl_le_p(void *ptr, float32 v) +{ + union { + float32 f; + uint32_t i; + } u; + u.f = v; + stl_le_p(ptr, u.i); +} + +static inline float64 ldfq_le_p(const void *ptr) +{ + CPU_DoubleU u; + u.l.lower = ldl_le_p(ptr); + u.l.upper = ldl_le_p(ptr + 4); + return u.d; +} + +static inline void stfq_le_p(void *ptr, float64 v) +{ + CPU_DoubleU u; + u.d = v; + stl_le_p(ptr, u.l.lower); + stl_le_p(ptr + 4, u.l.upper); +} + +#else + +static inline int lduw_le_p(const void *ptr) +{ + return *(uint16_t *)ptr; +} + +static inline int ldsw_le_p(const void *ptr) +{ + return *(int16_t *)ptr; +} + +static inline int ldl_le_p(const void *ptr) +{ + return *(uint32_t *)ptr; +} + +static inline uint64_t ldq_le_p(const void *ptr) +{ + return *(uint64_t *)ptr; +} + +static inline void stw_le_p(void *ptr, int v) +{ + *(uint16_t *)ptr = v; +} + +static inline void stl_le_p(void *ptr, int v) +{ + *(uint32_t *)ptr = v; +} + +static inline void stq_le_p(void *ptr, uint64_t v) +{ + *(uint64_t *)ptr = v; +} + +/* float access */ + +static inline float32 ldfl_le_p(const void *ptr) +{ + return *(float32 *)ptr; +} + +static inline float64 ldfq_le_p(const void *ptr) +{ + return *(float64 *)ptr; +} + +static inline void stfl_le_p(void *ptr, float32 v) +{ + *(float32 *)ptr = v; +} + +static inline void stfq_le_p(void *ptr, float64 v) +{ + *(float64 *)ptr = v; +} +#endif + +#if !defined(HOST_WORDS_BIGENDIAN) || defined(WORDS_ALIGNED) + +static inline int lduw_be_p(const void *ptr) +{ +#if defined(__i386__) + int val; + asm volatile ("movzwl %1, %0\n" + "xchgb %b0, %h0\n" + : "=q" (val) + : "m" (*(uint16_t *)ptr)); + return val; +#else + const uint8_t *b = ptr; + return ((b[0] << 8) | b[1]); +#endif +} + +static inline int ldsw_be_p(const void *ptr) +{ +#if defined(__i386__) + int val; + asm volatile ("movzwl %1, %0\n" + "xchgb %b0, %h0\n" + : "=q" (val) + : "m" (*(uint16_t *)ptr)); + return (int16_t)val; +#else + const uint8_t *b = ptr; + return (int16_t)((b[0] << 8) | b[1]); +#endif +} + +static inline int ldl_be_p(const void *ptr) +{ +#if defined(__i386__) || defined(__x86_64__) + int val; + asm volatile ("movl %1, %0\n" + "bswap %0\n" + : "=r" (val) + : "m" (*(uint32_t *)ptr)); + return val; +#else + const uint8_t *b = ptr; + return (b[0] << 24) | (b[1] << 16) | (b[2] << 8) | b[3]; +#endif +} + +static inline uint64_t ldq_be_p(const void *ptr) +{ + uint32_t a,b; + a = ldl_be_p(ptr); + b = ldl_be_p((uint8_t *)ptr + 4); + return (((uint64_t)a<<32)|b); +} + +static inline void stw_be_p(void *ptr, int v) +{ +#if defined(__i386__) + asm volatile ("xchgb %b0, %h0\n" + "movw %w0, %1\n" + : "=q" (v) + : "m" (*(uint16_t *)ptr), "0" (v)); +#else + uint8_t *d = (uint8_t *) ptr; + d[0] = v >> 8; + d[1] = v; +#endif +} + +static inline void stl_be_p(void *ptr, int v) +{ +#if defined(__i386__) || defined(__x86_64__) + asm volatile ("bswap %0\n" + "movl %0, %1\n" + : "=r" (v) + : "m" (*(uint32_t *)ptr), "0" (v)); +#else + uint8_t *d = (uint8_t *) ptr; + d[0] = v >> 24; + d[1] = v >> 16; + d[2] = v >> 8; + d[3] = v; +#endif +} + +static inline void stq_be_p(void *ptr, uint64_t v) +{ + stl_be_p(ptr, v >> 32); + stl_be_p((uint8_t *)ptr + 4, v); +} + +/* float access */ + +static inline float32 ldfl_be_p(const void *ptr) +{ + union { + float32 f; + uint32_t i; + } u; + u.i = ldl_be_p(ptr); + return u.f; +} + +static inline void stfl_be_p(void *ptr, float32 v) +{ + union { + float32 f; + uint32_t i; + } u; + u.f = v; + stl_be_p(ptr, u.i); +} + +static inline float64 ldfq_be_p(const void *ptr) +{ + CPU_DoubleU u; + u.l.upper = ldl_be_p(ptr); + u.l.lower = ldl_be_p((uint8_t *)ptr + 4); + return u.d; +} + +static inline void stfq_be_p(void *ptr, float64 v) +{ + CPU_DoubleU u; + u.d = v; + stl_be_p(ptr, u.l.upper); + stl_be_p((uint8_t *)ptr + 4, u.l.lower); +} + +#else + +static inline int lduw_be_p(const void *ptr) +{ + return *(uint16_t *)ptr; +} + +static inline int ldsw_be_p(const void *ptr) +{ + return *(int16_t *)ptr; +} + +static inline int ldl_be_p(const void *ptr) +{ + return *(uint32_t *)ptr; +} + +static inline uint64_t ldq_be_p(const void *ptr) +{ + return *(uint64_t *)ptr; +} + +static inline void stw_be_p(void *ptr, int v) +{ + *(uint16_t *)ptr = v; +} + +static inline void stl_be_p(void *ptr, int v) +{ + *(uint32_t *)ptr = v; +} + +static inline void stq_be_p(void *ptr, uint64_t v) +{ + *(uint64_t *)ptr = v; +} + +/* float access */ + +static inline float32 ldfl_be_p(const void *ptr) +{ + return *(float32 *)ptr; +} + +static inline float64 ldfq_be_p(const void *ptr) +{ + return *(float64 *)ptr; +} + +static inline void stfl_be_p(void *ptr, float32 v) +{ + *(float32 *)ptr = v; +} + +static inline void stfq_be_p(void *ptr, float64 v) +{ + *(float64 *)ptr = v; +} + +#endif + #endif /* BSWAP_H */ diff --git a/configure b/configure index 38e3724f33..0e071323d3 100755 --- a/configure +++ b/configure @@ -233,7 +233,7 @@ QEMU_CFLAGS="-Wall -Wundef -Wwrite-strings -Wmissing-prototypes $QEMU_CFLAGS" QEMU_CFLAGS="-Wstrict-prototypes -Wredundant-decls $QEMU_CFLAGS" QEMU_CFLAGS="-D_GNU_SOURCE -D_FILE_OFFSET_BITS=64 -D_LARGEFILE_SOURCE $QEMU_CFLAGS" QEMU_CFLAGS="-D_FORTIFY_SOURCE=2 $QEMU_CFLAGS" -QEMU_INCLUDES="-I. -I\$(SRC_PATH)" +QEMU_INCLUDES="-I. -I\$(SRC_PATH) -I\$(SRC_PATH)/fpu" LDFLAGS="-g $LDFLAGS" # make source path absolute @@ -3374,7 +3374,6 @@ else includes="-I\$(SRC_PATH)/tcg/\$(ARCH) $includes" fi includes="-I\$(SRC_PATH)/tcg $includes" -includes="-I\$(SRC_PATH)/fpu $includes" if test "$target_user_only" = "yes" ; then libdis_config_mak=libdis-user/config.mak diff --git a/cpu-all.h b/cpu-all.h index e8391009a3..fa0205c28f 100644 --- a/cpu-all.h +++ b/cpu-all.h @@ -35,8 +35,6 @@ * TARGET_WORDS_BIGENDIAN : same for target cpu */ -#include "softfloat.h" - #if defined(HOST_WORDS_BIGENDIAN) != defined(TARGET_WORDS_BIGENDIAN) #define BSWAP_NEEDED #endif @@ -114,64 +112,6 @@ static inline void tswap64s(uint64_t *s) #define bswaptls(s) bswap64s(s) #endif -typedef union { - float32 f; - uint32_t l; -} CPU_FloatU; - -/* NOTE: arm FPA is horrible as double 32 bit words are stored in big - endian ! */ -typedef union { - float64 d; -#if defined(HOST_WORDS_BIGENDIAN) - struct { - uint32_t upper; - uint32_t lower; - } l; -#else - struct { - uint32_t lower; - uint32_t upper; - } l; -#endif - uint64_t ll; -} CPU_DoubleU; - -typedef union { - floatx80 d; - struct { - uint64_t lower; - uint16_t upper; - } l; -} CPU_LDoubleU; - -typedef union { - float128 q; -#if defined(HOST_WORDS_BIGENDIAN) - struct { - uint32_t upmost; - uint32_t upper; - uint32_t lower; - uint32_t lowest; - } l; - struct { - uint64_t upper; - uint64_t lower; - } ll; -#else - struct { - uint32_t lowest; - uint32_t lower; - uint32_t upper; - uint32_t upmost; - } l; - struct { - uint64_t lower; - uint64_t upper; - } ll; -#endif -} CPU_QuadU; - /* CPU memory access without any memory or io remapping */ /* @@ -207,392 +147,8 @@ typedef union { * user : user mode access using soft MMU * kernel : kernel mode access using soft MMU */ -static inline int ldub_p(const void *ptr) -{ - return *(uint8_t *)ptr; -} -static inline int ldsb_p(const void *ptr) -{ - return *(int8_t *)ptr; -} - -static inline void stb_p(void *ptr, int v) -{ - *(uint8_t *)ptr = v; -} - -/* NOTE: on arm, putting 2 in /proc/sys/debug/alignment so that the - kernel handles unaligned load/stores may give better results, but - it is a system wide setting : bad */ -#if defined(HOST_WORDS_BIGENDIAN) || defined(WORDS_ALIGNED) - -/* conservative code for little endian unaligned accesses */ -static inline int lduw_le_p(const void *ptr) -{ -#ifdef _ARCH_PPC - int val; - __asm__ __volatile__ ("lhbrx %0,0,%1" : "=r" (val) : "r" (ptr)); - return val; -#else - const uint8_t *p = ptr; - return p[0] | (p[1] << 8); -#endif -} - -static inline int ldsw_le_p(const void *ptr) -{ -#ifdef _ARCH_PPC - int val; - __asm__ __volatile__ ("lhbrx %0,0,%1" : "=r" (val) : "r" (ptr)); - return (int16_t)val; -#else - const uint8_t *p = ptr; - return (int16_t)(p[0] | (p[1] << 8)); -#endif -} - -static inline int ldl_le_p(const void *ptr) -{ -#ifdef _ARCH_PPC - int val; - __asm__ __volatile__ ("lwbrx %0,0,%1" : "=r" (val) : "r" (ptr)); - return val; -#else - const uint8_t *p = ptr; - return p[0] | (p[1] << 8) | (p[2] << 16) | (p[3] << 24); -#endif -} - -static inline uint64_t ldq_le_p(const void *ptr) -{ - const uint8_t *p = ptr; - uint32_t v1, v2; - v1 = ldl_le_p(p); - v2 = ldl_le_p(p + 4); - return v1 | ((uint64_t)v2 << 32); -} - -static inline void stw_le_p(void *ptr, int v) -{ -#ifdef _ARCH_PPC - __asm__ __volatile__ ("sthbrx %1,0,%2" : "=m" (*(uint16_t *)ptr) : "r" (v), "r" (ptr)); -#else - uint8_t *p = ptr; - p[0] = v; - p[1] = v >> 8; -#endif -} - -static inline void stl_le_p(void *ptr, int v) -{ -#ifdef _ARCH_PPC - __asm__ __volatile__ ("stwbrx %1,0,%2" : "=m" (*(uint32_t *)ptr) : "r" (v), "r" (ptr)); -#else - uint8_t *p = ptr; - p[0] = v; - p[1] = v >> 8; - p[2] = v >> 16; - p[3] = v >> 24; -#endif -} - -static inline void stq_le_p(void *ptr, uint64_t v) -{ - uint8_t *p = ptr; - stl_le_p(p, (uint32_t)v); - stl_le_p(p + 4, v >> 32); -} - -/* float access */ - -static inline float32 ldfl_le_p(const void *ptr) -{ - union { - float32 f; - uint32_t i; - } u; - u.i = ldl_le_p(ptr); - return u.f; -} - -static inline void stfl_le_p(void *ptr, float32 v) -{ - union { - float32 f; - uint32_t i; - } u; - u.f = v; - stl_le_p(ptr, u.i); -} - -static inline float64 ldfq_le_p(const void *ptr) -{ - CPU_DoubleU u; - u.l.lower = ldl_le_p(ptr); - u.l.upper = ldl_le_p(ptr + 4); - return u.d; -} - -static inline void stfq_le_p(void *ptr, float64 v) -{ - CPU_DoubleU u; - u.d = v; - stl_le_p(ptr, u.l.lower); - stl_le_p(ptr + 4, u.l.upper); -} - -#else - -static inline int lduw_le_p(const void *ptr) -{ - return *(uint16_t *)ptr; -} - -static inline int ldsw_le_p(const void *ptr) -{ - return *(int16_t *)ptr; -} - -static inline int ldl_le_p(const void *ptr) -{ - return *(uint32_t *)ptr; -} - -static inline uint64_t ldq_le_p(const void *ptr) -{ - return *(uint64_t *)ptr; -} - -static inline void stw_le_p(void *ptr, int v) -{ - *(uint16_t *)ptr = v; -} - -static inline void stl_le_p(void *ptr, int v) -{ - *(uint32_t *)ptr = v; -} - -static inline void stq_le_p(void *ptr, uint64_t v) -{ - *(uint64_t *)ptr = v; -} - -/* float access */ - -static inline float32 ldfl_le_p(const void *ptr) -{ - return *(float32 *)ptr; -} - -static inline float64 ldfq_le_p(const void *ptr) -{ - return *(float64 *)ptr; -} - -static inline void stfl_le_p(void *ptr, float32 v) -{ - *(float32 *)ptr = v; -} - -static inline void stfq_le_p(void *ptr, float64 v) -{ - *(float64 *)ptr = v; -} -#endif - -#if !defined(HOST_WORDS_BIGENDIAN) || defined(WORDS_ALIGNED) - -static inline int lduw_be_p(const void *ptr) -{ -#if defined(__i386__) - int val; - asm volatile ("movzwl %1, %0\n" - "xchgb %b0, %h0\n" - : "=q" (val) - : "m" (*(uint16_t *)ptr)); - return val; -#else - const uint8_t *b = ptr; - return ((b[0] << 8) | b[1]); -#endif -} - -static inline int ldsw_be_p(const void *ptr) -{ -#if defined(__i386__) - int val; - asm volatile ("movzwl %1, %0\n" - "xchgb %b0, %h0\n" - : "=q" (val) - : "m" (*(uint16_t *)ptr)); - return (int16_t)val; -#else - const uint8_t *b = ptr; - return (int16_t)((b[0] << 8) | b[1]); -#endif -} - -static inline int ldl_be_p(const void *ptr) -{ -#if defined(__i386__) || defined(__x86_64__) - int val; - asm volatile ("movl %1, %0\n" - "bswap %0\n" - : "=r" (val) - : "m" (*(uint32_t *)ptr)); - return val; -#else - const uint8_t *b = ptr; - return (b[0] << 24) | (b[1] << 16) | (b[2] << 8) | b[3]; -#endif -} - -static inline uint64_t ldq_be_p(const void *ptr) -{ - uint32_t a,b; - a = ldl_be_p(ptr); - b = ldl_be_p((uint8_t *)ptr + 4); - return (((uint64_t)a<<32)|b); -} - -static inline void stw_be_p(void *ptr, int v) -{ -#if defined(__i386__) - asm volatile ("xchgb %b0, %h0\n" - "movw %w0, %1\n" - : "=q" (v) - : "m" (*(uint16_t *)ptr), "0" (v)); -#else - uint8_t *d = (uint8_t *) ptr; - d[0] = v >> 8; - d[1] = v; -#endif -} - -static inline void stl_be_p(void *ptr, int v) -{ -#if defined(__i386__) || defined(__x86_64__) - asm volatile ("bswap %0\n" - "movl %0, %1\n" - : "=r" (v) - : "m" (*(uint32_t *)ptr), "0" (v)); -#else - uint8_t *d = (uint8_t *) ptr; - d[0] = v >> 24; - d[1] = v >> 16; - d[2] = v >> 8; - d[3] = v; -#endif -} - -static inline void stq_be_p(void *ptr, uint64_t v) -{ - stl_be_p(ptr, v >> 32); - stl_be_p((uint8_t *)ptr + 4, v); -} - -/* float access */ - -static inline float32 ldfl_be_p(const void *ptr) -{ - union { - float32 f; - uint32_t i; - } u; - u.i = ldl_be_p(ptr); - return u.f; -} - -static inline void stfl_be_p(void *ptr, float32 v) -{ - union { - float32 f; - uint32_t i; - } u; - u.f = v; - stl_be_p(ptr, u.i); -} - -static inline float64 ldfq_be_p(const void *ptr) -{ - CPU_DoubleU u; - u.l.upper = ldl_be_p(ptr); - u.l.lower = ldl_be_p((uint8_t *)ptr + 4); - return u.d; -} - -static inline void stfq_be_p(void *ptr, float64 v) -{ - CPU_DoubleU u; - u.d = v; - stl_be_p(ptr, u.l.upper); - stl_be_p((uint8_t *)ptr + 4, u.l.lower); -} - -#else - -static inline int lduw_be_p(const void *ptr) -{ - return *(uint16_t *)ptr; -} - -static inline int ldsw_be_p(const void *ptr) -{ - return *(int16_t *)ptr; -} - -static inline int ldl_be_p(const void *ptr) -{ - return *(uint32_t *)ptr; -} - -static inline uint64_t ldq_be_p(const void *ptr) -{ - return *(uint64_t *)ptr; -} - -static inline void stw_be_p(void *ptr, int v) -{ - *(uint16_t *)ptr = v; -} - -static inline void stl_be_p(void *ptr, int v) -{ - *(uint32_t *)ptr = v; -} - -static inline void stq_be_p(void *ptr, uint64_t v) -{ - *(uint64_t *)ptr = v; -} - -/* float access */ - -static inline float32 ldfl_be_p(const void *ptr) -{ - return *(float32 *)ptr; -} - -static inline float64 ldfq_be_p(const void *ptr) -{ - return *(float64 *)ptr; -} - -static inline void stfl_be_p(void *ptr, float32 v) -{ - *(float32 *)ptr = v; -} - -static inline void stfq_be_p(void *ptr, float64 v) -{ - *(float64 *)ptr = v; -} - -#endif - -/* target CPU memory access functions */ +/* target-endianness CPU memory access functions */ #if defined(TARGET_WORDS_BIGENDIAN) #define lduw_p(p) lduw_be_p(p) #define ldsw_p(p) ldsw_be_p(p) From 33fa8234c3d642317583c992b7fdc67ce7fdd1b5 Mon Sep 17 00:00:00 2001 From: "Dr. David Alan Gilbert" Date: Mon, 25 Jul 2011 13:21:30 +0100 Subject: [PATCH 109/230] Fix last sector write on sd card When writing the last sector of an SD card using WRITE_MULTIPLE_BLOCK QEmu throws an error saying that we've run off the end, and leaves itself in the wrong state. Tested on ARM Vexpress model. Signed-off-by: Dr. David Alan Gilbert Signed-off-by: Anthony Liguori --- hw/sd.c | 19 +++++++++++-------- 1 file changed, 11 insertions(+), 8 deletions(-) diff --git a/hw/sd.c b/hw/sd.c index cedfb20249..219a0dd296 100644 --- a/hw/sd.c +++ b/hw/sd.c @@ -1450,14 +1450,8 @@ void sd_write_data(SDState *sd, uint8_t value) break; case 25: /* CMD25: WRITE_MULTIPLE_BLOCK */ - sd->data[sd->data_offset ++] = value; - if (sd->data_offset >= sd->blk_len) { - /* TODO: Check CRC before committing */ - sd->state = sd_programming_state; - BLK_WRITE_BLOCK(sd->data_start, sd->data_offset); - sd->blk_written ++; - sd->data_start += sd->blk_len; - sd->data_offset = 0; + if (sd->data_offset == 0) { + /* Start of the block - lets check the address is valid */ if (sd->data_start + sd->blk_len > sd->size) { sd->card_status |= ADDRESS_ERROR; break; @@ -1466,6 +1460,15 @@ void sd_write_data(SDState *sd, uint8_t value) sd->card_status |= WP_VIOLATION; break; } + } + sd->data[sd->data_offset++] = value; + if (sd->data_offset >= sd->blk_len) { + /* TODO: Check CRC before committing */ + sd->state = sd_programming_state; + BLK_WRITE_BLOCK(sd->data_start, sd->data_offset); + sd->blk_written++; + sd->data_start += sd->blk_len; + sd->data_offset = 0; sd->csd[14] |= 0x40; /* Bzzzzzzztt .... Operation complete. */ From c7f4111a06208b46c6d05934d2a1e5cfbebc0180 Mon Sep 17 00:00:00 2001 From: Jes Sorensen Date: Mon, 25 Jul 2011 17:13:36 +0200 Subject: [PATCH 110/230] Add missing trace call to oslib-posix.c:qemu_vmalloc() Acked-by: Stefan Hajnoczi Signed-off-by: Jes Sorensen Signed-off-by: Anthony Liguori --- oslib-posix.c | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/oslib-posix.c b/oslib-posix.c index 3a18e865f3..196099cc77 100644 --- a/oslib-posix.c +++ b/oslib-posix.c @@ -79,7 +79,10 @@ void *qemu_memalign(size_t alignment, size_t size) /* alloc shared memory pages */ void *qemu_vmalloc(size_t size) { - return qemu_memalign(getpagesize(), size); + void *ptr; + ptr = qemu_memalign(getpagesize(), size); + trace_qemu_vmalloc(size, ptr); + return ptr; } void qemu_vfree(void *ptr) From 1ece9905747fb42ab6b2797c8ddde56496f14796 Mon Sep 17 00:00:00 2001 From: Alon Levy Date: Tue, 26 Jul 2011 12:30:40 +0300 Subject: [PATCH 111/230] configure: add --disable-zlib-test This is required for building libcacard which doesn't itself require zlib without bringing in this requirement to the build environment. Signed-off-by: Alon Levy Signed-off-by: Anthony Liguori --- configure | 23 ++++++++++++++--------- 1 file changed, 14 insertions(+), 9 deletions(-) diff --git a/configure b/configure index 0e071323d3..e80a1e9763 100755 --- a/configure +++ b/configure @@ -179,6 +179,7 @@ smartcard="" smartcard_nss="" usb_redir="" opengl="" +zlib="yes" # parse CC options first for opt do @@ -751,6 +752,8 @@ for opt do ;; --enable-usb-redir) usb_redir="yes" ;; + --disable-zlib-test) zlib="no" + ;; *) echo "ERROR: unknown option $opt"; show_help="yes" ;; esac @@ -1190,18 +1193,20 @@ fi ########################################## # zlib check -cat > $TMPC << EOF +if test "$zlib" != "no" ; then + cat > $TMPC << EOF #include int main(void) { zlibVersion(); return 0; } EOF -if compile_prog "" "-lz" ; then - : -else - echo - echo "Error: zlib check failed" - echo "Make sure to have the zlib libs and headers installed." - echo - exit 1 + if compile_prog "" "-lz" ; then + : + else + echo + echo "Error: zlib check failed" + echo "Make sure to have the zlib libs and headers installed." + echo + exit 1 + fi fi ########################################## From 6b8273a1b97876950d91c228a420a851e10e12bb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?G=C3=B6ran=20Weinholt?= Date: Sun, 24 Jul 2011 17:55:58 +0200 Subject: [PATCH 112/230] multiboot: Fix bss segment support MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Multiboot images can specify a bss segment. The boot loader must clear the memory of the bss and ensure that no modules or structures are allocated inside it. Several fields are provided in the Multiboot header that were previously not used properly. The header is now used to determine how much data should be read from the image and how much memory should be reserved to the bss segment. Signed-off-by: Göran Weinholt Signed-off-by: Anthony Liguori --- hw/multiboot.c | 14 +++++++++----- 1 file changed, 9 insertions(+), 5 deletions(-) diff --git a/hw/multiboot.c b/hw/multiboot.c index 2426e84833..a1d3f41293 100644 --- a/hw/multiboot.c +++ b/hw/multiboot.c @@ -198,11 +198,14 @@ int load_multiboot(void *fw_cfg, } else { /* Valid if mh_flags sets MULTIBOOT_HEADER_HAS_ADDR. */ uint32_t mh_header_addr = ldl_p(header+i+12); + uint32_t mh_load_end_addr = ldl_p(header+i+20); + uint32_t mh_bss_end_addr = ldl_p(header+i+24); mh_load_addr = ldl_p(header+i+16); uint32_t mb_kernel_text_offset = i - (mh_header_addr - mh_load_addr); + uint32_t mb_load_size = mh_load_end_addr - mh_load_addr; mh_entry_addr = ldl_p(header+i+28); - mb_kernel_size = kernel_file_size - mb_kernel_text_offset; + mb_kernel_size = mh_bss_end_addr - mh_load_addr; /* Valid if mh_flags sets MULTIBOOT_HEADER_HAS_VBE. uint32_t mh_mode_type = ldl_p(header+i+32); @@ -212,17 +215,18 @@ int load_multiboot(void *fw_cfg, mb_debug("multiboot: mh_header_addr = %#x\n", mh_header_addr); mb_debug("multiboot: mh_load_addr = %#x\n", mh_load_addr); - mb_debug("multiboot: mh_load_end_addr = %#x\n", ldl_p(header+i+20)); - mb_debug("multiboot: mh_bss_end_addr = %#x\n", ldl_p(header+i+24)); + mb_debug("multiboot: mh_load_end_addr = %#x\n", mh_load_end_addr); + mb_debug("multiboot: mh_bss_end_addr = %#x\n", mh_bss_end_addr); mb_debug("qemu: loading multiboot kernel (%#x bytes) at %#x\n", - mb_kernel_size, mh_load_addr); + mb_load_size, mh_load_addr); mbs.mb_buf = qemu_malloc(mb_kernel_size); fseek(f, mb_kernel_text_offset, SEEK_SET); - if (fread(mbs.mb_buf, 1, mb_kernel_size, f) != mb_kernel_size) { + if (fread(mbs.mb_buf, 1, mb_load_size, f) != mb_load_size) { fprintf(stderr, "fread() failed\n"); exit(1); } + memset(mbs.mb_buf + mb_load_size, 0, mb_kernel_size - mb_load_size); fclose(f); } From ecf169b7fa920b87e380981c5206148d057d85fb Mon Sep 17 00:00:00 2001 From: Stefan Berger Date: Tue, 26 Jul 2011 10:33:11 -0400 Subject: [PATCH 113/230] Fix a compilation error in xen-mapcache.c MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This patch fixes a compilation error in xen-mapcache.c . /home/stefanb/qemu/qemu-git/xen-mapcache.c: In function ‘xen_ram_addr_from_mapcache’: /home/stefanb/qemu/qemu-git/xen-mapcache.c:240:42: error: variable ‘pentry’ set but not used [-Werror=unused-but-set-variable] cc1: all warnings being treated as errors Signed-off-by: Stefan Berger Signed-off-by: Anthony Liguori --- xen-mapcache.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/xen-mapcache.c b/xen-mapcache.c index 007136af26..15d12413d4 100644 --- a/xen-mapcache.c +++ b/xen-mapcache.c @@ -237,7 +237,7 @@ uint8_t *xen_map_cache(target_phys_addr_t phys_addr, target_phys_addr_t size, ram_addr_t xen_ram_addr_from_mapcache(void *ptr) { - MapCacheEntry *entry = NULL, *pentry = NULL; + MapCacheEntry *entry = NULL; MapCacheRev *reventry; target_phys_addr_t paddr_index; target_phys_addr_t size; @@ -263,7 +263,6 @@ ram_addr_t xen_ram_addr_from_mapcache(void *ptr) entry = &mapcache->entry[paddr_index % mapcache->nr_buckets]; while (entry && (entry->paddr_index != paddr_index || entry->size != size)) { - pentry = entry; entry = entry->next; } if (!entry) { From 5f070c5fb768cc587b1a75379b7b17c3f841fd40 Mon Sep 17 00:00:00 2001 From: Avi Kivity Date: Mon, 25 Jul 2011 18:55:53 +0300 Subject: [PATCH 114/230] CODING_STYLE: explicitly allow braceless 'else if' It's already allowed by the example; there are about 1800 instances in the tree; and disallowing it would lead to if (a) { ... } else { if (b) { ... } else { if (c) { ... } else { if (d) { ... } else { ... } } } } instead of if (a) { ... } else if (b) { ... } else if (c) { ... } else if (d) { ... } else { ... } which is more readable. Acked-by: Blue Swirl Signed-off-by: Avi Kivity Signed-off-by: Anthony Liguori --- CODING_STYLE | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/CODING_STYLE b/CODING_STYLE index 5ecfa22161..6e61c49089 100644 --- a/CODING_STYLE +++ b/CODING_STYLE @@ -68,6 +68,10 @@ keyword. Example: printf("a was something else entirely.\n"); } +Note that 'else if' is considered a single statement; otherwise a long if/ +else if/else if/.../else sequence would need an indent for every else +statement. + An exception is the opening brace for a function; for reasons of tradition and clarity it comes on a line by itself: From 2645c6dcaf6ea2a51a3b6dfa407dd203004e4d11 Mon Sep 17 00:00:00 2001 From: Jan Kiszka Date: Mon, 25 Jul 2011 18:11:20 +0200 Subject: [PATCH 115/230] Allow to leave type on default in -machine This allows to specify -machine options without setting an explicit machine type. We will pick the default machine in this case. Requesting the list of available machines is still possible via '-machine ?' e.g. Signed-off-by: Jan Kiszka Signed-off-by: Anthony Liguori --- vl.c | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/vl.c b/vl.c index a3d8d77bf8..73316cf17e 100644 --- a/vl.c +++ b/vl.c @@ -2724,7 +2724,10 @@ int main(int argc, char **argv, char **envp) fprintf(stderr, "parse error: %s\n", optarg); exit(1); } - machine = machine_parse(qemu_opt_get(opts, "type")); + optarg = qemu_opt_get(opts, "type"); + if (optarg) { + machine = machine_parse(optarg); + } break; case QEMU_OPTION_usb: usb_enabled = 1; From c62f6d1d76aea587556c85b6b7b5c44167006264 Mon Sep 17 00:00:00 2001 From: TeLeMan Date: Mon, 25 Jul 2011 16:29:14 +0800 Subject: [PATCH 116/230] monitor: fix build breakage with --disable-vnc The breakage was introduced by the commit 13661089810d3e59931f3e80d7cb541b99af7071 Signed-off-by: TeLeMan Signed-off-by: Anthony Liguori --- monitor.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/monitor.c b/monitor.c index 718935b881..1b8ba2c1fa 100644 --- a/monitor.c +++ b/monitor.c @@ -1200,10 +1200,12 @@ static int add_graphics_client(Monitor *mon, const QDict *qdict, QObject **ret_d } qerror_report(QERR_ADD_CLIENT_FAILED); return -1; +#ifdef CONFIG_VNC } else if (strcmp(protocol, "vnc") == 0) { int fd = monitor_get_fd(mon, fdname); vnc_display_add_client(NULL, fd, skipauth); return 0; +#endif } else if ((s = qemu_chr_find(protocol)) != NULL) { int fd = monitor_get_fd(mon, fdname); if (qemu_chr_add_client(s, fd) < 0) { From f9049203d33847562de155a7c5bc75fe7a3e77f6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Juha=20Riihim=C3=A4ki?= Date: Fri, 29 Jul 2011 16:35:14 +0100 Subject: [PATCH 117/230] hw/omap_l4.c: Add helper function omap_l4_region_base MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Add helper function omap_l4_region_base() to return the base address of a particular region of an L4 target agent. Signed-off-by: Juha Riihimäki [Riku Voipio: Fixes and restructuring patchset] Signed-off-by: Riku Voipio [Peter Maydell: More fixes and cleanups for upstream submission] Signed-off-by: Peter Maydell Signed-off-by: Andrzej Zaborowski --- hw/omap.h | 2 ++ hw/omap_l4.c | 6 ++++++ 2 files changed, 8 insertions(+) diff --git a/hw/omap.h b/hw/omap.h index c227a82b2c..00a0ea9042 100644 --- a/hw/omap.h +++ b/hw/omap.h @@ -93,6 +93,8 @@ struct omap_target_agent_s *omap_l4ta_get( int cs); target_phys_addr_t omap_l4_attach(struct omap_target_agent_s *ta, int region, int iotype); +target_phys_addr_t omap_l4_region_base(struct omap_target_agent_s *ta, + int region); int l4_register_io_memory(CPUReadMemoryFunc * const *mem_read, CPUWriteMemoryFunc * const *mem_write, void *opaque); diff --git a/hw/omap_l4.c b/hw/omap_l4.c index 4af0ca8ea6..59c84b19a2 100644 --- a/hw/omap_l4.c +++ b/hw/omap_l4.c @@ -146,6 +146,12 @@ struct omap_l4_s *omap_l4_init(target_phys_addr_t base, int ta_num) return bus; } +target_phys_addr_t omap_l4_region_base(struct omap_target_agent_s *ta, + int region) +{ + return ta->bus->base + ta->start[region].offset; +} + static uint32_t omap_l4ta_read(void *opaque, target_phys_addr_t addr) { struct omap_target_agent_s *s = (struct omap_target_agent_s *) opaque; From 0a34f96690bcb56bd6bc55566c2773e77c67650c Mon Sep 17 00:00:00 2001 From: Peter Maydell Date: Fri, 29 Jul 2011 16:35:16 +0100 Subject: [PATCH 118/230] hw/omap_clk: Add the clock for the OMAP2430-specific fifth GPIO module The OMAP2430 has a fifth GPIO module which earlier OMAP2 models lack; add the clock definition for it. Signed-off-by: Peter Maydell Signed-off-by: Andrzej Zaborowski --- hw/omap_clk.c | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/hw/omap_clk.c b/hw/omap_clk.c index 6bcabef8ac..577b326ae9 100644 --- a/hw/omap_clk.c +++ b/hw/omap_clk.c @@ -836,7 +836,7 @@ static struct clk i2c2_iclk = { .parent = &core_l4_iclk, }; -static struct clk gpio_dbclk[4] = { +static struct clk gpio_dbclk[5] = { { .name = "gpio1_dbclk", .flags = CLOCK_IN_OMAP242X | CLOCK_IN_OMAP243X, @@ -853,6 +853,10 @@ static struct clk gpio_dbclk[4] = { .name = "gpio4_dbclk", .flags = CLOCK_IN_OMAP242X | CLOCK_IN_OMAP243X, .parent = &wu_32k_clk, + }, { + .name = "gpio5_dbclk", + .flags = CLOCK_IN_OMAP243X, + .parent = &wu_32k_clk, }, }; From 77831c204fda6303408aee1853c36768d853b413 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Juha=20Riihim=C3=A4ki?= Date: Fri, 29 Jul 2011 16:35:17 +0100 Subject: [PATCH 119/230] hw/omap_gpio.c: Convert to qdev MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Convert the OMAP GPIO module to qdev. Signed-off-by: Juha Riihimäki [Riku Voipio: Fixes and restructuring patchset] Signed-off-by: Riku Voipio [Peter Maydell: More fixes and cleanups for upstream submission] Signed-off-by: Peter Maydell Signed-off-by: Andrzej Zaborowski --- hw/nseries.c | 43 ++++---- hw/omap.h | 20 +--- hw/omap1.c | 10 +- hw/omap2.c | 34 +++++-- hw/omap_gpio.c | 260 +++++++++++++++++++++++++++++-------------------- hw/palm.c | 26 ++--- 6 files changed, 219 insertions(+), 174 deletions(-) diff --git a/hw/nseries.c b/hw/nseries.c index 2f84f5305b..d9a542862d 100644 --- a/hw/nseries.c +++ b/hw/nseries.c @@ -134,9 +134,9 @@ static void n800_mmc_cs_cb(void *opaque, int line, int level) static void n8x0_gpio_setup(struct n800_s *s) { qemu_irq *mmc_cs = qemu_allocate_irqs(n800_mmc_cs_cb, s->cpu->mmc, 1); - omap2_gpio_out_set(s->cpu->gpif, N8X0_MMC_CS_GPIO, mmc_cs[0]); + qdev_connect_gpio_out(s->cpu->gpio, N8X0_MMC_CS_GPIO, mmc_cs[0]); - qemu_irq_lower(omap2_gpio_in_get(s->cpu->gpif, N800_BAT_COVER_GPIO)[0]); + qemu_irq_lower(qdev_get_gpio_in(s->cpu->gpio, N800_BAT_COVER_GPIO)); } #define MAEMO_CAL_HEADER(...) \ @@ -168,8 +168,8 @@ static void n8x0_nand_setup(struct n800_s *s) omap_gpmc_attach(s->cpu->gpmc, N8X0_ONENAND_CS, 0, onenand_base_update, onenand_base_unmap, (s->nand = onenand_init(0xec4800, 1, - omap2_gpio_in_get(s->cpu->gpif, - N8X0_ONENAND_GPIO)[0]))); + qdev_get_gpio_in(s->cpu->gpio, + N8X0_ONENAND_GPIO)))); otp_region = onenand_raw_otp(s->nand); memcpy(otp_region + 0x000, n8x0_cal_wlan_mac, sizeof(n8x0_cal_wlan_mac)); @@ -180,7 +180,7 @@ static void n8x0_nand_setup(struct n800_s *s) static void n8x0_i2c_setup(struct n800_s *s) { DeviceState *dev; - qemu_irq tmp_irq = omap2_gpio_in_get(s->cpu->gpif, N8X0_TMP105_GPIO)[0]; + qemu_irq tmp_irq = qdev_get_gpio_in(s->cpu->gpio, N8X0_TMP105_GPIO); /* Attach the CPU on one end of our I2C bus. */ s->i2c = omap_i2c_bus(s->cpu->i2c[0]); @@ -249,8 +249,8 @@ static void n800_tsc_kbd_setup(struct n800_s *s) /* XXX: are the three pins inverted inside the chip between the * tsc and the cpu (N4111)? */ qemu_irq penirq = NULL; /* NC */ - qemu_irq kbirq = omap2_gpio_in_get(s->cpu->gpif, N800_TSC_KP_IRQ_GPIO)[0]; - qemu_irq dav = omap2_gpio_in_get(s->cpu->gpif, N800_TSC_TS_GPIO)[0]; + qemu_irq kbirq = qdev_get_gpio_in(s->cpu->gpio, N800_TSC_KP_IRQ_GPIO); + qemu_irq dav = qdev_get_gpio_in(s->cpu->gpio, N800_TSC_TS_GPIO); s->ts.chip = tsc2301_init(penirq, kbirq, dav); s->ts.opaque = s->ts.chip->opaque; @@ -269,7 +269,7 @@ static void n800_tsc_kbd_setup(struct n800_s *s) static void n810_tsc_setup(struct n800_s *s) { - qemu_irq pintdav = omap2_gpio_in_get(s->cpu->gpif, N810_TSC_TS_GPIO)[0]; + qemu_irq pintdav = qdev_get_gpio_in(s->cpu->gpio, N810_TSC_TS_GPIO); s->ts.opaque = tsc2005_init(pintdav); s->ts.txrx = tsc2005_txrx; @@ -361,7 +361,7 @@ static int n810_keys[0x80] = { static void n810_kbd_setup(struct n800_s *s) { - qemu_irq kbd_irq = omap2_gpio_in_get(s->cpu->gpif, N810_KEYBOARD_GPIO)[0]; + qemu_irq kbd_irq = qdev_get_gpio_in(s->cpu->gpio, N810_KEYBOARD_GPIO); DeviceState *dev; int i; @@ -726,15 +726,15 @@ static void n8x0_dss_setup(struct n800_s *s) static void n8x0_cbus_setup(struct n800_s *s) { - qemu_irq dat_out = omap2_gpio_in_get(s->cpu->gpif, N8X0_CBUS_DAT_GPIO)[0]; - qemu_irq retu_irq = omap2_gpio_in_get(s->cpu->gpif, N8X0_RETU_GPIO)[0]; - qemu_irq tahvo_irq = omap2_gpio_in_get(s->cpu->gpif, N8X0_TAHVO_GPIO)[0]; + qemu_irq dat_out = qdev_get_gpio_in(s->cpu->gpio, N8X0_CBUS_DAT_GPIO); + qemu_irq retu_irq = qdev_get_gpio_in(s->cpu->gpio, N8X0_RETU_GPIO); + qemu_irq tahvo_irq = qdev_get_gpio_in(s->cpu->gpio, N8X0_TAHVO_GPIO); CBus *cbus = cbus_init(dat_out); - omap2_gpio_out_set(s->cpu->gpif, N8X0_CBUS_CLK_GPIO, cbus->clk); - omap2_gpio_out_set(s->cpu->gpif, N8X0_CBUS_DAT_GPIO, cbus->dat); - omap2_gpio_out_set(s->cpu->gpif, N8X0_CBUS_SEL_GPIO, cbus->sel); + qdev_connect_gpio_out(s->cpu->gpio, N8X0_CBUS_CLK_GPIO, cbus->clk); + qdev_connect_gpio_out(s->cpu->gpio, N8X0_CBUS_DAT_GPIO, cbus->dat); + qdev_connect_gpio_out(s->cpu->gpio, N8X0_CBUS_SEL_GPIO, cbus->sel); cbus_attach(cbus, s->retu = retu_init(retu_irq, 1)); cbus_attach(cbus, s->tahvo = tahvo_init(tahvo_irq, 1)); @@ -743,12 +743,11 @@ static void n8x0_cbus_setup(struct n800_s *s) static void n8x0_uart_setup(struct n800_s *s) { CharDriverState *radio = uart_hci_init( - omap2_gpio_in_get(s->cpu->gpif, - N8X0_BT_HOST_WKUP_GPIO)[0]); + qdev_get_gpio_in(s->cpu->gpio, N8X0_BT_HOST_WKUP_GPIO)); - omap2_gpio_out_set(s->cpu->gpif, N8X0_BT_RESET_GPIO, + qdev_connect_gpio_out(s->cpu->gpio, N8X0_BT_RESET_GPIO, csrhci_pins_get(radio)[csrhci_pin_reset]); - omap2_gpio_out_set(s->cpu->gpif, N8X0_BT_WKUP_GPIO, + qdev_connect_gpio_out(s->cpu->gpio, N8X0_BT_WKUP_GPIO, csrhci_pins_get(radio)[csrhci_pin_wakeup]); omap_uart_attach(s->cpu->uart[BT_UART], radio); @@ -763,7 +762,7 @@ static void n8x0_usb_power_cb(void *opaque, int line, int level) static void n8x0_usb_setup(struct n800_s *s) { - qemu_irq tusb_irq = omap2_gpio_in_get(s->cpu->gpif, N8X0_TUSB_INT_GPIO)[0]; + qemu_irq tusb_irq = qdev_get_gpio_in(s->cpu->gpio, N8X0_TUSB_INT_GPIO); qemu_irq tusb_pwr = qemu_allocate_irqs(n8x0_usb_power_cb, s, 1)[0]; TUSBState *tusb = tusb6010_init(tusb_irq); @@ -774,7 +773,7 @@ static void n8x0_usb_setup(struct n800_s *s) tusb6010_sync_io(tusb), NULL, NULL, tusb); s->usb = tusb; - omap2_gpio_out_set(s->cpu->gpif, N8X0_TUSB_ENABLE_GPIO, tusb_pwr); + qdev_connect_gpio_out(s->cpu->gpio, N8X0_TUSB_ENABLE_GPIO, tusb_pwr); } /* Setup done before the main bootloader starts by some early setup code @@ -1020,7 +1019,7 @@ static void n8x0_boot_init(void *opaque) /* If the machine has a slided keyboard, open it */ if (s->kbd) - qemu_irq_raise(omap2_gpio_in_get(s->cpu->gpif, N810_SLIDE_GPIO)[0]); + qemu_irq_raise(qdev_get_gpio_in(s->cpu->gpio, N810_SLIDE_GPIO)); } #define OMAP_TAG_NOKIA_BT 0x4e01 diff --git a/hw/omap.h b/hw/omap.h index 00a0ea9042..a064353aba 100644 --- a/hw/omap.h +++ b/hw/omap.h @@ -683,22 +683,6 @@ qemu_irq *omap_mpuio_in_get(struct omap_mpuio_s *s); void omap_mpuio_out_set(struct omap_mpuio_s *s, int line, qemu_irq handler); void omap_mpuio_key(struct omap_mpuio_s *s, int row, int col, int down); -/* omap1 gpio module interface */ -struct omap_gpio_s; -struct omap_gpio_s *omap_gpio_init(target_phys_addr_t base, - qemu_irq irq, omap_clk clk); -void omap_gpio_reset(struct omap_gpio_s *s); -qemu_irq *omap_gpio_in_get(struct omap_gpio_s *s); -void omap_gpio_out_set(struct omap_gpio_s *s, int line, qemu_irq handler); - -/* omap2 gpio interface */ -struct omap_gpif_s; -struct omap_gpif_s *omap2_gpio_init(struct omap_target_agent_s *ta, - qemu_irq *irq, omap_clk *fclk, omap_clk iclk, int modules); -void omap_gpif_reset(struct omap_gpif_s *s); -qemu_irq *omap2_gpio_in_get(struct omap_gpif_s *s, int start); -void omap2_gpio_out_set(struct omap_gpif_s *s, int line, qemu_irq handler); - struct uWireSlave { uint16_t (*receive)(void *opaque); void (*send)(void *opaque, uint16_t data); @@ -852,7 +836,7 @@ struct omap_mpu_state_s { /* MPUI-TIPB peripherals */ struct omap_uart_s *uart[3]; - struct omap_gpio_s *gpio; + DeviceState *gpio; struct omap_mcbsp_s *mcbsp1; struct omap_mcbsp_s *mcbsp3; @@ -950,8 +934,6 @@ struct omap_mpu_state_s { struct omap_gpmc_s *gpmc; struct omap_sysctl_s *sysc; - struct omap_gpif_s *gpif; - struct omap_mcspi_s *mcspi[2]; struct omap_dss_s *dss; diff --git a/hw/omap1.c b/hw/omap1.c index 364c26f877..400de475d9 100644 --- a/hw/omap1.c +++ b/hw/omap1.c @@ -27,6 +27,7 @@ #include "pc.h" #include "blockdev.h" #include "range.h" +#include "sysbus.h" /* Should signal the TCMI/GPMC */ uint32_t omap_badwidth_read8(void *opaque, target_phys_addr_t addr) @@ -3585,7 +3586,6 @@ static void omap1_mpu_reset(void *opaque) omap_uart_reset(mpu->uart[2]); omap_mmc_reset(mpu->mmc); omap_mpuio_reset(mpu->mpuio); - omap_gpio_reset(mpu->gpio); omap_uwire_reset(mpu->microwire); omap_pwl_reset(mpu); omap_pwt_reset(mpu); @@ -3845,8 +3845,12 @@ struct omap_mpu_state_s *omap310_mpu_init(unsigned long sdram_size, s->irq[1][OMAP_INT_KEYBOARD], s->irq[1][OMAP_INT_MPUIO], s->wakeup, omap_findclk(s, "clk32-kHz")); - s->gpio = omap_gpio_init(0xfffce000, s->irq[0][OMAP_INT_GPIO_BANK1], - omap_findclk(s, "arm_gpio_ck")); + s->gpio = qdev_create(NULL, "omap-gpio"); + qdev_prop_set_int32(s->gpio, "mpu_model", s->mpu_model); + qdev_init_nofail(s->gpio); + sysbus_connect_irq(sysbus_from_qdev(s->gpio), 0, + s->irq[0][OMAP_INT_GPIO_BANK1]); + sysbus_mmio_map(sysbus_from_qdev(s->gpio), 0, 0xfffce000); s->microwire = omap_uwire_init(0xfffb3000, &s->irq[1][OMAP_INT_uWireTX], s->drq[OMAP_DMA_UWIRE_TX], omap_findclk(s, "mpuper_ck")); diff --git a/hw/omap2.c b/hw/omap2.c index 0f13272c7b..c9b35405ed 100644 --- a/hw/omap2.c +++ b/hw/omap2.c @@ -27,6 +27,7 @@ #include "qemu-char.h" #include "flash.h" #include "soc_dma.h" +#include "sysbus.h" #include "audio/audio.h" /* Enhanced Audio Controller (CODEC only) */ @@ -2203,7 +2204,6 @@ static void omap2_mpu_reset(void *opaque) omap_uart_reset(mpu->uart[1]); omap_uart_reset(mpu->uart[2]); omap_mmc_reset(mpu->mmc); - omap_gpif_reset(mpu->gpif); omap_mcspi_reset(mpu->mcspi[0]); omap_mcspi_reset(mpu->mcspi[1]); omap_i2c_reset(mpu->i2c[0]); @@ -2232,9 +2232,10 @@ struct omap_mpu_state_s *omap2420_mpu_init(unsigned long sdram_size, ram_addr_t sram_base, q2_base; qemu_irq *cpu_irq; qemu_irq dma_irqs[4]; - omap_clk gpio_clks[4]; DriveInfo *dinfo; int i; + SysBusDevice *busdev; + struct omap_target_agent_s *ta; /* Core */ s->mpu_model = omap2420; @@ -2377,13 +2378,28 @@ struct omap_mpu_state_s *omap2420_mpu_init(unsigned long sdram_size, omap_findclk(s, "i2c2.fclk"), omap_findclk(s, "i2c2.iclk")); - gpio_clks[0] = omap_findclk(s, "gpio1_dbclk"); - gpio_clks[1] = omap_findclk(s, "gpio2_dbclk"); - gpio_clks[2] = omap_findclk(s, "gpio3_dbclk"); - gpio_clks[3] = omap_findclk(s, "gpio4_dbclk"); - s->gpif = omap2_gpio_init(omap_l4ta(s->l4, 3), - &s->irq[0][OMAP_INT_24XX_GPIO_BANK1], - gpio_clks, omap_findclk(s, "gpio_iclk"), 4); + s->gpio = qdev_create(NULL, "omap2-gpio"); + qdev_prop_set_int32(s->gpio, "mpu_model", s->mpu_model); + qdev_prop_set_ptr(s->gpio, "iclk", omap_findclk(s, "gpio_iclk")); + qdev_prop_set_ptr(s->gpio, "fclk0", omap_findclk(s, "gpio1_dbclk")); + qdev_prop_set_ptr(s->gpio, "fclk1", omap_findclk(s, "gpio2_dbclk")); + qdev_prop_set_ptr(s->gpio, "fclk2", omap_findclk(s, "gpio3_dbclk")); + qdev_prop_set_ptr(s->gpio, "fclk3", omap_findclk(s, "gpio4_dbclk")); + if (s->mpu_model == omap2430) { + qdev_prop_set_ptr(s->gpio, "fclk4", omap_findclk(s, "gpio5_dbclk")); + } + qdev_init_nofail(s->gpio); + busdev = sysbus_from_qdev(s->gpio); + sysbus_connect_irq(busdev, 0, s->irq[0][OMAP_INT_24XX_GPIO_BANK1]); + sysbus_connect_irq(busdev, 3, s->irq[0][OMAP_INT_24XX_GPIO_BANK2]); + sysbus_connect_irq(busdev, 6, s->irq[0][OMAP_INT_24XX_GPIO_BANK3]); + sysbus_connect_irq(busdev, 9, s->irq[0][OMAP_INT_24XX_GPIO_BANK4]); + ta = omap_l4ta(s->l4, 3); + sysbus_mmio_map(busdev, 0, omap_l4_region_base(ta, 1)); + sysbus_mmio_map(busdev, 1, omap_l4_region_base(ta, 0)); + sysbus_mmio_map(busdev, 2, omap_l4_region_base(ta, 2)); + sysbus_mmio_map(busdev, 3, omap_l4_region_base(ta, 4)); + sysbus_mmio_map(busdev, 4, omap_l4_region_base(ta, 5)); s->sdrc = omap_sdrc_init(0x68009000); s->gpmc = omap_gpmc_init(0x6800a000, s->irq[0][OMAP_INT_24XX_GPMC_IRQ]); diff --git a/hw/omap_gpio.c b/hw/omap_gpio.c index 478f7d9825..c23964c66d 100644 --- a/hw/omap_gpio.c +++ b/hw/omap_gpio.c @@ -20,10 +20,10 @@ #include "hw.h" #include "omap.h" -/* General-Purpose I/O */ +#include "sysbus.h" + struct omap_gpio_s { qemu_irq irq; - qemu_irq *in; qemu_irq handler[16]; uint16_t inputs; @@ -35,9 +35,17 @@ struct omap_gpio_s { uint16_t pins; }; +struct omap_gpif_s { + SysBusDevice busdev; + int mpu_model; + void *clk; + struct omap_gpio_s omap1; +}; + +/* General-Purpose I/O of OMAP1 */ static void omap_gpio_set(void *opaque, int line, int level) { - struct omap_gpio_s *s = (struct omap_gpio_s *) opaque; + struct omap_gpio_s *s = &((struct omap_gpif_s *) opaque)->omap1; uint16_t prev = s->inputs; if (level) @@ -160,7 +168,7 @@ static CPUWriteMemoryFunc * const omap_gpio_writefn[] = { omap_badwidth_write16, }; -void omap_gpio_reset(struct omap_gpio_s *s) +static void omap_gpio_reset(struct omap_gpio_s *s) { s->inputs = 0; s->outputs = ~0; @@ -171,43 +179,12 @@ void omap_gpio_reset(struct omap_gpio_s *s) s->pins = ~0; } -struct omap_gpio_s *omap_gpio_init(target_phys_addr_t base, - qemu_irq irq, omap_clk clk) -{ - int iomemtype; - struct omap_gpio_s *s = (struct omap_gpio_s *) - qemu_mallocz(sizeof(struct omap_gpio_s)); - - s->irq = irq; - s->in = qemu_allocate_irqs(omap_gpio_set, s, 16); - omap_gpio_reset(s); - - iomemtype = cpu_register_io_memory(omap_gpio_readfn, - omap_gpio_writefn, s, DEVICE_NATIVE_ENDIAN); - cpu_register_physical_memory(base, 0x1000, iomemtype); - - return s; -} - -qemu_irq *omap_gpio_in_get(struct omap_gpio_s *s) -{ - return s->in; -} - -void omap_gpio_out_set(struct omap_gpio_s *s, int line, qemu_irq handler) -{ - if (line >= 16 || line < 0) - hw_error("%s: No GPIO line %i\n", __FUNCTION__, line); - s->handler[line] = handler; -} - -/* General-Purpose Interface of OMAP2 */ struct omap2_gpio_s { qemu_irq irq[2]; qemu_irq wkup; - qemu_irq *in; - qemu_irq handler[32]; + qemu_irq *handler; + uint8_t revision; uint8_t config[2]; uint32_t inputs; uint32_t outputs; @@ -221,8 +198,21 @@ struct omap2_gpio_s { uint8_t delay; }; +struct omap2_gpif_s { + SysBusDevice busdev; + int mpu_model; + void *iclk; + void *fclk[6]; + int modulecount; + struct omap2_gpio_s *modules; + qemu_irq *handler; + int autoidle; + int gpo; +}; + +/* General-Purpose Interface of OMAP2/3 */ static inline void omap2_gpio_module_int_update(struct omap2_gpio_s *s, - int line) + int line) { qemu_set_irq(s->irq[line], s->ints[line] & s->mask[line]); } @@ -269,10 +259,12 @@ static inline void omap2_gpio_module_int(struct omap2_gpio_s *s, int line) omap2_gpio_module_wake(s, line); } -static void omap2_gpio_module_set(void *opaque, int line, int level) +static void omap2_gpio_set(void *opaque, int line, int level) { - struct omap2_gpio_s *s = (struct omap2_gpio_s *) opaque; + struct omap2_gpif_s *p = opaque; + struct omap2_gpio_s *s = &p->modules[line >> 5]; + line &= 31; if (level) { if (s->dir & (1 << line) & ((~s->inputs & s->edge[0]) | s->level[1])) omap2_gpio_module_int(s, line); @@ -308,7 +300,7 @@ static uint32_t omap2_gpio_module_read(void *opaque, target_phys_addr_t addr) switch (addr) { case 0x00: /* GPIO_REVISION */ - return 0x18; + return s->revision; case 0x10: /* GPIO_SYSCONFIG */ return s->config[0]; @@ -583,45 +575,28 @@ static CPUWriteMemoryFunc * const omap2_gpio_module_writefn[] = { omap2_gpio_module_write, }; -static void omap2_gpio_module_init(struct omap2_gpio_s *s, - struct omap_target_agent_s *ta, int region, - qemu_irq mpu, qemu_irq dsp, qemu_irq wkup, - omap_clk fclk, omap_clk iclk) +static void omap_gpif_reset(DeviceState *dev) { - int iomemtype; - - s->irq[0] = mpu; - s->irq[1] = dsp; - s->wkup = wkup; - s->in = qemu_allocate_irqs(omap2_gpio_module_set, s, 32); - - iomemtype = l4_register_io_memory(omap2_gpio_module_readfn, - omap2_gpio_module_writefn, s); - omap_l4_attach(ta, region, iomemtype); + struct omap_gpif_s *s = FROM_SYSBUS(struct omap_gpif_s, + sysbus_from_qdev(dev)); + omap_gpio_reset(&s->omap1); } -struct omap_gpif_s { - struct omap2_gpio_s module[5]; - int modules; - - int autoidle; - int gpo; -}; - -void omap_gpif_reset(struct omap_gpif_s *s) +static void omap2_gpif_reset(DeviceState *dev) { int i; - - for (i = 0; i < s->modules; i ++) - omap2_gpio_module_reset(s->module + i); - + struct omap2_gpif_s *s = FROM_SYSBUS(struct omap2_gpif_s, + sysbus_from_qdev(dev)); + for (i = 0; i < s->modulecount; i++) { + omap2_gpio_module_reset(&s->modules[i]); + } s->autoidle = 0; s->gpo = 0; } -static uint32_t omap_gpif_top_read(void *opaque, target_phys_addr_t addr) +static uint32_t omap2_gpif_top_read(void *opaque, target_phys_addr_t addr) { - struct omap_gpif_s *s = (struct omap_gpif_s *) opaque; + struct omap2_gpif_s *s = (struct omap2_gpif_s *) opaque; switch (addr) { case 0x00: /* IPGENERICOCPSPL_REVISION */ @@ -647,10 +622,10 @@ static uint32_t omap_gpif_top_read(void *opaque, target_phys_addr_t addr) return 0; } -static void omap_gpif_top_write(void *opaque, target_phys_addr_t addr, +static void omap2_gpif_top_write(void *opaque, target_phys_addr_t addr, uint32_t value) { - struct omap_gpif_s *s = (struct omap_gpif_s *) opaque; + struct omap2_gpif_s *s = (struct omap2_gpif_s *) opaque; switch (addr) { case 0x00: /* IPGENERICOCPSPL_REVISION */ @@ -662,7 +637,7 @@ static void omap_gpif_top_write(void *opaque, target_phys_addr_t addr, case 0x10: /* IPGENERICOCPSPL_SYSCONFIG */ if (value & (1 << 1)) /* SOFTRESET */ - omap_gpif_reset(s); + omap2_gpif_reset(&s->busdev.qdev); s->autoidle = value & 1; break; @@ -676,50 +651,119 @@ static void omap_gpif_top_write(void *opaque, target_phys_addr_t addr, } } -static CPUReadMemoryFunc * const omap_gpif_top_readfn[] = { - omap_gpif_top_read, - omap_gpif_top_read, - omap_gpif_top_read, +static CPUReadMemoryFunc * const omap2_gpif_top_readfn[] = { + omap2_gpif_top_read, + omap2_gpif_top_read, + omap2_gpif_top_read, }; -static CPUWriteMemoryFunc * const omap_gpif_top_writefn[] = { - omap_gpif_top_write, - omap_gpif_top_write, - omap_gpif_top_write, +static CPUWriteMemoryFunc * const omap2_gpif_top_writefn[] = { + omap2_gpif_top_write, + omap2_gpif_top_write, + omap2_gpif_top_write, }; -struct omap_gpif_s *omap2_gpio_init(struct omap_target_agent_s *ta, - qemu_irq *irq, omap_clk *fclk, omap_clk iclk, int modules) +static int omap_gpio_init(SysBusDevice *dev) { - int iomemtype, i; - struct omap_gpif_s *s = (struct omap_gpif_s *) - qemu_mallocz(sizeof(struct omap_gpif_s)); - int region[4] = { 0, 2, 4, 5 }; - - s->modules = modules; - for (i = 0; i < modules; i ++) - omap2_gpio_module_init(s->module + i, ta, region[i], - irq[i], NULL, NULL, fclk[i], iclk); - - omap_gpif_reset(s); - - iomemtype = l4_register_io_memory(omap_gpif_top_readfn, - omap_gpif_top_writefn, s); - omap_l4_attach(ta, 1, iomemtype); - - return s; + struct omap_gpif_s *s = FROM_SYSBUS(struct omap_gpif_s, dev); + if (!s->clk) { + hw_error("omap-gpio: clk not connected\n"); + } + qdev_init_gpio_in(&dev->qdev, omap_gpio_set, 16); + qdev_init_gpio_out(&dev->qdev, s->omap1.handler, 16); + sysbus_init_irq(dev, &s->omap1.irq); + sysbus_init_mmio(dev, 0x1000, + cpu_register_io_memory(omap_gpio_readfn, + omap_gpio_writefn, + &s->omap1, + DEVICE_NATIVE_ENDIAN)); + return 0; } -qemu_irq *omap2_gpio_in_get(struct omap_gpif_s *s, int start) +static int omap2_gpio_init(SysBusDevice *dev) { - if (start >= s->modules * 32 || start < 0) - hw_error("%s: No GPIO line %i\n", __FUNCTION__, start); - return s->module[start >> 5].in + (start & 31); + int i; + struct omap2_gpif_s *s = FROM_SYSBUS(struct omap2_gpif_s, dev); + if (!s->iclk) { + hw_error("omap2-gpio: iclk not connected\n"); + } + if (s->mpu_model < omap3430) { + s->modulecount = (s->mpu_model < omap2430) ? 4 : 5; + sysbus_init_mmio(dev, 0x1000, + cpu_register_io_memory(omap2_gpif_top_readfn, + omap2_gpif_top_writefn, s, + DEVICE_NATIVE_ENDIAN)); + } else { + s->modulecount = 6; + } + s->modules = qemu_mallocz(s->modulecount * sizeof(struct omap2_gpio_s)); + s->handler = qemu_mallocz(s->modulecount * 32 * sizeof(qemu_irq)); + qdev_init_gpio_in(&dev->qdev, omap2_gpio_set, s->modulecount * 32); + qdev_init_gpio_out(&dev->qdev, s->handler, s->modulecount * 32); + for (i = 0; i < s->modulecount; i++) { + struct omap2_gpio_s *m = &s->modules[i]; + if (!s->fclk[i]) { + hw_error("omap2-gpio: fclk%d not connected\n", i); + } + m->revision = (s->mpu_model < omap3430) ? 0x18 : 0x25; + m->handler = &s->handler[i * 32]; + sysbus_init_irq(dev, &m->irq[0]); /* mpu irq */ + sysbus_init_irq(dev, &m->irq[1]); /* dsp irq */ + sysbus_init_irq(dev, &m->wkup); + sysbus_init_mmio(dev, 0x1000, + cpu_register_io_memory(omap2_gpio_module_readfn, + omap2_gpio_module_writefn, + m, DEVICE_NATIVE_ENDIAN)); + } + return 0; } -void omap2_gpio_out_set(struct omap_gpif_s *s, int line, qemu_irq handler) +/* Using qdev pointer properties for the clocks is not ideal. + * qdev should support a generic means of defining a 'port' with + * an arbitrary interface for connecting two devices. Then we + * could reframe the omap clock API in terms of clock ports, + * and get some type safety. For now the best qdev provides is + * passing an arbitrary pointer. + * (It's not possible to pass in the string which is the clock + * name, because this device does not have the necessary information + * (ie the struct omap_mpu_state_s*) to do the clockname to pointer + * translation.) + */ + +static SysBusDeviceInfo omap_gpio_info = { + .init = omap_gpio_init, + .qdev.name = "omap-gpio", + .qdev.size = sizeof(struct omap_gpif_s), + .qdev.reset = omap_gpif_reset, + .qdev.props = (Property[]) { + DEFINE_PROP_INT32("mpu_model", struct omap_gpif_s, mpu_model, 0), + DEFINE_PROP_PTR("clk", struct omap_gpif_s, clk), + DEFINE_PROP_END_OF_LIST() + } +}; + +static SysBusDeviceInfo omap2_gpio_info = { + .init = omap2_gpio_init, + .qdev.name = "omap2-gpio", + .qdev.size = sizeof(struct omap2_gpif_s), + .qdev.reset = omap2_gpif_reset, + .qdev.props = (Property[]) { + DEFINE_PROP_INT32("mpu_model", struct omap2_gpif_s, mpu_model, 0), + DEFINE_PROP_PTR("iclk", struct omap2_gpif_s, iclk), + DEFINE_PROP_PTR("fclk0", struct omap2_gpif_s, fclk[0]), + DEFINE_PROP_PTR("fclk1", struct omap2_gpif_s, fclk[1]), + DEFINE_PROP_PTR("fclk2", struct omap2_gpif_s, fclk[2]), + DEFINE_PROP_PTR("fclk3", struct omap2_gpif_s, fclk[3]), + DEFINE_PROP_PTR("fclk4", struct omap2_gpif_s, fclk[4]), + DEFINE_PROP_PTR("fclk5", struct omap2_gpif_s, fclk[5]), + DEFINE_PROP_END_OF_LIST() + } +}; + +static void omap_gpio_register_device(void) { - if (line >= s->modules * 32 || line < 0) - hw_error("%s: No GPIO line %i\n", __FUNCTION__, line); - s->module[line >> 5].handler[line & 31] = handler; + sysbus_register_withprop(&omap_gpio_info); + sysbus_register_withprop(&omap2_gpio_info); } + +device_init(omap_gpio_register_device) diff --git a/hw/palm.c b/hw/palm.c index f22d7775ee..4c67e75c83 100644 --- a/hw/palm.c +++ b/hw/palm.c @@ -94,7 +94,7 @@ static void palmte_microwire_setup(struct omap_mpu_state_s *cpu) { uWireSlave *tsc; - tsc = tsc2102_init(omap_gpio_in_get(cpu->gpio)[PALMTE_PINTDAV_GPIO]); + tsc = tsc2102_init(qdev_get_gpio_in(cpu->gpio, PALMTE_PINTDAV_GPIO)); omap_uwire_attach(cpu->microwire, tsc, 0); omap_mcbsp_i2s_attach(cpu->mcbsp1, tsc210x_codec(tsc)); @@ -163,24 +163,24 @@ static void palmte_gpio_setup(struct omap_mpu_state_s *cpu) qemu_irq *misc_gpio; omap_mmc_handlers(cpu->mmc, - omap_gpio_in_get(cpu->gpio)[PALMTE_MMC_WP_GPIO], + qdev_get_gpio_in(cpu->gpio, PALMTE_MMC_WP_GPIO), qemu_irq_invert(omap_mpuio_in_get(cpu->mpuio) [PALMTE_MMC_SWITCH_GPIO])); misc_gpio = qemu_allocate_irqs(palmte_onoff_gpios, cpu, 7); - omap_gpio_out_set(cpu->gpio, PALMTE_MMC_POWER_GPIO, misc_gpio[0]); - omap_gpio_out_set(cpu->gpio, PALMTE_SPEAKER_GPIO, misc_gpio[1]); - omap_gpio_out_set(cpu->gpio, 11, misc_gpio[2]); - omap_gpio_out_set(cpu->gpio, 12, misc_gpio[3]); - omap_gpio_out_set(cpu->gpio, 13, misc_gpio[4]); - omap_mpuio_out_set(cpu->mpuio, 1, misc_gpio[5]); - omap_mpuio_out_set(cpu->mpuio, 3, misc_gpio[6]); + qdev_connect_gpio_out(cpu->gpio, PALMTE_MMC_POWER_GPIO, misc_gpio[0]); + qdev_connect_gpio_out(cpu->gpio, PALMTE_SPEAKER_GPIO, misc_gpio[1]); + qdev_connect_gpio_out(cpu->gpio, 11, misc_gpio[2]); + qdev_connect_gpio_out(cpu->gpio, 12, misc_gpio[3]); + qdev_connect_gpio_out(cpu->gpio, 13, misc_gpio[4]); + omap_mpuio_out_set(cpu->mpuio, 1, misc_gpio[5]); + omap_mpuio_out_set(cpu->mpuio, 3, misc_gpio[6]); /* Reset some inputs to initial state. */ - qemu_irq_lower(omap_gpio_in_get(cpu->gpio)[PALMTE_USBDETECT_GPIO]); - qemu_irq_lower(omap_gpio_in_get(cpu->gpio)[PALMTE_USB_OR_DC_GPIO]); - qemu_irq_lower(omap_gpio_in_get(cpu->gpio)[4]); - qemu_irq_lower(omap_gpio_in_get(cpu->gpio)[PALMTE_HEADPHONES_GPIO]); + qemu_irq_lower(qdev_get_gpio_in(cpu->gpio, PALMTE_USBDETECT_GPIO)); + qemu_irq_lower(qdev_get_gpio_in(cpu->gpio, PALMTE_USB_OR_DC_GPIO)); + qemu_irq_lower(qdev_get_gpio_in(cpu->gpio, 4)); + qemu_irq_lower(qdev_get_gpio_in(cpu->gpio, PALMTE_HEADPHONES_GPIO)); qemu_irq_lower(omap_mpuio_in_get(cpu->mpuio)[PALMTE_DC_GPIO]); qemu_irq_raise(omap_mpuio_in_get(cpu->mpuio)[6]); qemu_irq_raise(omap_mpuio_in_get(cpu->mpuio)[7]); From c4f05c8cf715fa613e1985421080e62a7b169284 Mon Sep 17 00:00:00 2001 From: Peter Maydell Date: Fri, 29 Jul 2011 16:35:18 +0100 Subject: [PATCH 120/230] lm832x: Take DeviceState pointer in lm832x_key_event() Since lm832x has been qdev'ified, its users will generally have a DeviceState pointer rather than an i2c_slave pointer, so adjust lm832x_key_event's prototype to suit. This allows the n810 (its only user) to actually pass a correct pointer to it rather than NULL. The effect is that we no longer segfault when a key is pressed. Signed-off-by: Peter Maydell Signed-off-by: Andrzej Zaborowski --- hw/i2c.h | 2 +- hw/lm832x.c | 4 ++-- hw/nseries.c | 7 +++---- 3 files changed, 6 insertions(+), 7 deletions(-) diff --git a/hw/i2c.h b/hw/i2c.h index 5514402029..9381d01589 100644 --- a/hw/i2c.h +++ b/hw/i2c.h @@ -72,6 +72,6 @@ void wm8750_set_bclk_in(void *opaque, int new_hz); void tmp105_set(i2c_slave *i2c, int temp); /* lm832x.c */ -void lm832x_key_event(i2c_slave *i2c, int key, int state); +void lm832x_key_event(DeviceState *dev, int key, int state); #endif diff --git a/hw/lm832x.c b/hw/lm832x.c index 590a4ccff9..992ce49729 100644 --- a/hw/lm832x.c +++ b/hw/lm832x.c @@ -474,9 +474,9 @@ static int lm8323_init(i2c_slave *i2c) return 0; } -void lm832x_key_event(struct i2c_slave *i2c, int key, int state) +void lm832x_key_event(DeviceState *dev, int key, int state) { - LM823KbdState *s = (LM823KbdState *) i2c; + LM823KbdState *s = FROM_I2C_SLAVE(LM823KbdState, I2C_SLAVE_FROM_QDEV(dev)); if ((s->status & INT_ERROR) && (s->error & ERR_FIFOOVR)) return; diff --git a/hw/nseries.c b/hw/nseries.c index d9a542862d..d12ed46461 100644 --- a/hw/nseries.c +++ b/hw/nseries.c @@ -45,7 +45,7 @@ struct n800_s { i2c_bus *i2c; int keymap[0x80]; - i2c_slave *kbd; + DeviceState *kbd; TUSBState *usb; void *retu; @@ -362,7 +362,6 @@ static int n810_keys[0x80] = { static void n810_kbd_setup(struct n800_s *s) { qemu_irq kbd_irq = qdev_get_gpio_in(s->cpu->gpio, N810_KEYBOARD_GPIO); - DeviceState *dev; int i; for (i = 0; i < 0x80; i ++) @@ -375,8 +374,8 @@ static void n810_kbd_setup(struct n800_s *s) /* Attach the LM8322 keyboard to the I2C bus, * should happen in n8x0_i2c_setup and s->kbd be initialised here. */ - dev = i2c_create_slave(s->i2c, "lm8323", N810_LM8323_ADDR); - qdev_connect_gpio_out(dev, 0, kbd_irq); + s->kbd = i2c_create_slave(s->i2c, "lm8323", N810_LM8323_ADDR); + qdev_connect_gpio_out(s->kbd, 0, kbd_irq); } /* LCD MIPI DBI-C controller (URAL) */ From 522f253ca8c731aafc8e53087a18f6015c4e776e Mon Sep 17 00:00:00 2001 From: Peter Maydell Date: Fri, 29 Jul 2011 16:35:19 +0100 Subject: [PATCH 121/230] hw/nand: Pass block device state to init function Pass the BlockDeviceState to the nand_init() function rather than having it look it up via drive_get() itself. Signed-off-by: Peter Maydell Signed-off-by: Andrzej Zaborowski --- hw/axis_dev88.c | 6 +++++- hw/flash.h | 2 +- hw/nand.c | 8 ++------ hw/spitz.c | 4 +++- hw/tc6393xb.c | 5 ++++- 5 files changed, 15 insertions(+), 10 deletions(-) diff --git a/hw/axis_dev88.c b/hw/axis_dev88.c index 0e2135afd0..de1f5a5fce 100644 --- a/hw/axis_dev88.c +++ b/hw/axis_dev88.c @@ -30,6 +30,7 @@ #include "loader.h" #include "elf.h" #include "cris-boot.h" +#include "blockdev.h" #define D(x) #define DNAND(x) @@ -251,6 +252,7 @@ void axisdev88_init (ram_addr_t ram_size, CPUState *env; DeviceState *dev; SysBusDevice *s; + DriveInfo *nand; qemu_irq irq[30], nmi[2], *cpu_irq; void *etraxfs_dmac; struct etraxfs_dma_client *eth[2] = {NULL, NULL}; @@ -278,7 +280,9 @@ void axisdev88_init (ram_addr_t ram_size, /* Attach a NAND flash to CS1. */ - nand_state.nand = nand_init(NAND_MFR_STMICRO, 0x39); + nand = drive_get(IF_MTD, 0, 0); + nand_state.nand = nand_init(nand ? nand->bdrv : NULL, + NAND_MFR_STMICRO, 0x39); nand_regs = cpu_register_io_memory(nand_read, nand_write, &nand_state, DEVICE_NATIVE_ENDIAN); cpu_register_physical_memory(0x10000000, 0x05000000, nand_regs); diff --git a/hw/flash.h b/hw/flash.h index c22e1a922c..a992bb8157 100644 --- a/hw/flash.h +++ b/hw/flash.h @@ -19,7 +19,7 @@ pflash_t *pflash_cfi02_register(target_phys_addr_t base, ram_addr_t off, /* nand.c */ typedef struct NANDFlashState NANDFlashState; -NANDFlashState *nand_init(int manf_id, int chip_id); +NANDFlashState *nand_init(BlockDriverState *bdrv, int manf_id, int chip_id); void nand_done(NANDFlashState *s); void nand_setpins(NANDFlashState *s, uint8_t cle, uint8_t ale, uint8_t ce, uint8_t wp, uint8_t gnd); diff --git a/hw/nand.c b/hw/nand.c index 37e51d7140..d6204d983e 100644 --- a/hw/nand.c +++ b/hw/nand.c @@ -14,7 +14,6 @@ # include "hw.h" # include "flash.h" # include "blockdev.h" -/* FIXME: Pass block device as an argument. */ # define NAND_CMD_READ0 0x00 # define NAND_CMD_READ1 0x01 @@ -451,20 +450,17 @@ uint8_t nand_getio(NANDFlashState *s) return *(s->ioaddr ++); } -NANDFlashState *nand_init(int manf_id, int chip_id) +NANDFlashState *nand_init(BlockDriverState *bdrv, int manf_id, int chip_id) { int pagesize; NANDFlashState *s; - DriveInfo *dinfo; if (nand_flash_ids[chip_id].size == 0) { hw_error("%s: Unsupported NAND chip ID.\n", __FUNCTION__); } s = (NANDFlashState *) qemu_mallocz(sizeof(NANDFlashState)); - dinfo = drive_get(IF_MTD, 0, 0); - if (dinfo) - s->bdrv = dinfo->bdrv; + s->bdrv = bdrv; s->manf_id = manf_id; s->chip_id = chip_id; s->size = nand_flash_ids[s->chip_id].size << 20; diff --git a/hw/spitz.c b/hw/spitz.c index 006f7a97e3..78e9c34592 100644 --- a/hw/spitz.c +++ b/hw/spitz.c @@ -169,11 +169,13 @@ static void sl_flash_register(PXA2xxState *cpu, int size) static int sl_nand_init(SysBusDevice *dev) { int iomemtype; SLNANDState *s; + DriveInfo *nand; s = FROM_SYSBUS(SLNANDState, dev); s->ctl = 0; - s->nand = nand_init(s->manf_id, s->chip_id); + nand = drive_get(IF_MTD, 0, 0); + s->nand = nand_init(nand ? nand->bdrv : NULL, s->manf_id, s->chip_id); iomemtype = cpu_register_io_memory(sl_readfn, sl_writefn, s, DEVICE_NATIVE_ENDIAN); diff --git a/hw/tc6393xb.c b/hw/tc6393xb.c index ed49e944df..4de081966b 100644 --- a/hw/tc6393xb.c +++ b/hw/tc6393xb.c @@ -12,6 +12,7 @@ #include "flash.h" #include "console.h" #include "pixel_ops.h" +#include "blockdev.h" #define IRQ_TC6393_NAND 0 #define IRQ_TC6393_MMC 1 @@ -566,6 +567,7 @@ TC6393xbState *tc6393xb_init(uint32_t base, qemu_irq irq) { int iomemtype; TC6393xbState *s; + DriveInfo *nand; CPUReadMemoryFunc * const tc6393xb_readfn[] = { tc6393xb_readb, tc6393xb_readw, @@ -586,7 +588,8 @@ TC6393xbState *tc6393xb_init(uint32_t base, qemu_irq irq) s->sub_irqs = qemu_allocate_irqs(tc6393xb_sub_irq, s, TC6393XB_NR_IRQS); - s->flash = nand_init(NAND_MFR_TOSHIBA, 0x76); + nand = drive_get(IF_MTD, 0, 0); + s->flash = nand_init(nand ? nand->bdrv : NULL, NAND_MFR_TOSHIBA, 0x76); iomemtype = cpu_register_io_memory(tc6393xb_readfn, tc6393xb_writefn, s, DEVICE_NATIVE_ENDIAN); From d5f2fd586f1cc4651f8b03336b34c28dceab43bb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Juha=20Riihim=C3=A4ki?= Date: Fri, 29 Jul 2011 16:35:20 +0100 Subject: [PATCH 122/230] hw/nand: Support large NAND devices MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Add support for NAND devices of over 1Gb. Signed-off-by: Juha Riihimäki [Riku Voipio: Fixes and restructuring patchset] Signed-off-by: Riku Voipio [Peter Maydell: More fixes and cleanups for upstream submission] Signed-off-by: Peter Maydell Signed-off-by: Andrzej Zaborowski --- hw/nand.c | 48 +++++++++++++++++++++++++++--------------------- 1 file changed, 27 insertions(+), 21 deletions(-) diff --git a/hw/nand.c b/hw/nand.c index d6204d983e..764356adc4 100644 --- a/hw/nand.c +++ b/hw/nand.c @@ -6,6 +6,10 @@ * Copyright (c) 2006 Openedhand Ltd. * Written by Andrzej Zaborowski * + * Support for additional features based on "MT29F2G16ABCWP 2Gx16" + * datasheet from Micron Technology and "NAND02G-B2C" datasheet + * from ST Microelectronics. + * * This code is licensed under the GNU GPL v2. */ @@ -57,14 +61,15 @@ struct NANDFlashState { uint8_t *ioaddr; int iolen; - uint32_t cmd, addr; + uint32_t cmd; + uint64_t addr; int addrlen; int status; int offset; void (*blk_write)(NANDFlashState *s); void (*blk_erase)(NANDFlashState *s); - void (*blk_load)(NANDFlashState *s, uint32_t addr, int offset); + void (*blk_load)(NANDFlashState *s, uint64_t addr, int offset); uint32_t ioaddr_vmstate; }; @@ -318,7 +323,7 @@ static const VMStateDescription vmstate_nand = { VMSTATE_UINT32(ioaddr_vmstate, NANDFlashState), VMSTATE_INT32(iolen, NANDFlashState), VMSTATE_UINT32(cmd, NANDFlashState), - VMSTATE_UINT32(addr, NANDFlashState), + VMSTATE_UINT64(addr, NANDFlashState), VMSTATE_INT32(addrlen, NANDFlashState), VMSTATE_INT32(status, NANDFlashState), VMSTATE_INT32(offset, NANDFlashState), @@ -432,7 +437,7 @@ uint8_t nand_getio(NANDFlashState *s) /* Allow sequential reading */ if (!s->iolen && s->cmd == NAND_CMD_READ0) { - offset = (s->addr & ((1 << s->addr_shift) - 1)) + s->offset; + offset = (int) (s->addr & ((1 << s->addr_shift) - 1)) + s->offset; s->offset = 0; s->blk_load(s, s->addr, offset); @@ -526,7 +531,7 @@ void nand_done(NANDFlashState *s) /* Program a single page */ static void glue(nand_blk_write_, PAGE_SIZE)(NANDFlashState *s) { - uint32_t off, page, sector, soff; + uint64_t off, page, sector, soff; uint8_t iobuf[(PAGE_SECTORS + 2) * 0x200]; if (PAGE(s->addr) >= s->pages) return; @@ -539,7 +544,7 @@ static void glue(nand_blk_write_, PAGE_SIZE)(NANDFlashState *s) off = (s->addr & PAGE_MASK) + s->offset; soff = SECTOR_OFFSET(s->addr); if (bdrv_read(s->bdrv, sector, iobuf, PAGE_SECTORS) == -1) { - printf("%s: read error in sector %i\n", __FUNCTION__, sector); + printf("%s: read error in sector %" PRIu64 "\n", __func__, sector); return; } @@ -551,20 +556,20 @@ static void glue(nand_blk_write_, PAGE_SIZE)(NANDFlashState *s) } if (bdrv_write(s->bdrv, sector, iobuf, PAGE_SECTORS) == -1) - printf("%s: write error in sector %i\n", __FUNCTION__, sector); + printf("%s: write error in sector %" PRIu64 "\n", __func__, sector); } else { off = PAGE_START(s->addr) + (s->addr & PAGE_MASK) + s->offset; sector = off >> 9; soff = off & 0x1ff; if (bdrv_read(s->bdrv, sector, iobuf, PAGE_SECTORS + 2) == -1) { - printf("%s: read error in sector %i\n", __FUNCTION__, sector); + printf("%s: read error in sector %" PRIu64 "\n", __func__, sector); return; } memcpy(iobuf + soff, s->io, s->iolen); if (bdrv_write(s->bdrv, sector, iobuf, PAGE_SECTORS + 2) == -1) - printf("%s: write error in sector %i\n", __FUNCTION__, sector); + printf("%s: write error in sector %" PRIu64 "\n", __func__, sector); } s->offset = 0; } @@ -572,7 +577,7 @@ static void glue(nand_blk_write_, PAGE_SIZE)(NANDFlashState *s) /* Erase a single block */ static void glue(nand_blk_erase_, PAGE_SIZE)(NANDFlashState *s) { - uint32_t i, page, addr; + uint64_t i, page, addr; uint8_t iobuf[0x200] = { [0 ... 0x1ff] = 0xff, }; addr = s->addr & ~((1 << (ADDR_SHIFT + s->erase_shift)) - 1); @@ -589,34 +594,35 @@ static void glue(nand_blk_erase_, PAGE_SIZE)(NANDFlashState *s) page = SECTOR(addr + (ADDR_SHIFT + s->erase_shift)); for (; i < page; i ++) if (bdrv_write(s->bdrv, i, iobuf, 1) == -1) - printf("%s: write error in sector %i\n", __FUNCTION__, i); + printf("%s: write error in sector %" PRIu64 "\n", __func__, i); } else { addr = PAGE_START(addr); page = addr >> 9; if (bdrv_read(s->bdrv, page, iobuf, 1) == -1) - printf("%s: read error in sector %i\n", __FUNCTION__, page); + printf("%s: read error in sector %" PRIu64 "\n", __func__, page); memset(iobuf + (addr & 0x1ff), 0xff, (~addr & 0x1ff) + 1); if (bdrv_write(s->bdrv, page, iobuf, 1) == -1) - printf("%s: write error in sector %i\n", __FUNCTION__, page); + printf("%s: write error in sector %" PRIu64 "\n", __func__, page); memset(iobuf, 0xff, 0x200); i = (addr & ~0x1ff) + 0x200; for (addr += ((PAGE_SIZE + OOB_SIZE) << s->erase_shift) - 0x200; i < addr; i += 0x200) if (bdrv_write(s->bdrv, i >> 9, iobuf, 1) == -1) - printf("%s: write error in sector %i\n", __FUNCTION__, i >> 9); + printf("%s: write error in sector %" PRIu64 "\n", + __func__, i >> 9); page = i >> 9; if (bdrv_read(s->bdrv, page, iobuf, 1) == -1) - printf("%s: read error in sector %i\n", __FUNCTION__, page); + printf("%s: read error in sector %" PRIu64 "\n", __func__, page); memset(iobuf, 0xff, ((addr - 1) & 0x1ff) + 1); if (bdrv_write(s->bdrv, page, iobuf, 1) == -1) - printf("%s: write error in sector %i\n", __FUNCTION__, page); + printf("%s: write error in sector %" PRIu64 "\n", __func__, page); } } static void glue(nand_blk_load_, PAGE_SIZE)(NANDFlashState *s, - uint32_t addr, int offset) + uint64_t addr, int offset) { if (PAGE(addr) >= s->pages) return; @@ -624,8 +630,8 @@ static void glue(nand_blk_load_, PAGE_SIZE)(NANDFlashState *s, if (s->bdrv) { if (s->mem_oob) { if (bdrv_read(s->bdrv, SECTOR(addr), s->io, PAGE_SECTORS) == -1) - printf("%s: read error in sector %i\n", - __FUNCTION__, SECTOR(addr)); + printf("%s: read error in sector %" PRIu64 "\n", + __func__, SECTOR(addr)); memcpy(s->io + SECTOR_OFFSET(s->addr) + PAGE_SIZE, s->storage + (PAGE(s->addr) << OOB_SHIFT), OOB_SIZE); @@ -633,8 +639,8 @@ static void glue(nand_blk_load_, PAGE_SIZE)(NANDFlashState *s, } else { if (bdrv_read(s->bdrv, PAGE_START(addr) >> 9, s->io, (PAGE_SECTORS + 2)) == -1) - printf("%s: read error in sector %i\n", - __FUNCTION__, PAGE_START(addr) >> 9); + printf("%s: read error in sector %" PRIu64 "\n", + __func__, PAGE_START(addr) >> 9); s->ioaddr = s->io + (PAGE_START(addr) & 0x1ff) + offset; } } else { From ac2466cdc625d0cf9e7a885b7901084ac59d507f Mon Sep 17 00:00:00 2001 From: Andrzej Zaborowski Date: Sat, 30 Jul 2011 06:01:37 +0200 Subject: [PATCH 123/230] nand: Bump vmstate version after changing structure. Signed-off-by: Andrzej Zaborowski --- hw/nand.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/hw/nand.c b/hw/nand.c index 764356adc4..f5f204a2a7 100644 --- a/hw/nand.c +++ b/hw/nand.c @@ -308,9 +308,9 @@ static int nand_post_load(void *opaque, int version_id) static const VMStateDescription vmstate_nand = { .name = "nand", - .version_id = 0, - .minimum_version_id = 0, - .minimum_version_id_old = 0, + .version_id = 1, + .minimum_version_id = 1, + .minimum_version_id_old = 1, .pre_save = nand_pre_save, .post_load = nand_post_load, .fields = (VMStateField[]) { From 48197dfa6a26fa1807f19f510a2e840bb3885680 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Juha=20Riihim=C3=A4ki?= Date: Fri, 29 Jul 2011 16:35:21 +0100 Subject: [PATCH 124/230] hw/nand: Support devices wider than 8 bits MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Support NAND devices which are wider than 8 bits. Signed-off-by: Juha Riihimäki [Riku Voipio: Fixes and restructuring patchset] Signed-off-by: Riku Voipio [Peter Maydell: More fixes and cleanups for upstream submission] Signed-off-by: Peter Maydell Signed-off-by: Andrzej Zaborowski --- hw/flash.h | 5 ++- hw/nand.c | 117 ++++++++++++++++++++++++++++++++++++++--------------- 2 files changed, 88 insertions(+), 34 deletions(-) diff --git a/hw/flash.h b/hw/flash.h index a992bb8157..132ad299d3 100644 --- a/hw/flash.h +++ b/hw/flash.h @@ -24,8 +24,9 @@ void nand_done(NANDFlashState *s); void nand_setpins(NANDFlashState *s, uint8_t cle, uint8_t ale, uint8_t ce, uint8_t wp, uint8_t gnd); void nand_getpins(NANDFlashState *s, int *rb); -void nand_setio(NANDFlashState *s, uint8_t value); -uint8_t nand_getio(NANDFlashState *s); +void nand_setio(NANDFlashState *s, uint32_t value); +uint32_t nand_getio(NANDFlashState *s); +uint32_t nand_getbuswidth(NANDFlashState *s); #define NAND_MFR_TOSHIBA 0x98 #define NAND_MFR_SAMSUNG 0xec diff --git a/hw/nand.c b/hw/nand.c index f5f204a2a7..4d1dff1664 100644 --- a/hw/nand.c +++ b/hw/nand.c @@ -49,6 +49,7 @@ struct NANDFlashState { uint8_t manf_id, chip_id; + uint8_t buswidth; /* in BYTES */ int size, pages; int page_shift, oob_shift, erase_shift, addr_shift; uint8_t *storage; @@ -215,6 +216,14 @@ static void nand_reset(NANDFlashState *s) s->status &= NAND_IOSTATUS_UNPROTCT; } +static inline void nand_pushio_byte(NANDFlashState *s, uint8_t value) +{ + s->ioaddr[s->iolen++] = value; + for (value = s->buswidth; --value;) { + s->ioaddr[s->iolen++] = 0; + } +} + static void nand_command(NANDFlashState *s) { unsigned int offset; @@ -224,15 +233,19 @@ static void nand_command(NANDFlashState *s) break; case NAND_CMD_READID: - s->io[0] = s->manf_id; - s->io[1] = s->chip_id; - s->io[2] = 'Q'; /* Don't-care byte (often 0xa5) */ - if (nand_flash_ids[s->chip_id].options & NAND_SAMSUNG_LP) - s->io[3] = 0x15; /* Page Size, Block Size, Spare Size.. */ - else - s->io[3] = 0xc0; /* Multi-plane */ s->ioaddr = s->io; - s->iolen = 4; + s->iolen = 0; + nand_pushio_byte(s, s->manf_id); + nand_pushio_byte(s, s->chip_id); + nand_pushio_byte(s, 'Q'); /* Don't-care byte (often 0xa5) */ + if (nand_flash_ids[s->chip_id].options & NAND_SAMSUNG_LP) { + /* Page Size, Block Size, Spare Size; bit 6 indicates + * 8 vs 16 bit width NAND. + */ + nand_pushio_byte(s, (s->buswidth == 2) ? 0x55 : 0x15); + } else { + nand_pushio_byte(s, 0xc0); /* Multi-plane */ + } break; case NAND_CMD_RANDOMREAD2: @@ -277,9 +290,9 @@ static void nand_command(NANDFlashState *s) break; case NAND_CMD_READSTATUS: - s->io[0] = s->status; s->ioaddr = s->io; - s->iolen = 1; + s->iolen = 0; + nand_pushio_byte(s, s->status); break; default: @@ -357,8 +370,10 @@ void nand_getpins(NANDFlashState *s, int *rb) *rb = 1; } -void nand_setio(NANDFlashState *s, uint8_t value) +void nand_setio(NANDFlashState *s, uint32_t value) { + int i; + if (!s->ce && s->cle) { if (nand_flash_ids[s->chip_id].options & NAND_SAMSUNG_LP) { if (s->cmd == NAND_CMD_READ0 && value == NAND_CMD_LPREAD2) @@ -404,36 +419,64 @@ void nand_setio(NANDFlashState *s, uint8_t value) s->addr = (s->addr & mask) | v; s->addrlen ++; - if (s->addrlen == 1 && s->cmd == NAND_CMD_READID) - nand_command(s); - - if (!(nand_flash_ids[s->chip_id].options & NAND_SAMSUNG_LP) && - s->addrlen == 3 && ( - s->cmd == NAND_CMD_READ0 || - s->cmd == NAND_CMD_PAGEPROGRAM1)) - nand_command(s); - if ((nand_flash_ids[s->chip_id].options & NAND_SAMSUNG_LP) && - s->addrlen == 4 && ( - s->cmd == NAND_CMD_READ0 || - s->cmd == NAND_CMD_PAGEPROGRAM1)) - nand_command(s); + switch (s->addrlen) { + case 1: + if (s->cmd == NAND_CMD_READID) { + nand_command(s); + } + break; + case 2: /* fix cache address as a byte address */ + s->addr <<= (s->buswidth - 1); + break; + case 3: + if (!(nand_flash_ids[s->chip_id].options & NAND_SAMSUNG_LP) && + (s->cmd == NAND_CMD_READ0 || + s->cmd == NAND_CMD_PAGEPROGRAM1)) { + nand_command(s); + } + break; + case 4: + if ((nand_flash_ids[s->chip_id].options & NAND_SAMSUNG_LP) && + nand_flash_ids[s->chip_id].size < 256 && /* 1Gb or less */ + (s->cmd == NAND_CMD_READ0 || + s->cmd == NAND_CMD_PAGEPROGRAM1)) { + nand_command(s); + } + break; + case 5: + if ((nand_flash_ids[s->chip_id].options & NAND_SAMSUNG_LP) && + nand_flash_ids[s->chip_id].size >= 256 && /* 2Gb or more */ + (s->cmd == NAND_CMD_READ0 || + s->cmd == NAND_CMD_PAGEPROGRAM1)) { + nand_command(s); + } + break; + default: + break; + } } if (!s->cle && !s->ale && s->cmd == NAND_CMD_PAGEPROGRAM1) { - if (s->iolen < (1 << s->page_shift) + (1 << s->oob_shift)) - s->io[s->iolen ++] = value; + if (s->iolen < (1 << s->page_shift) + (1 << s->oob_shift)) { + for (i = s->buswidth; i--; value >>= 8) { + s->io[s->iolen ++] = (uint8_t) (value & 0xff); + } + } } else if (!s->cle && !s->ale && s->cmd == NAND_CMD_COPYBACKPRG1) { if ((s->addr & ((1 << s->addr_shift) - 1)) < (1 << s->page_shift) + (1 << s->oob_shift)) { - s->io[s->iolen + (s->addr & ((1 << s->addr_shift) - 1))] = value; - s->addr ++; + for (i = s->buswidth; i--; s->addr++, value >>= 8) { + s->io[s->iolen + (s->addr & ((1 << s->addr_shift) - 1))] = + (uint8_t) (value & 0xff); + } } } } -uint8_t nand_getio(NANDFlashState *s) +uint32_t nand_getio(NANDFlashState *s) { int offset; + uint32_t x = 0; /* Allow sequential reading */ if (!s->iolen && s->cmd == NAND_CMD_READ0) { @@ -450,9 +493,18 @@ uint8_t nand_getio(NANDFlashState *s) if (s->ce || s->iolen <= 0) return 0; - s->iolen --; - s->addr++; - return *(s->ioaddr ++); + for (offset = s->buswidth; offset--;) { + x |= s->ioaddr[offset] << (offset << 3); + } + s->addr += s->buswidth; + s->ioaddr += s->buswidth; + s->iolen -= s->buswidth; + return x; +} + +uint32_t nand_getbuswidth(NANDFlashState *s) +{ + return s->buswidth << 3; } NANDFlashState *nand_init(BlockDriverState *bdrv, int manf_id, int chip_id) @@ -468,6 +520,7 @@ NANDFlashState *nand_init(BlockDriverState *bdrv, int manf_id, int chip_id) s->bdrv = bdrv; s->manf_id = manf_id; s->chip_id = chip_id; + s->buswidth = nand_flash_ids[s->chip_id].width >> 3; s->size = nand_flash_ids[s->chip_id].size << 20; if (nand_flash_ids[s->chip_id].options & NAND_SAMSUNG_LP) { s->page_shift = 11; From d72245fbcf3391bfb61ec447dc1888919b3d148b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Juha=20Riihim=C3=A4ki?= Date: Fri, 29 Jul 2011 16:35:22 +0100 Subject: [PATCH 125/230] hw/nand: Support multiple reads following READ STATUS MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit After receiving READ STATUS command all subsequent IO reads should return the status register value until another command is issued. Signed-off-by: Juha Riihimäki [Riku Voipio: Fixes and restructuring patchset] Signed-off-by: Riku Voipio [Peter Maydell: More fixes and cleanups for upstream submission] Signed-off-by: Peter Maydell Signed-off-by: Andrzej Zaborowski --- hw/nand.c | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/hw/nand.c b/hw/nand.c index 4d1dff1664..1eefe28c56 100644 --- a/hw/nand.c +++ b/hw/nand.c @@ -496,9 +496,14 @@ uint32_t nand_getio(NANDFlashState *s) for (offset = s->buswidth; offset--;) { x |= s->ioaddr[offset] << (offset << 3); } - s->addr += s->buswidth; - s->ioaddr += s->buswidth; - s->iolen -= s->buswidth; + /* after receiving READ STATUS command all subsequent reads will + * return the status register value until another command is issued + */ + if (s->cmd != NAND_CMD_READSTATUS) { + s->addr += s->buswidth; + s->ioaddr += s->buswidth; + s->iolen -= s->buswidth; + } return x; } From 89f640bc0405ed1e9c5c5a6cb6c19c8012d11e3f Mon Sep 17 00:00:00 2001 From: Peter Maydell Date: Fri, 29 Jul 2011 16:35:23 +0100 Subject: [PATCH 126/230] hw/nand: Writing to NAND can only clear bits Writing to a NAND device cannot set bits, it can only clear them; implement this rather than simply copying the data. Signed-off-by: Peter Maydell Signed-off-by: Andrzej Zaborowski --- hw/nand.c | 17 +++++++++++++---- 1 file changed, 13 insertions(+), 4 deletions(-) diff --git a/hw/nand.c b/hw/nand.c index 1eefe28c56..35804c7e87 100644 --- a/hw/nand.c +++ b/hw/nand.c @@ -75,6 +75,15 @@ struct NANDFlashState { uint32_t ioaddr_vmstate; }; +static void mem_and(uint8_t *dest, const uint8_t *src, size_t n) +{ + /* Like memcpy() but we logical-AND the data into the destination */ + int i; + for (i = 0; i < n; i++) { + dest[i] &= src[i]; + } +} + # define NAND_NO_AUTOINCR 0x00000001 # define NAND_BUSWIDTH_16 0x00000002 # define NAND_NO_PADDING 0x00000004 @@ -595,7 +604,7 @@ static void glue(nand_blk_write_, PAGE_SIZE)(NANDFlashState *s) return; if (!s->bdrv) { - memcpy(s->storage + PAGE_START(s->addr) + (s->addr & PAGE_MASK) + + mem_and(s->storage + PAGE_START(s->addr) + (s->addr & PAGE_MASK) + s->offset, s->io, s->iolen); } else if (s->mem_oob) { sector = SECTOR(s->addr); @@ -606,10 +615,10 @@ static void glue(nand_blk_write_, PAGE_SIZE)(NANDFlashState *s) return; } - memcpy(iobuf + (soff | off), s->io, MIN(s->iolen, PAGE_SIZE - off)); + mem_and(iobuf + (soff | off), s->io, MIN(s->iolen, PAGE_SIZE - off)); if (off + s->iolen > PAGE_SIZE) { page = PAGE(s->addr); - memcpy(s->storage + (page << OOB_SHIFT), s->io + PAGE_SIZE - off, + mem_and(s->storage + (page << OOB_SHIFT), s->io + PAGE_SIZE - off, MIN(OOB_SIZE, off + s->iolen - PAGE_SIZE)); } @@ -624,7 +633,7 @@ static void glue(nand_blk_write_, PAGE_SIZE)(NANDFlashState *s) return; } - memcpy(iobuf + soff, s->io, s->iolen); + mem_and(iobuf + soff, s->io, s->iolen); if (bdrv_write(s->bdrv, sector, iobuf, PAGE_SECTORS + 2) == -1) printf("%s: write error in sector %" PRIu64 "\n", __func__, sector); From d4220389ffcc1e6302e759d3b15f8605201d6369 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Juha=20Riihim=C3=A4ki?= Date: Fri, 29 Jul 2011 16:35:24 +0100 Subject: [PATCH 127/230] hw/nand: qdevify MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Qdevify the NAND device. Signed-off-by: Juha Riihimäki [Riku Voipio: Fixes and restructuring patchset] Signed-off-by: Riku Voipio [Peter Maydell: More fixes and cleanups for upstream submission] Signed-off-by: Peter Maydell Signed-off-by: Andrzej Zaborowski --- hw/axis_dev88.c | 2 +- hw/flash.h | 14 ++--- hw/nand.c | 164 +++++++++++++++++++++++++++--------------------- hw/spitz.c | 2 +- hw/tc6393xb.c | 2 +- 5 files changed, 103 insertions(+), 81 deletions(-) diff --git a/hw/axis_dev88.c b/hw/axis_dev88.c index de1f5a5fce..e0a8c14c12 100644 --- a/hw/axis_dev88.c +++ b/hw/axis_dev88.c @@ -37,7 +37,7 @@ struct nand_state_t { - NANDFlashState *nand; + DeviceState *nand; unsigned int rdy:1; unsigned int ale:1; unsigned int cle:1; diff --git a/hw/flash.h b/hw/flash.h index 132ad299d3..43260ce590 100644 --- a/hw/flash.h +++ b/hw/flash.h @@ -18,15 +18,13 @@ pflash_t *pflash_cfi02_register(target_phys_addr_t base, ram_addr_t off, int be); /* nand.c */ -typedef struct NANDFlashState NANDFlashState; -NANDFlashState *nand_init(BlockDriverState *bdrv, int manf_id, int chip_id); -void nand_done(NANDFlashState *s); -void nand_setpins(NANDFlashState *s, uint8_t cle, uint8_t ale, +DeviceState *nand_init(BlockDriverState *bdrv, int manf_id, int chip_id); +void nand_setpins(DeviceState *dev, uint8_t cle, uint8_t ale, uint8_t ce, uint8_t wp, uint8_t gnd); -void nand_getpins(NANDFlashState *s, int *rb); -void nand_setio(NANDFlashState *s, uint32_t value); -uint32_t nand_getio(NANDFlashState *s); -uint32_t nand_getbuswidth(NANDFlashState *s); +void nand_getpins(DeviceState *dev, int *rb); +void nand_setio(DeviceState *dev, uint32_t value); +uint32_t nand_getio(DeviceState *dev); +uint32_t nand_getbuswidth(DeviceState *dev); #define NAND_MFR_TOSHIBA 0x98 #define NAND_MFR_SAMSUNG 0xec diff --git a/hw/nand.c b/hw/nand.c index 35804c7e87..28d9f0b60d 100644 --- a/hw/nand.c +++ b/hw/nand.c @@ -18,6 +18,7 @@ # include "hw.h" # include "flash.h" # include "blockdev.h" +# include "sysbus.h" # define NAND_CMD_READ0 0x00 # define NAND_CMD_READ1 0x01 @@ -47,7 +48,9 @@ # define MAX_PAGE 0x800 # define MAX_OOB 0x40 +typedef struct NANDFlashState NANDFlashState; struct NANDFlashState { + SysBusDevice busdev; uint8_t manf_id, chip_id; uint8_t buswidth; /* in BYTES */ int size, pages; @@ -215,8 +218,9 @@ static const struct { [0xc5] = { 2048, 16, 0, 0, LP_OPTIONS16 }, }; -static void nand_reset(NANDFlashState *s) +static void nand_reset(DeviceState *dev) { + NANDFlashState *s = FROM_SYSBUS(NANDFlashState, sysbus_from_qdev(dev)); s->cmd = NAND_CMD_READ0; s->addr = 0; s->addrlen = 0; @@ -270,7 +274,7 @@ static void nand_command(NANDFlashState *s) break; case NAND_CMD_RESET: - nand_reset(s); + nand_reset(&s->busdev.qdev); break; case NAND_CMD_PAGEPROGRAM1: @@ -354,15 +358,85 @@ static const VMStateDescription vmstate_nand = { } }; +static int nand_device_init(SysBusDevice *dev) +{ + int pagesize; + NANDFlashState *s = FROM_SYSBUS(NANDFlashState, dev); + + s->buswidth = nand_flash_ids[s->chip_id].width >> 3; + s->size = nand_flash_ids[s->chip_id].size << 20; + if (nand_flash_ids[s->chip_id].options & NAND_SAMSUNG_LP) { + s->page_shift = 11; + s->erase_shift = 6; + } else { + s->page_shift = nand_flash_ids[s->chip_id].page_shift; + s->erase_shift = nand_flash_ids[s->chip_id].erase_shift; + } + + switch (1 << s->page_shift) { + case 256: + nand_init_256(s); + break; + case 512: + nand_init_512(s); + break; + case 2048: + nand_init_2048(s); + break; + default: + hw_error("%s: Unsupported NAND block size.\n", __func__); + } + + pagesize = 1 << s->oob_shift; + s->mem_oob = 1; + if (s->bdrv && bdrv_getlength(s->bdrv) >= + (s->pages << s->page_shift) + (s->pages << s->oob_shift)) { + pagesize = 0; + s->mem_oob = 0; + } + + if (!s->bdrv) { + pagesize += 1 << s->page_shift; + } + if (pagesize) { + s->storage = (uint8_t *) memset(qemu_malloc(s->pages * pagesize), + 0xff, s->pages * pagesize); + } + /* Give s->ioaddr a sane value in case we save state before it is used. */ + s->ioaddr = s->io; + + return 0; +} + +static SysBusDeviceInfo nand_info = { + .init = nand_device_init, + .qdev.name = "nand", + .qdev.size = sizeof(NANDFlashState), + .qdev.reset = nand_reset, + .qdev.vmsd = &vmstate_nand, + .qdev.props = (Property[]) { + DEFINE_PROP_UINT8("manufacturer_id", NANDFlashState, manf_id, 0), + DEFINE_PROP_UINT8("chip_id", NANDFlashState, chip_id, 0), + DEFINE_PROP_DRIVE("drive", NANDFlashState, bdrv), + DEFINE_PROP_END_OF_LIST() + } +}; + +static void nand_create_device(void) +{ + sysbus_register_withprop(&nand_info); +} + /* * Chip inputs are CLE, ALE, CE, WP, GND and eight I/O pins. Chip * outputs are R/B and eight I/O pins. * * CE, WP and R/B are active low. */ -void nand_setpins(NANDFlashState *s, uint8_t cle, uint8_t ale, +void nand_setpins(DeviceState *dev, uint8_t cle, uint8_t ale, uint8_t ce, uint8_t wp, uint8_t gnd) { + NANDFlashState *s = (NANDFlashState *) dev; s->cle = cle; s->ale = ale; s->ce = ce; @@ -374,15 +448,15 @@ void nand_setpins(NANDFlashState *s, uint8_t cle, uint8_t ale, s->status &= ~NAND_IOSTATUS_UNPROTCT; } -void nand_getpins(NANDFlashState *s, int *rb) +void nand_getpins(DeviceState *dev, int *rb) { *rb = 1; } -void nand_setio(NANDFlashState *s, uint32_t value) +void nand_setio(DeviceState *dev, uint32_t value) { int i; - + NANDFlashState *s = (NANDFlashState *) dev; if (!s->ce && s->cle) { if (nand_flash_ids[s->chip_id].options & NAND_SAMSUNG_LP) { if (s->cmd == NAND_CMD_READ0 && value == NAND_CMD_LPREAD2) @@ -482,10 +556,11 @@ void nand_setio(NANDFlashState *s, uint32_t value) } } -uint32_t nand_getio(NANDFlashState *s) +uint32_t nand_getio(DeviceState *dev) { int offset; uint32_t x = 0; + NANDFlashState *s = (NANDFlashState *) dev; /* Allow sequential reading */ if (!s->iolen && s->cmd == NAND_CMD_READ0) { @@ -516,82 +591,31 @@ uint32_t nand_getio(NANDFlashState *s) return x; } -uint32_t nand_getbuswidth(NANDFlashState *s) +uint32_t nand_getbuswidth(DeviceState *dev) { + NANDFlashState *s = (NANDFlashState *) dev; return s->buswidth << 3; } -NANDFlashState *nand_init(BlockDriverState *bdrv, int manf_id, int chip_id) +DeviceState *nand_init(BlockDriverState *bdrv, int manf_id, int chip_id) { - int pagesize; - NANDFlashState *s; + DeviceState *dev; if (nand_flash_ids[chip_id].size == 0) { hw_error("%s: Unsupported NAND chip ID.\n", __FUNCTION__); } - - s = (NANDFlashState *) qemu_mallocz(sizeof(NANDFlashState)); - s->bdrv = bdrv; - s->manf_id = manf_id; - s->chip_id = chip_id; - s->buswidth = nand_flash_ids[s->chip_id].width >> 3; - s->size = nand_flash_ids[s->chip_id].size << 20; - if (nand_flash_ids[s->chip_id].options & NAND_SAMSUNG_LP) { - s->page_shift = 11; - s->erase_shift = 6; - } else { - s->page_shift = nand_flash_ids[s->chip_id].page_shift; - s->erase_shift = nand_flash_ids[s->chip_id].erase_shift; + dev = qdev_create(NULL, "nand"); + qdev_prop_set_uint8(dev, "manufacturer_id", manf_id); + qdev_prop_set_uint8(dev, "chip_id", chip_id); + if (bdrv) { + qdev_prop_set_drive_nofail(dev, "drive", bdrv); } - switch (1 << s->page_shift) { - case 256: - nand_init_256(s); - break; - case 512: - nand_init_512(s); - break; - case 2048: - nand_init_2048(s); - break; - default: - hw_error("%s: Unsupported NAND block size.\n", __FUNCTION__); - } - - pagesize = 1 << s->oob_shift; - s->mem_oob = 1; - if (s->bdrv && bdrv_getlength(s->bdrv) >= - (s->pages << s->page_shift) + (s->pages << s->oob_shift)) { - pagesize = 0; - s->mem_oob = 0; - } - - if (!s->bdrv) - pagesize += 1 << s->page_shift; - if (pagesize) - s->storage = (uint8_t *) memset(qemu_malloc(s->pages * pagesize), - 0xff, s->pages * pagesize); - /* Give s->ioaddr a sane value in case we save state before it - is used. */ - s->ioaddr = s->io; - - vmstate_register(NULL, -1, &vmstate_nand, s); - - return s; + qdev_init_nofail(dev); + return dev; } -void nand_done(NANDFlashState *s) -{ - if (s->bdrv) { - bdrv_close(s->bdrv); - bdrv_delete(s->bdrv); - } - - if (!s->bdrv || s->mem_oob) - qemu_free(s->storage); - - qemu_free(s); -} +device_init(nand_create_device) #else diff --git a/hw/spitz.c b/hw/spitz.c index 78e9c34592..c05b5f7d56 100644 --- a/hw/spitz.c +++ b/hw/spitz.c @@ -48,7 +48,7 @@ typedef struct { SysBusDevice busdev; - NANDFlashState *nand; + DeviceState *nand; uint8_t ctl; uint8_t manf_id; uint8_t chip_id; diff --git a/hw/tc6393xb.c b/hw/tc6393xb.c index 4de081966b..a1c48bf1d9 100644 --- a/hw/tc6393xb.c +++ b/hw/tc6393xb.c @@ -118,7 +118,7 @@ struct TC6393xbState { } nand; int nand_enable; uint32_t nand_phys; - NANDFlashState *flash; + DeviceState *flash; ECCState ecc; DisplayState *ds; From af5a75f41c2fd172ceaa1cabd4bec99de8dde83a Mon Sep 17 00:00:00 2001 From: Peter Maydell Date: Fri, 29 Jul 2011 16:35:25 +0100 Subject: [PATCH 128/230] onenand: Pass BlockDriverState to init function Pass the BlockDriverState to the onenand init function so it doesn't need to look up the drive itself. Signed-off-by: Peter Maydell Signed-off-by: Andrzej Zaborowski --- hw/flash.h | 3 ++- hw/nseries.c | 10 ++++++---- hw/onenand.c | 9 ++++----- 3 files changed, 12 insertions(+), 10 deletions(-) diff --git a/hw/flash.h b/hw/flash.h index 43260ce590..d61647f02a 100644 --- a/hw/flash.h +++ b/hw/flash.h @@ -38,7 +38,8 @@ uint32_t nand_getbuswidth(DeviceState *dev); /* onenand.c */ void onenand_base_update(void *opaque, target_phys_addr_t new); void onenand_base_unmap(void *opaque); -void *onenand_init(uint32_t id, int regshift, qemu_irq irq); +void *onenand_init(BlockDriverState *bdrv, uint32_t id, + int regshift, qemu_irq irq); void *onenand_raw_otp(void *opaque); /* ecc.c */ diff --git a/hw/nseries.c b/hw/nseries.c index d12ed46461..be50a5cfc6 100644 --- a/hw/nseries.c +++ b/hw/nseries.c @@ -31,6 +31,7 @@ #include "hw.h" #include "bt.h" #include "loader.h" +#include "blockdev.h" /* Nokia N8x0 support */ struct n800_s { @@ -163,13 +164,14 @@ static const uint8_t n8x0_cal_bt_id[] = { static void n8x0_nand_setup(struct n800_s *s) { char *otp_region; + DriveInfo *dinfo; + dinfo = drive_get(IF_MTD, 0, 0); /* Either ec40xx or ec48xx are OK for the ID */ + s->nand = onenand_init(dinfo ? dinfo->bdrv : 0, 0xec4800, 1, + qdev_get_gpio_in(s->cpu->gpio, N8X0_ONENAND_GPIO)); omap_gpmc_attach(s->cpu->gpmc, N8X0_ONENAND_CS, 0, onenand_base_update, - onenand_base_unmap, - (s->nand = onenand_init(0xec4800, 1, - qdev_get_gpio_in(s->cpu->gpio, - N8X0_ONENAND_GPIO)))); + onenand_base_unmap, s->nand); otp_region = onenand_raw_otp(s->nand); memcpy(otp_region + 0x000, n8x0_cal_wlan_mac, sizeof(n8x0_cal_wlan_mac)); diff --git a/hw/onenand.c b/hw/onenand.c index 71c1ab40b4..942b69a194 100644 --- a/hw/onenand.c +++ b/hw/onenand.c @@ -615,10 +615,10 @@ static CPUWriteMemoryFunc * const onenand_writefn[] = { onenand_write, }; -void *onenand_init(uint32_t id, int regshift, qemu_irq irq) +void *onenand_init(BlockDriverState *bdrv, uint32_t id, + int regshift, qemu_irq irq) { OneNANDState *s = (OneNANDState *) qemu_mallocz(sizeof(*s)); - DriveInfo *dinfo = drive_get(IF_MTD, 0, 0); uint32_t size = 1 << (24 + ((id >> 12) & 7)); void *ram; @@ -632,11 +632,10 @@ void *onenand_init(uint32_t id, int regshift, qemu_irq irq) s->density_mask = (id & (1 << 11)) ? (1 << (6 + ((id >> 12) & 7))) : 0; s->iomemtype = cpu_register_io_memory(onenand_readfn, onenand_writefn, s, DEVICE_NATIVE_ENDIAN); - if (!dinfo) + s->bdrv = bdrv; + if (!s->bdrv) { s->image = memset(qemu_malloc(size + (size >> 5)), 0xff, size + (size >> 5)); - else - s->bdrv = dinfo->bdrv; s->otp = memset(qemu_malloc((64 + 2) << PAGE_SHIFT), 0xff, (64 + 2) << PAGE_SHIFT); s->ram = qemu_ram_alloc(NULL, "onenand.ram", 0xc000 << s->shift); From 5923ba424b4754a60ea5f6dc7777684e018648e6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Juha=20Riihim=C3=A4ki?= Date: Fri, 29 Jul 2011 16:35:26 +0100 Subject: [PATCH 129/230] onenand: Handle various ID fields separately MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Handle the manufacturer, device and version IDs separately rather than smooshing them all together into a single uint32_t. Note that the ID registers are actually 16 bit, even though typically the top bits are 0 and the Read Identification Data command only returns the bottom 8 bits. Signed-off-by: Juha Riihimäki [Riku Voipio: Fixes and restructuring patchset] Signed-off-by: Riku Voipio [Peter Maydell: More fixes and cleanups for upstream submission] Signed-off-by: Peter Maydell Signed-off-by: Andrzej Zaborowski --- hw/flash.h | 3 ++- hw/nseries.c | 5 +++-- hw/onenand.c | 29 ++++++++++++++++++----------- 3 files changed, 23 insertions(+), 14 deletions(-) diff --git a/hw/flash.h b/hw/flash.h index d61647f02a..140ae39801 100644 --- a/hw/flash.h +++ b/hw/flash.h @@ -38,7 +38,8 @@ uint32_t nand_getbuswidth(DeviceState *dev); /* onenand.c */ void onenand_base_update(void *opaque, target_phys_addr_t new); void onenand_base_unmap(void *opaque); -void *onenand_init(BlockDriverState *bdrv, uint32_t id, +void *onenand_init(BlockDriverState *bdrv, + uint16_t man_id, uint16_t dev_id, uint16_t ver_id, int regshift, qemu_irq irq); void *onenand_raw_otp(void *opaque); diff --git a/hw/nseries.c b/hw/nseries.c index be50a5cfc6..6a5575e78e 100644 --- a/hw/nseries.c +++ b/hw/nseries.c @@ -167,8 +167,9 @@ static void n8x0_nand_setup(struct n800_s *s) DriveInfo *dinfo; dinfo = drive_get(IF_MTD, 0, 0); - /* Either ec40xx or ec48xx are OK for the ID */ - s->nand = onenand_init(dinfo ? dinfo->bdrv : 0, 0xec4800, 1, + /* Either 0x40 or 0x48 are OK for the device ID */ + s->nand = onenand_init(dinfo ? dinfo->bdrv : 0, + NAND_MFR_SAMSUNG, 0x48, 0, 1, qdev_get_gpio_in(s->cpu->gpio, N8X0_ONENAND_GPIO)); omap_gpmc_attach(s->cpu->gpmc, N8X0_ONENAND_CS, 0, onenand_base_update, onenand_base_unmap, s->nand); diff --git a/hw/onenand.c b/hw/onenand.c index 942b69a194..d87079efdb 100644 --- a/hw/onenand.c +++ b/hw/onenand.c @@ -31,7 +31,11 @@ #define BLOCK_SHIFT (PAGE_SHIFT + 6) typedef struct { - uint32_t id; + struct { + uint16_t man; + uint16_t dev; + uint16_t ver; + } id; int shift; target_phys_addr_t base; qemu_irq intr; @@ -453,12 +457,12 @@ static uint32_t onenand_read(void *opaque, target_phys_addr_t addr) return lduw_le_p(s->boot[0] + addr); case 0xf000: /* Manufacturer ID */ - return (s->id >> 16) & 0xff; + return s->id.man; case 0xf001: /* Device ID */ - return (s->id >> 8) & 0xff; - /* TODO: get the following values from a real chip! */ + return s->id.dev; case 0xf002: /* Version ID */ - return (s->id >> 0) & 0xff; + return s->id.ver; + /* TODO: get the following values from a real chip! */ case 0xf003: /* Data Buffer size */ return 1 << PAGE_SHIFT; case 0xf004: /* Boot Buffer size */ @@ -541,8 +545,8 @@ static void onenand_write(void *opaque, target_phys_addr_t addr, case 0x0090: /* Read Identification Data */ memset(s->boot[0], 0, 3 << s->shift); - s->boot[0][0 << s->shift] = (s->id >> 16) & 0xff; - s->boot[0][1 << s->shift] = (s->id >> 8) & 0xff; + s->boot[0][0 << s->shift] = s->id.man & 0xff; + s->boot[0][1 << s->shift] = s->id.dev & 0xff; s->boot[0][2 << s->shift] = s->wpstatus & 0xff; break; @@ -615,21 +619,24 @@ static CPUWriteMemoryFunc * const onenand_writefn[] = { onenand_write, }; -void *onenand_init(BlockDriverState *bdrv, uint32_t id, +void *onenand_init(BlockDriverState *bdrv, + uint16_t man_id, uint16_t dev_id, uint16_t ver_id, int regshift, qemu_irq irq) { OneNANDState *s = (OneNANDState *) qemu_mallocz(sizeof(*s)); - uint32_t size = 1 << (24 + ((id >> 12) & 7)); + uint32_t size = 1 << (24 + ((dev_id >> 4) & 7)); void *ram; s->shift = regshift; s->intr = irq; s->rdy = NULL; - s->id = id; + s->id.man = man_id; + s->id.dev = dev_id; + s->id.ver = ver_id; s->blocks = size >> BLOCK_SHIFT; s->secs = size >> 9; s->blockwp = qemu_malloc(s->blocks); - s->density_mask = (id & (1 << 11)) ? (1 << (6 + ((id >> 12) & 7))) : 0; + s->density_mask = (dev_id & 0x08) ? (1 << (6 + ((dev_id >> 4) & 7))) : 0; s->iomemtype = cpu_register_io_memory(onenand_readfn, onenand_writefn, s, DEVICE_NATIVE_ENDIAN); s->bdrv = bdrv; From f1588dd26c25bd7590e18a0cce59a5fa82323ce5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Juha=20Riihim=C3=A4ki?= Date: Fri, 29 Jul 2011 16:35:28 +0100 Subject: [PATCH 130/230] hw/onenand: program actions can only clear bits MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The program actions onenand_prog_main() and onenand_prog_spare() can only set bits. This implies a rewrite of onenand_erase() to not use the program functions, since erase does need to set bits. Signed-off-by: Juha Riihimäki [Riku Voipio: Fixes and restructuring patchset] Signed-off-by: Riku Voipio [Peter Maydell: More fixes and cleanups for upstream submission] Signed-off-by: Peter Maydell Signed-off-by: Andrzej Zaborowski --- hw/onenand.c | 133 ++++++++++++++++++++++++++++++++++++++++----------- 1 file changed, 105 insertions(+), 28 deletions(-) diff --git a/hw/onenand.c b/hw/onenand.c index d87079efdb..e8d1d4b1a5 100644 --- a/hw/onenand.c +++ b/hw/onenand.c @@ -179,14 +179,39 @@ static inline int onenand_load_main(OneNANDState *s, int sec, int secn, static inline int onenand_prog_main(OneNANDState *s, int sec, int secn, void *src) { - if (s->bdrv_cur) - return bdrv_write(s->bdrv_cur, sec, src, secn) < 0; - else if (sec + secn > s->secs_cur) - return 1; + int result = 0; - memcpy(s->current + (sec << 9), src, secn << 9); + if (secn > 0) { + uint32_t size = (uint32_t) secn * 512; + const uint8_t *sp = (const uint8_t *) src; + uint8_t *dp = 0; + if (s->bdrv_cur) { + dp = qemu_malloc(size); + if (!dp || bdrv_read(s->bdrv_cur, sec, dp, secn) < 0) { + result = 1; + } + } else { + if (sec + secn > s->secs_cur) { + result = 1; + } else { + dp = (uint8_t *) s->current + (sec << 9); + } + } + if (!result) { + uint32_t i; + for (i = 0; i < size; i++) { + dp[i] &= sp[i]; + } + if (s->bdrv_cur) { + result = bdrv_write(s->bdrv_cur, sec, dp, secn) < 0; + } + } + if (dp && s->bdrv_cur) { + qemu_free(dp); + } + } - return 0; + return result; } static inline int onenand_load_spare(OneNANDState *s, int sec, int secn, @@ -209,35 +234,87 @@ static inline int onenand_load_spare(OneNANDState *s, int sec, int secn, static inline int onenand_prog_spare(OneNANDState *s, int sec, int secn, void *src) { - uint8_t buf[512]; - - if (s->bdrv_cur) { - if (bdrv_read(s->bdrv_cur, s->secs_cur + (sec >> 5), buf, 1) < 0) - return 1; - memcpy(buf + ((sec & 31) << 4), src, secn << 4); - return bdrv_write(s->bdrv_cur, s->secs_cur + (sec >> 5), buf, 1) < 0; - } else if (sec + secn > s->secs_cur) - return 1; - - memcpy(s->current + (s->secs_cur << 9) + (sec << 4), src, secn << 4); - - return 0; + int result = 0; + if (secn > 0) { + const uint8_t *sp = (const uint8_t *) src; + uint8_t *dp = 0, *dpp = 0; + if (s->bdrv_cur) { + dp = qemu_malloc(512); + if (!dp || bdrv_read(s->bdrv_cur, + s->secs_cur + (sec >> 5), + dp, 1) < 0) { + result = 1; + } else { + dpp = dp + ((sec & 31) << 4); + } + } else { + if (sec + secn > s->secs_cur) { + result = 1; + } else { + dpp = s->current + (s->secs_cur << 9) + (sec << 4); + } + } + if (!result) { + uint32_t i; + for (i = 0; i < (secn << 4); i++) { + dpp[i] &= sp[i]; + } + if (s->bdrv_cur) { + result = bdrv_write(s->bdrv_cur, s->secs_cur + (sec >> 5), + dp, 1) < 0; + } + } + if (dp) { + qemu_free(dp); + } + } + return result; } static inline int onenand_erase(OneNANDState *s, int sec, int num) { - /* TODO: optimise */ - uint8_t buf[512]; - - memset(buf, 0xff, sizeof(buf)); - for (; num > 0; num --, sec ++) { - if (onenand_prog_main(s, sec, 1, buf)) - return 1; - if (onenand_prog_spare(s, sec, 1, buf)) - return 1; + uint8_t *blankbuf, *tmpbuf; + blankbuf = qemu_malloc(512); + if (!blankbuf) { + return 1; + } + tmpbuf = qemu_malloc(512); + if (!tmpbuf) { + qemu_free(blankbuf); + return 1; + } + memset(blankbuf, 0xff, 512); + for (; num > 0; num--, sec++) { + if (s->bdrv_cur) { + int erasesec = s->secs_cur + (sec >> 5); + if (bdrv_write(s->bdrv_cur, sec, blankbuf, 1)) { + goto fail; + } + if (bdrv_read(s->bdrv_cur, erasesec, tmpbuf, 1) < 0) { + goto fail; + } + memcpy(tmpbuf + ((sec & 31) << 4), blankbuf, 1 << 4); + if (bdrv_write(s->bdrv_cur, erasesec, tmpbuf, 1) < 0) { + goto fail; + } + } else { + if (sec + 1 > s->secs_cur) { + goto fail; + } + memcpy(s->current + (sec << 9), blankbuf, 512); + memcpy(s->current + (s->secs_cur << 9) + (sec << 4), + blankbuf, 1 << 4); + } } + qemu_free(tmpbuf); + qemu_free(blankbuf); return 0; + +fail: + qemu_free(tmpbuf); + qemu_free(blankbuf); + return 1; } static void onenand_command(OneNANDState *s, int cmd) From 63efb1d9c4140108ab57e706fa7a90a21e07cfcc Mon Sep 17 00:00:00 2001 From: Andrzej Zaborowski Date: Sat, 30 Jul 2011 06:53:39 +0200 Subject: [PATCH 131/230] onenand: Add missing brace. Signed-off-by: Andrzej Zaborowski --- hw/onenand.c | 1 + 1 file changed, 1 insertion(+) diff --git a/hw/onenand.c b/hw/onenand.c index e8d1d4b1a5..b0cbebc178 100644 --- a/hw/onenand.c +++ b/hw/onenand.c @@ -720,6 +720,7 @@ void *onenand_init(BlockDriverState *bdrv, if (!s->bdrv) { s->image = memset(qemu_malloc(size + (size >> 5)), 0xff, size + (size >> 5)); + } s->otp = memset(qemu_malloc((64 + 2) << PAGE_SHIFT), 0xff, (64 + 2) << PAGE_SHIFT); s->ram = qemu_ram_alloc(NULL, "onenand.ram", 0xc000 << s->shift); From 3bf11207c0676cfd29a3c76c6709fdf9a983c0c8 Mon Sep 17 00:00:00 2001 From: Vasily Khoruzhick Date: Wed, 6 Jul 2011 16:52:49 +0300 Subject: [PATCH 132/230] Add support for Zipit Z2 machine Zipit Z2 is small PXA270 based handheld. Signed-off-by: Vasily Khoruzhick Reviewed-by: Peter Maydell Signed-off-by: Andrzej Zaborowski --- Makefile.target | 1 + hw/z2.c | 358 ++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 359 insertions(+) create mode 100644 hw/z2.c diff --git a/Makefile.target b/Makefile.target index 8884a56aa3..d4ea0428fb 100644 --- a/Makefile.target +++ b/Makefile.target @@ -352,6 +352,7 @@ obj-arm-y += omap2.o omap_dss.o soc_dma.o omap_gptimer.o omap_synctimer.o \ obj-arm-y += omap_sx1.o palm.o tsc210x.o obj-arm-y += nseries.o blizzard.o onenand.o vga.o cbus.o tusb6010.o usb-musb.o obj-arm-y += mst_fpga.o mainstone.o +obj-arm-y += z2.o obj-arm-y += musicpal.o bitbang_i2c.o marvell_88w8618_audio.o obj-arm-y += framebuffer.o obj-arm-y += syborg.o syborg_fb.o syborg_interrupt.o syborg_keyboard.o diff --git a/hw/z2.c b/hw/z2.c new file mode 100644 index 0000000000..f93a1bf0fe --- /dev/null +++ b/hw/z2.c @@ -0,0 +1,358 @@ +/* + * PXA270-based Zipit Z2 device + * + * Copyright (c) 2011 by Vasily Khoruzhick + * + * Code is based on mainstone platform. + * + * This code is licensed under the GNU GPL v2. + */ + +#include "hw.h" +#include "pxa.h" +#include "arm-misc.h" +#include "devices.h" +#include "i2c.h" +#include "ssi.h" +#include "boards.h" +#include "sysemu.h" +#include "flash.h" +#include "blockdev.h" +#include "console.h" +#include "audio/audio.h" + +#ifdef DEBUG_Z2 +#define DPRINTF(fmt, ...) \ + printf(fmt, ## __VA_ARGS__) +#else +#define DPRINTF(fmt, ...) +#endif + +static struct keymap map[0x100] = { + [0 ... 0xff] = { -1, -1 }, + [0x3b] = {0, 0}, /* Option = F1 */ + [0xc8] = {0, 1}, /* Up */ + [0xd0] = {0, 2}, /* Down */ + [0xcb] = {0, 3}, /* Left */ + [0xcd] = {0, 4}, /* Right */ + [0xcf] = {0, 5}, /* End */ + [0x0d] = {0, 6}, /* KPPLUS */ + [0xc7] = {1, 0}, /* Home */ + [0x10] = {1, 1}, /* Q */ + [0x17] = {1, 2}, /* I */ + [0x22] = {1, 3}, /* G */ + [0x2d] = {1, 4}, /* X */ + [0x1c] = {1, 5}, /* Enter */ + [0x0c] = {1, 6}, /* KPMINUS */ + [0xc9] = {2, 0}, /* PageUp */ + [0x11] = {2, 1}, /* W */ + [0x18] = {2, 2}, /* O */ + [0x23] = {2, 3}, /* H */ + [0x2e] = {2, 4}, /* C */ + [0x38] = {2, 5}, /* LeftAlt */ + [0xd1] = {3, 0}, /* PageDown */ + [0x12] = {3, 1}, /* E */ + [0x19] = {3, 2}, /* P */ + [0x24] = {3, 3}, /* J */ + [0x2f] = {3, 4}, /* V */ + [0x2a] = {3, 5}, /* LeftShift */ + [0x01] = {4, 0}, /* Esc */ + [0x13] = {4, 1}, /* R */ + [0x1e] = {4, 2}, /* A */ + [0x25] = {4, 3}, /* K */ + [0x30] = {4, 4}, /* B */ + [0x1d] = {4, 5}, /* LeftCtrl */ + [0x0f] = {5, 0}, /* Tab */ + [0x14] = {5, 1}, /* T */ + [0x1f] = {5, 2}, /* S */ + [0x26] = {5, 3}, /* L */ + [0x31] = {5, 4}, /* N */ + [0x39] = {5, 5}, /* Space */ + [0x3c] = {6, 0}, /* Stop = F2 */ + [0x15] = {6, 1}, /* Y */ + [0x20] = {6, 2}, /* D */ + [0x0e] = {6, 3}, /* Backspace */ + [0x32] = {6, 4}, /* M */ + [0x33] = {6, 5}, /* Comma */ + [0x3d] = {7, 0}, /* Play = F3 */ + [0x16] = {7, 1}, /* U */ + [0x21] = {7, 2}, /* F */ + [0x2c] = {7, 3}, /* Z */ + [0x27] = {7, 4}, /* Semicolon */ + [0x34] = {7, 5}, /* Dot */ +}; + +#define Z2_RAM_SIZE 0x02000000 +#define Z2_FLASH_BASE 0x00000000 +#define Z2_FLASH_SIZE 0x00800000 + +static struct arm_boot_info z2_binfo = { + .loader_start = PXA2XX_SDRAM_BASE, + .ram_size = Z2_RAM_SIZE, +}; + +#define Z2_GPIO_SD_DETECT 96 +#define Z2_GPIO_AC_IN 0 +#define Z2_GPIO_KEY_ON 1 +#define Z2_GPIO_LCD_CS 88 + +typedef struct { + SSISlave ssidev; + int32_t selected; + int32_t enabled; + uint8_t buf[3]; + uint32_t cur_reg; + int pos; +} ZipitLCD; + +static uint32_t zipit_lcd_transfer(SSISlave *dev, uint32_t value) +{ + ZipitLCD *z = FROM_SSI_SLAVE(ZipitLCD, dev); + uint16_t val; + if (z->selected) { + z->buf[z->pos] = value & 0xff; + z->pos++; + } + if (z->pos == 3) { + switch (z->buf[0]) { + case 0x74: + DPRINTF("%s: reg: 0x%.2x\n", __func__, z->buf[2]); + z->cur_reg = z->buf[2]; + break; + case 0x76: + val = z->buf[1] << 8 | z->buf[2]; + DPRINTF("%s: value: 0x%.4x\n", __func__, val); + if (z->cur_reg == 0x22 && val == 0x0000) { + z->enabled = 1; + printf("%s: LCD enabled\n", __func__); + } else if (z->cur_reg == 0x10 && val == 0x0000) { + z->enabled = 0; + printf("%s: LCD disabled\n", __func__); + } + break; + default: + DPRINTF("%s: unknown command!\n", __func__); + break; + } + z->pos = 0; + } + return 0; +} + +static void z2_lcd_cs(void *opaque, int line, int level) +{ + ZipitLCD *z2_lcd = opaque; + z2_lcd->selected = !level; +} + +static int zipit_lcd_init(SSISlave *dev) +{ + ZipitLCD *z = FROM_SSI_SLAVE(ZipitLCD, dev); + z->selected = 0; + z->enabled = 0; + z->pos = 0; + + return 0; +} + +static VMStateDescription vmstate_zipit_lcd_state = { + .name = "zipit-lcd", + .version_id = 1, + .minimum_version_id = 1, + .minimum_version_id_old = 1, + .fields = (VMStateField[]) { + VMSTATE_INT32(selected, ZipitLCD), + VMSTATE_INT32(enabled, ZipitLCD), + VMSTATE_BUFFER(buf, ZipitLCD), + VMSTATE_UINT32(cur_reg, ZipitLCD), + VMSTATE_INT32(pos, ZipitLCD), + VMSTATE_END_OF_LIST(), + } +}; + +static SSISlaveInfo zipit_lcd_info = { + .qdev.name = "zipit-lcd", + .qdev.size = sizeof(ZipitLCD), + .qdev.vmsd = &vmstate_zipit_lcd_state, + .init = zipit_lcd_init, + .transfer = zipit_lcd_transfer +}; + +typedef struct { + i2c_slave i2c; + int len; + uint8_t buf[3]; +} AER915State; + +static int aer915_send(i2c_slave *i2c, uint8_t data) +{ + AER915State *s = FROM_I2C_SLAVE(AER915State, i2c); + s->buf[s->len] = data; + if (s->len++ > 2) { + DPRINTF("%s: message too long (%i bytes)\n", + __func__, s->len); + return 1; + } + + if (s->len == 2) { + DPRINTF("%s: reg %d value 0x%02x\n", __func__, + s->buf[0], s->buf[1]); + } + + return 0; +} + +static void aer915_event(i2c_slave *i2c, enum i2c_event event) +{ + AER915State *s = FROM_I2C_SLAVE(AER915State, i2c); + switch (event) { + case I2C_START_SEND: + s->len = 0; + break; + case I2C_START_RECV: + if (s->len != 1) { + DPRINTF("%s: short message!?\n", __func__); + } + break; + case I2C_FINISH: + break; + default: + break; + } +} + +static int aer915_recv(i2c_slave *slave) +{ + int retval = 0x00; + AER915State *s = FROM_I2C_SLAVE(AER915State, slave); + + switch (s->buf[0]) { + /* Return hardcoded battery voltage, + * 0xf0 means ~4.1V + */ + case 0x02: + retval = 0xf0; + break; + /* Return 0x00 for other regs, + * we don't know what they are for, + * anyway they return 0x00 on real hardware. + */ + default: + break; + } + + return retval; +} + +static int aer915_init(i2c_slave *i2c) +{ + /* Nothing to do. */ + return 0; +} + +static VMStateDescription vmstate_aer915_state = { + .name = "aer915", + .version_id = 1, + .minimum_version_id = 1, + .minimum_version_id_old = 1, + .fields = (VMStateField[]) { + VMSTATE_INT32(len, AER915State), + VMSTATE_BUFFER(buf, AER915State), + VMSTATE_END_OF_LIST(), + } +}; + +static I2CSlaveInfo aer915_info = { + .qdev.name = "aer915", + .qdev.size = sizeof(AER915State), + .qdev.vmsd = &vmstate_aer915_state, + .init = aer915_init, + .event = aer915_event, + .recv = aer915_recv, + .send = aer915_send +}; + +static void z2_init(ram_addr_t ram_size, + const char *boot_device, + const char *kernel_filename, const char *kernel_cmdline, + const char *initrd_filename, const char *cpu_model) +{ + uint32_t sector_len = 0x10000; + PXA2xxState *cpu; + DriveInfo *dinfo; + int be; + void *z2_lcd; + i2c_bus *bus; + DeviceState *wm; + + if (!cpu_model) { + cpu_model = "pxa270-c5"; + } + + /* Setup CPU & memory */ + cpu = pxa270_init(z2_binfo.ram_size, cpu_model); + +#ifdef TARGET_WORDS_BIGENDIAN + be = 1; +#else + be = 0; +#endif + dinfo = drive_get(IF_PFLASH, 0, 0); + if (!dinfo) { + fprintf(stderr, "Flash image must be given with the " + "'pflash' parameter\n"); + exit(1); + } + + if (!pflash_cfi01_register(Z2_FLASH_BASE, + qemu_ram_alloc(NULL, "z2.flash0", Z2_FLASH_SIZE), + dinfo->bdrv, sector_len, + Z2_FLASH_SIZE / sector_len, 4, 0, 0, 0, 0, + be)) { + fprintf(stderr, "qemu: Error registering flash memory.\n"); + exit(1); + } + + /* setup keypad */ + pxa27x_register_keypad(cpu->kp, map, 0x100); + + /* MMC/SD host */ + pxa2xx_mmci_handlers(cpu->mmc, + NULL, + qdev_get_gpio_in(cpu->gpio, Z2_GPIO_SD_DETECT)); + + ssi_register_slave(&zipit_lcd_info); + i2c_register_slave(&aer915_info); + z2_lcd = ssi_create_slave(cpu->ssp[1], "zipit-lcd"); + bus = pxa2xx_i2c_bus(cpu->i2c[0]); + i2c_create_slave(bus, "aer915", 0x55); + wm = i2c_create_slave(bus, "wm8750", 0x1b); + cpu->i2s->opaque = wm; + cpu->i2s->codec_out = wm8750_dac_dat; + cpu->i2s->codec_in = wm8750_adc_dat; + wm8750_data_req_set(wm, cpu->i2s->data_req, cpu->i2s); + + qdev_connect_gpio_out(cpu->gpio, Z2_GPIO_LCD_CS, + qemu_allocate_irqs(z2_lcd_cs, z2_lcd, 1)[0]); + + if (kernel_filename) { + z2_binfo.kernel_filename = kernel_filename; + z2_binfo.kernel_cmdline = kernel_cmdline; + z2_binfo.initrd_filename = initrd_filename; + z2_binfo.board_id = 0x6dd; + arm_load_kernel(cpu->env, &z2_binfo); + } +} + +static QEMUMachine z2_machine = { + .name = "z2", + .desc = "Zipit Z2 (PXA27x)", + .init = z2_init, +}; + +static void z2_machine_init(void) +{ + qemu_register_machine(&z2_machine); +} + +machine_init(z2_machine_init); From 8534b8ba337e55031592144ea524f7bcaf144113 Mon Sep 17 00:00:00 2001 From: Jan Kiszka Date: Sat, 30 Jul 2011 07:18:41 +0200 Subject: [PATCH 133/230] usb-hid: Fix 0/0 position for Windows in tablet mode For unknown reasons, Windows drivers (tested with XP and Win7) ignore usb-tablet events that move the pointer to 0/0. So always report 0/0 as 1/0. Signed-off-by: Jan Kiszka Signed-off-by: Andrzej Zaborowski --- hw/usb-hid.c | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/hw/usb-hid.c b/hw/usb-hid.c index b812da2a6a..9008320c86 100644 --- a/hw/usb-hid.c +++ b/hw/usb-hid.c @@ -459,6 +459,11 @@ static void usb_pointer_event_combine(USBPointerEvent *e, int xyrel, } else { e->xdx = x1; e->ydy = y1; + /* Windows drivers do not like the 0/0 position and ignore such + * events. */ + if (!(x1 | y1)) { + x1 = 1; + } } e->dz += z1; } From 4b5dfd8246321d2cdca0508f6837a681f7873f43 Mon Sep 17 00:00:00 2001 From: Peter Maydell Date: Mon, 18 Jul 2011 11:44:09 +0100 Subject: [PATCH 134/230] user: Restore debug usage message for '-d ?' in user mode emulation The code which prints the debug usage message on '-d ?' for *-user has to come before the check for "not enough arguments", so that "qemu-foo -d ?" prints the list of possible debug log items rather than the generic usage message. (This was inadvertently broken in commit c235d73.) Signed-off-by: Peter Maydell Signed-off-by: Andrzej Zaborowski --- bsd-user/main.c | 8 +++++--- darwin-user/main.c | 8 +++++--- linux-user/main.c | 11 ++++++----- 3 files changed, 16 insertions(+), 11 deletions(-) diff --git a/bsd-user/main.c b/bsd-user/main.c index 6018a419ed..a63b8777fc 100644 --- a/bsd-user/main.c +++ b/bsd-user/main.c @@ -856,9 +856,6 @@ int main(int argc, char **argv) usage(); } } - if (optind >= argc) - usage(); - filename = argv[optind]; /* init debug */ cpu_set_log_filename(log_file); @@ -877,6 +874,11 @@ int main(int argc, char **argv) cpu_set_log(mask); } + if (optind >= argc) { + usage(); + } + filename = argv[optind]; + /* Zero out regs */ memset(regs, 0, sizeof(struct target_pt_regs)); diff --git a/darwin-user/main.c b/darwin-user/main.c index 35196a12cc..72307adeb7 100644 --- a/darwin-user/main.c +++ b/darwin-user/main.c @@ -809,9 +809,6 @@ int main(int argc, char **argv) usage(); } } - if (optind >= argc) - usage(); - filename = argv[optind]; /* init debug */ cpu_set_log_filename(log_file); @@ -830,6 +827,11 @@ int main(int argc, char **argv) cpu_set_log(mask); } + if (optind >= argc) { + usage(); + } + filename = argv[optind]; + /* Zero out regs */ memset(regs, 0, sizeof(struct target_pt_regs)); diff --git a/linux-user/main.c b/linux-user/main.c index 2135b9c714..6a8f4bdc11 100644 --- a/linux-user/main.c +++ b/linux-user/main.c @@ -3048,11 +3048,6 @@ int main(int argc, char **argv, char **envp) usage(); } } - if (optind >= argc) - usage(); - filename = argv[optind]; - exec_path = argv[optind]; - /* init debug */ cpu_set_log_filename(log_file); if (log_mask) { @@ -3070,6 +3065,12 @@ int main(int argc, char **argv, char **envp) cpu_set_log(mask); } + if (optind >= argc) { + usage(); + } + filename = argv[optind]; + exec_path = argv[optind]; + /* Zero out regs */ memset(regs, 0, sizeof(struct target_pt_regs)); From 5e37141bbb9796ef139aee902a882ca97d59b84d Mon Sep 17 00:00:00 2001 From: Vincent Palatin Date: Mon, 25 Jul 2011 16:19:05 -0700 Subject: [PATCH 135/230] sd: do not add one sector to the disk size This leads to random off-by-one error. When the size of the SD is exactly 1GB, the emulation was returning a wrong SDHC CSD descriptor. Signed-off-by: Vincent Palatin Signed-off-by: Andrzej Zaborowski --- hw/sd.c | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/hw/sd.c b/hw/sd.c index 219a0dd296..c2c80ab7b8 100644 --- a/hw/sd.c +++ b/hw/sd.c @@ -393,9 +393,7 @@ static void sd_reset(SDState *sd, BlockDriverState *bdrv) } else { sect = 0; } - sect <<= 9; - - size = sect + 1; + size = sect << 9; sect = (size >> (HWBLOCK_SHIFT + SECTOR_SHIFT + WPGROUP_SHIFT)) + 1; From ccb57e0ea74892a29969f9a28c67df3fdcb5259d Mon Sep 17 00:00:00 2001 From: Tsuneo Saito Date: Sat, 23 Jul 2011 11:20:06 +0900 Subject: [PATCH 136/230] SPARC64: fix fnor* and fnand* Fix the problem that result values are not assigned to the destination registers. Signed-off-by: Tsuneo Saito Signed-off-by: Blue Swirl --- target-sparc/translate.c | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/target-sparc/translate.c b/target-sparc/translate.c index 15967c551a..f68b3bcdd8 100644 --- a/target-sparc/translate.c +++ b/target-sparc/translate.c @@ -3980,14 +3980,15 @@ static void disas_sparc_insn(DisasContext * dc) break; case 0x062: /* VIS I fnor */ CHECK_FPU_FEATURE(dc, VIS1); - tcg_gen_nor_i32(cpu_tmp32, cpu_fpr[DFPREG(rs1)], + tcg_gen_nor_i32(cpu_fpr[DFPREG(rd)], cpu_fpr[DFPREG(rs1)], cpu_fpr[DFPREG(rs2)]); - tcg_gen_nor_i32(cpu_tmp32, cpu_fpr[DFPREG(rs1) + 1], + tcg_gen_nor_i32(cpu_fpr[DFPREG(rd) + 1], + cpu_fpr[DFPREG(rs1) + 1], cpu_fpr[DFPREG(rs2) + 1]); break; case 0x063: /* VIS I fnors */ CHECK_FPU_FEATURE(dc, VIS1); - tcg_gen_nor_i32(cpu_tmp32, cpu_fpr[rs1], cpu_fpr[rs2]); + tcg_gen_nor_i32(cpu_fpr[rd], cpu_fpr[rs1], cpu_fpr[rs2]); break; case 0x064: /* VIS I fandnot2 */ CHECK_FPU_FEATURE(dc, VIS1); @@ -4047,14 +4048,15 @@ static void disas_sparc_insn(DisasContext * dc) break; case 0x06e: /* VIS I fnand */ CHECK_FPU_FEATURE(dc, VIS1); - tcg_gen_nand_i32(cpu_tmp32, cpu_fpr[DFPREG(rs1)], + tcg_gen_nand_i32(cpu_fpr[DFPREG(rd)], cpu_fpr[DFPREG(rs1)], cpu_fpr[DFPREG(rs2)]); - tcg_gen_nand_i32(cpu_tmp32, cpu_fpr[DFPREG(rs1) + 1], + tcg_gen_nand_i32(cpu_fpr[DFPREG(rd) + 1], + cpu_fpr[DFPREG(rs1) + 1], cpu_fpr[DFPREG(rs2) + 1]); break; case 0x06f: /* VIS I fnands */ CHECK_FPU_FEATURE(dc, VIS1); - tcg_gen_nand_i32(cpu_tmp32, cpu_fpr[rs1], cpu_fpr[rs2]); + tcg_gen_nand_i32(cpu_fpr[rd], cpu_fpr[rs1], cpu_fpr[rs2]); break; case 0x070: /* VIS I fand */ CHECK_FPU_FEATURE(dc, VIS1); From 638737ad0342ba48f3dfbd2ae03a48cc53501b26 Mon Sep 17 00:00:00 2001 From: Tsuneo Saito Date: Sat, 23 Jul 2011 11:20:07 +0900 Subject: [PATCH 137/230] SPARC64: implement %fprs dirty bits Implement %fprs.DU/DL bits. The FPU sets %fprs.DL and %fprs.DU when values are assigned to %f0-31 and %f32-63 respectively. Signed-off-by: Tsuneo Saito Signed-off-by: Blue Swirl --- target-sparc/translate.c | 116 +++++++++++++++++++++++++++++++++++++++ 1 file changed, 116 insertions(+) diff --git a/target-sparc/translate.c b/target-sparc/translate.c index f68b3bcdd8..958fbc5a9d 100644 --- a/target-sparc/translate.c +++ b/target-sparc/translate.c @@ -1558,6 +1558,13 @@ static int gen_trap_ifnofpu(DisasContext *dc, TCGv r_cond) return 0; } +static inline void gen_update_fprs_dirty(int rd) +{ +#if defined(TARGET_SPARC64) + tcg_gen_ori_i32(cpu_fprs, cpu_fprs, (rd < 32) ? 1 : 2); +#endif +} + static inline void gen_op_clear_ieee_excp_and_FTT(void) { tcg_gen_andi_tl(cpu_fsr, cpu_fsr, FSR_FTT_CEXC_NMASK); @@ -2351,12 +2358,15 @@ static void disas_sparc_insn(DisasContext * dc) switch (xop) { case 0x1: /* fmovs */ tcg_gen_mov_i32(cpu_fpr[rd], cpu_fpr[rs2]); + gen_update_fprs_dirty(rd); break; case 0x5: /* fnegs */ gen_helper_fnegs(cpu_fpr[rd], cpu_fpr[rs2]); + gen_update_fprs_dirty(rd); break; case 0x9: /* fabss */ gen_helper_fabss(cpu_fpr[rd], cpu_fpr[rs2]); + gen_update_fprs_dirty(rd); break; case 0x29: /* fsqrts */ CHECK_FPU_FEATURE(dc, FSQRT); @@ -2364,6 +2374,7 @@ static void disas_sparc_insn(DisasContext * dc) gen_helper_fsqrts(cpu_tmp32, cpu_fpr[rs2]); gen_helper_check_ieee_exceptions(); tcg_gen_mov_i32(cpu_fpr[rd], cpu_tmp32); + gen_update_fprs_dirty(rd); break; case 0x2a: /* fsqrtd */ CHECK_FPU_FEATURE(dc, FSQRT); @@ -2372,6 +2383,7 @@ static void disas_sparc_insn(DisasContext * dc) gen_helper_fsqrtd(); gen_helper_check_ieee_exceptions(); gen_op_store_DT0_fpr(DFPREG(rd)); + gen_update_fprs_dirty(DFPREG(rd)); break; case 0x2b: /* fsqrtq */ CHECK_FPU_FEATURE(dc, FLOAT128); @@ -2380,12 +2392,14 @@ static void disas_sparc_insn(DisasContext * dc) gen_helper_fsqrtq(); gen_helper_check_ieee_exceptions(); gen_op_store_QT0_fpr(QFPREG(rd)); + gen_update_fprs_dirty(QFPREG(rd)); break; case 0x41: /* fadds */ gen_clear_float_exceptions(); gen_helper_fadds(cpu_tmp32, cpu_fpr[rs1], cpu_fpr[rs2]); gen_helper_check_ieee_exceptions(); tcg_gen_mov_i32(cpu_fpr[rd], cpu_tmp32); + gen_update_fprs_dirty(rd); break; case 0x42: /* faddd */ gen_op_load_fpr_DT0(DFPREG(rs1)); @@ -2394,6 +2408,7 @@ static void disas_sparc_insn(DisasContext * dc) gen_helper_faddd(); gen_helper_check_ieee_exceptions(); gen_op_store_DT0_fpr(DFPREG(rd)); + gen_update_fprs_dirty(DFPREG(rd)); break; case 0x43: /* faddq */ CHECK_FPU_FEATURE(dc, FLOAT128); @@ -2403,12 +2418,14 @@ static void disas_sparc_insn(DisasContext * dc) gen_helper_faddq(); gen_helper_check_ieee_exceptions(); gen_op_store_QT0_fpr(QFPREG(rd)); + gen_update_fprs_dirty(QFPREG(rd)); break; case 0x45: /* fsubs */ gen_clear_float_exceptions(); gen_helper_fsubs(cpu_tmp32, cpu_fpr[rs1], cpu_fpr[rs2]); gen_helper_check_ieee_exceptions(); tcg_gen_mov_i32(cpu_fpr[rd], cpu_tmp32); + gen_update_fprs_dirty(rd); break; case 0x46: /* fsubd */ gen_op_load_fpr_DT0(DFPREG(rs1)); @@ -2417,6 +2434,7 @@ static void disas_sparc_insn(DisasContext * dc) gen_helper_fsubd(); gen_helper_check_ieee_exceptions(); gen_op_store_DT0_fpr(DFPREG(rd)); + gen_update_fprs_dirty(DFPREG(rd)); break; case 0x47: /* fsubq */ CHECK_FPU_FEATURE(dc, FLOAT128); @@ -2426,6 +2444,7 @@ static void disas_sparc_insn(DisasContext * dc) gen_helper_fsubq(); gen_helper_check_ieee_exceptions(); gen_op_store_QT0_fpr(QFPREG(rd)); + gen_update_fprs_dirty(QFPREG(rd)); break; case 0x49: /* fmuls */ CHECK_FPU_FEATURE(dc, FMUL); @@ -2433,6 +2452,7 @@ static void disas_sparc_insn(DisasContext * dc) gen_helper_fmuls(cpu_tmp32, cpu_fpr[rs1], cpu_fpr[rs2]); gen_helper_check_ieee_exceptions(); tcg_gen_mov_i32(cpu_fpr[rd], cpu_tmp32); + gen_update_fprs_dirty(rd); break; case 0x4a: /* fmuld */ CHECK_FPU_FEATURE(dc, FMUL); @@ -2442,6 +2462,7 @@ static void disas_sparc_insn(DisasContext * dc) gen_helper_fmuld(); gen_helper_check_ieee_exceptions(); gen_op_store_DT0_fpr(DFPREG(rd)); + gen_update_fprs_dirty(DFPREG(rd)); break; case 0x4b: /* fmulq */ CHECK_FPU_FEATURE(dc, FLOAT128); @@ -2452,12 +2473,14 @@ static void disas_sparc_insn(DisasContext * dc) gen_helper_fmulq(); gen_helper_check_ieee_exceptions(); gen_op_store_QT0_fpr(QFPREG(rd)); + gen_update_fprs_dirty(QFPREG(rd)); break; case 0x4d: /* fdivs */ gen_clear_float_exceptions(); gen_helper_fdivs(cpu_tmp32, cpu_fpr[rs1], cpu_fpr[rs2]); gen_helper_check_ieee_exceptions(); tcg_gen_mov_i32(cpu_fpr[rd], cpu_tmp32); + gen_update_fprs_dirty(rd); break; case 0x4e: /* fdivd */ gen_op_load_fpr_DT0(DFPREG(rs1)); @@ -2466,6 +2489,7 @@ static void disas_sparc_insn(DisasContext * dc) gen_helper_fdivd(); gen_helper_check_ieee_exceptions(); gen_op_store_DT0_fpr(DFPREG(rd)); + gen_update_fprs_dirty(DFPREG(rd)); break; case 0x4f: /* fdivq */ CHECK_FPU_FEATURE(dc, FLOAT128); @@ -2475,6 +2499,7 @@ static void disas_sparc_insn(DisasContext * dc) gen_helper_fdivq(); gen_helper_check_ieee_exceptions(); gen_op_store_QT0_fpr(QFPREG(rd)); + gen_update_fprs_dirty(QFPREG(rd)); break; case 0x69: /* fsmuld */ CHECK_FPU_FEATURE(dc, FSMULD); @@ -2482,6 +2507,7 @@ static void disas_sparc_insn(DisasContext * dc) gen_helper_fsmuld(cpu_fpr[rs1], cpu_fpr[rs2]); gen_helper_check_ieee_exceptions(); gen_op_store_DT0_fpr(DFPREG(rd)); + gen_update_fprs_dirty(DFPREG(rd)); break; case 0x6e: /* fdmulq */ CHECK_FPU_FEATURE(dc, FLOAT128); @@ -2491,12 +2517,14 @@ static void disas_sparc_insn(DisasContext * dc) gen_helper_fdmulq(); gen_helper_check_ieee_exceptions(); gen_op_store_QT0_fpr(QFPREG(rd)); + gen_update_fprs_dirty(QFPREG(rd)); break; case 0xc4: /* fitos */ gen_clear_float_exceptions(); gen_helper_fitos(cpu_tmp32, cpu_fpr[rs2]); gen_helper_check_ieee_exceptions(); tcg_gen_mov_i32(cpu_fpr[rd], cpu_tmp32); + gen_update_fprs_dirty(rd); break; case 0xc6: /* fdtos */ gen_op_load_fpr_DT1(DFPREG(rs2)); @@ -2504,6 +2532,7 @@ static void disas_sparc_insn(DisasContext * dc) gen_helper_fdtos(cpu_tmp32); gen_helper_check_ieee_exceptions(); tcg_gen_mov_i32(cpu_fpr[rd], cpu_tmp32); + gen_update_fprs_dirty(rd); break; case 0xc7: /* fqtos */ CHECK_FPU_FEATURE(dc, FLOAT128); @@ -2512,14 +2541,17 @@ static void disas_sparc_insn(DisasContext * dc) gen_helper_fqtos(cpu_tmp32); gen_helper_check_ieee_exceptions(); tcg_gen_mov_i32(cpu_fpr[rd], cpu_tmp32); + gen_update_fprs_dirty(rd); break; case 0xc8: /* fitod */ gen_helper_fitod(cpu_fpr[rs2]); gen_op_store_DT0_fpr(DFPREG(rd)); + gen_update_fprs_dirty(DFPREG(rd)); break; case 0xc9: /* fstod */ gen_helper_fstod(cpu_fpr[rs2]); gen_op_store_DT0_fpr(DFPREG(rd)); + gen_update_fprs_dirty(DFPREG(rd)); break; case 0xcb: /* fqtod */ CHECK_FPU_FEATURE(dc, FLOAT128); @@ -2528,28 +2560,33 @@ static void disas_sparc_insn(DisasContext * dc) gen_helper_fqtod(); gen_helper_check_ieee_exceptions(); gen_op_store_DT0_fpr(DFPREG(rd)); + gen_update_fprs_dirty(DFPREG(rd)); break; case 0xcc: /* fitoq */ CHECK_FPU_FEATURE(dc, FLOAT128); gen_helper_fitoq(cpu_fpr[rs2]); gen_op_store_QT0_fpr(QFPREG(rd)); + gen_update_fprs_dirty(QFPREG(rd)); break; case 0xcd: /* fstoq */ CHECK_FPU_FEATURE(dc, FLOAT128); gen_helper_fstoq(cpu_fpr[rs2]); gen_op_store_QT0_fpr(QFPREG(rd)); + gen_update_fprs_dirty(QFPREG(rd)); break; case 0xce: /* fdtoq */ CHECK_FPU_FEATURE(dc, FLOAT128); gen_op_load_fpr_DT1(DFPREG(rs2)); gen_helper_fdtoq(); gen_op_store_QT0_fpr(QFPREG(rd)); + gen_update_fprs_dirty(QFPREG(rd)); break; case 0xd1: /* fstoi */ gen_clear_float_exceptions(); gen_helper_fstoi(cpu_tmp32, cpu_fpr[rs2]); gen_helper_check_ieee_exceptions(); tcg_gen_mov_i32(cpu_fpr[rd], cpu_tmp32); + gen_update_fprs_dirty(rd); break; case 0xd2: /* fdtoi */ gen_op_load_fpr_DT1(DFPREG(rs2)); @@ -2557,6 +2594,7 @@ static void disas_sparc_insn(DisasContext * dc) gen_helper_fdtoi(cpu_tmp32); gen_helper_check_ieee_exceptions(); tcg_gen_mov_i32(cpu_fpr[rd], cpu_tmp32); + gen_update_fprs_dirty(rd); break; case 0xd3: /* fqtoi */ CHECK_FPU_FEATURE(dc, FLOAT128); @@ -2565,12 +2603,14 @@ static void disas_sparc_insn(DisasContext * dc) gen_helper_fqtoi(cpu_tmp32); gen_helper_check_ieee_exceptions(); tcg_gen_mov_i32(cpu_fpr[rd], cpu_tmp32); + gen_update_fprs_dirty(rd); break; #ifdef TARGET_SPARC64 case 0x2: /* V9 fmovd */ tcg_gen_mov_i32(cpu_fpr[DFPREG(rd)], cpu_fpr[DFPREG(rs2)]); tcg_gen_mov_i32(cpu_fpr[DFPREG(rd) + 1], cpu_fpr[DFPREG(rs2) + 1]); + gen_update_fprs_dirty(DFPREG(rd)); break; case 0x3: /* V9 fmovq */ CHECK_FPU_FEATURE(dc, FLOAT128); @@ -2581,34 +2621,40 @@ static void disas_sparc_insn(DisasContext * dc) cpu_fpr[QFPREG(rs2) + 2]); tcg_gen_mov_i32(cpu_fpr[QFPREG(rd) + 3], cpu_fpr[QFPREG(rs2) + 3]); + gen_update_fprs_dirty(QFPREG(rd)); break; case 0x6: /* V9 fnegd */ gen_op_load_fpr_DT1(DFPREG(rs2)); gen_helper_fnegd(); gen_op_store_DT0_fpr(DFPREG(rd)); + gen_update_fprs_dirty(DFPREG(rd)); break; case 0x7: /* V9 fnegq */ CHECK_FPU_FEATURE(dc, FLOAT128); gen_op_load_fpr_QT1(QFPREG(rs2)); gen_helper_fnegq(); gen_op_store_QT0_fpr(QFPREG(rd)); + gen_update_fprs_dirty(QFPREG(rd)); break; case 0xa: /* V9 fabsd */ gen_op_load_fpr_DT1(DFPREG(rs2)); gen_helper_fabsd(); gen_op_store_DT0_fpr(DFPREG(rd)); + gen_update_fprs_dirty(DFPREG(rd)); break; case 0xb: /* V9 fabsq */ CHECK_FPU_FEATURE(dc, FLOAT128); gen_op_load_fpr_QT1(QFPREG(rs2)); gen_helper_fabsq(); gen_op_store_QT0_fpr(QFPREG(rd)); + gen_update_fprs_dirty(QFPREG(rd)); break; case 0x81: /* V9 fstox */ gen_clear_float_exceptions(); gen_helper_fstox(cpu_fpr[rs2]); gen_helper_check_ieee_exceptions(); gen_op_store_DT0_fpr(DFPREG(rd)); + gen_update_fprs_dirty(DFPREG(rd)); break; case 0x82: /* V9 fdtox */ gen_op_load_fpr_DT1(DFPREG(rs2)); @@ -2616,6 +2662,7 @@ static void disas_sparc_insn(DisasContext * dc) gen_helper_fdtox(); gen_helper_check_ieee_exceptions(); gen_op_store_DT0_fpr(DFPREG(rd)); + gen_update_fprs_dirty(DFPREG(rd)); break; case 0x83: /* V9 fqtox */ CHECK_FPU_FEATURE(dc, FLOAT128); @@ -2624,6 +2671,7 @@ static void disas_sparc_insn(DisasContext * dc) gen_helper_fqtox(); gen_helper_check_ieee_exceptions(); gen_op_store_DT0_fpr(DFPREG(rd)); + gen_update_fprs_dirty(DFPREG(rd)); break; case 0x84: /* V9 fxtos */ gen_op_load_fpr_DT1(DFPREG(rs2)); @@ -2631,6 +2679,7 @@ static void disas_sparc_insn(DisasContext * dc) gen_helper_fxtos(cpu_tmp32); gen_helper_check_ieee_exceptions(); tcg_gen_mov_i32(cpu_fpr[rd], cpu_tmp32); + gen_update_fprs_dirty(rd); break; case 0x88: /* V9 fxtod */ gen_op_load_fpr_DT1(DFPREG(rs2)); @@ -2638,6 +2687,7 @@ static void disas_sparc_insn(DisasContext * dc) gen_helper_fxtod(); gen_helper_check_ieee_exceptions(); gen_op_store_DT0_fpr(DFPREG(rd)); + gen_update_fprs_dirty(DFPREG(rd)); break; case 0x8c: /* V9 fxtoq */ CHECK_FPU_FEATURE(dc, FLOAT128); @@ -2646,6 +2696,7 @@ static void disas_sparc_insn(DisasContext * dc) gen_helper_fxtoq(); gen_helper_check_ieee_exceptions(); gen_op_store_QT0_fpr(QFPREG(rd)); + gen_update_fprs_dirty(QFPREG(rd)); break; #endif default: @@ -2672,6 +2723,7 @@ static void disas_sparc_insn(DisasContext * dc) tcg_gen_brcondi_tl(gen_tcg_cond_reg[cond], cpu_src1, 0, l1); tcg_gen_mov_i32(cpu_fpr[rd], cpu_fpr[rs2]); + gen_update_fprs_dirty(rd); gen_set_label(l1); break; } else if ((xop & 0x11f) == 0x006) { // V9 fmovdr @@ -2684,6 +2736,7 @@ static void disas_sparc_insn(DisasContext * dc) 0, l1); tcg_gen_mov_i32(cpu_fpr[DFPREG(rd)], cpu_fpr[DFPREG(rs2)]); tcg_gen_mov_i32(cpu_fpr[DFPREG(rd) + 1], cpu_fpr[DFPREG(rs2) + 1]); + gen_update_fprs_dirty(DFPREG(rd)); gen_set_label(l1); break; } else if ((xop & 0x11f) == 0x007) { // V9 fmovqr @@ -2699,6 +2752,7 @@ static void disas_sparc_insn(DisasContext * dc) tcg_gen_mov_i32(cpu_fpr[QFPREG(rd) + 1], cpu_fpr[QFPREG(rs2) + 1]); tcg_gen_mov_i32(cpu_fpr[QFPREG(rd) + 2], cpu_fpr[QFPREG(rs2) + 2]); tcg_gen_mov_i32(cpu_fpr[QFPREG(rd) + 3], cpu_fpr[QFPREG(rs2) + 3]); + gen_update_fprs_dirty(QFPREG(rd)); gen_set_label(l1); break; } @@ -2717,6 +2771,7 @@ static void disas_sparc_insn(DisasContext * dc) tcg_gen_brcondi_tl(TCG_COND_EQ, r_cond, \ 0, l1); \ tcg_gen_mov_i32(cpu_fpr[rd], cpu_fpr[rs2]); \ + gen_update_fprs_dirty(rd); \ gen_set_label(l1); \ tcg_temp_free(r_cond); \ } @@ -2735,6 +2790,7 @@ static void disas_sparc_insn(DisasContext * dc) cpu_fpr[DFPREG(rs2)]); \ tcg_gen_mov_i32(cpu_fpr[DFPREG(rd) + 1], \ cpu_fpr[DFPREG(rs2) + 1]); \ + gen_update_fprs_dirty(DFPREG(rd)); \ gen_set_label(l1); \ tcg_temp_free(r_cond); \ } @@ -2757,6 +2813,7 @@ static void disas_sparc_insn(DisasContext * dc) cpu_fpr[QFPREG(rs2) + 2]); \ tcg_gen_mov_i32(cpu_fpr[QFPREG(rd) + 3], \ cpu_fpr[QFPREG(rs2) + 3]); \ + gen_update_fprs_dirty(QFPREG(rd)); \ gen_set_label(l1); \ tcg_temp_free(r_cond); \ } @@ -2815,6 +2872,7 @@ static void disas_sparc_insn(DisasContext * dc) tcg_gen_brcondi_tl(TCG_COND_EQ, r_cond, \ 0, l1); \ tcg_gen_mov_i32(cpu_fpr[rd], cpu_fpr[rs2]); \ + gen_update_fprs_dirty(rd); \ gen_set_label(l1); \ tcg_temp_free(r_cond); \ } @@ -2833,6 +2891,7 @@ static void disas_sparc_insn(DisasContext * dc) cpu_fpr[DFPREG(rs2)]); \ tcg_gen_mov_i32(cpu_fpr[DFPREG(rd) + 1], \ cpu_fpr[DFPREG(rs2) + 1]); \ + gen_update_fprs_dirty(DFPREG(rd)); \ gen_set_label(l1); \ tcg_temp_free(r_cond); \ } @@ -2855,6 +2914,7 @@ static void disas_sparc_insn(DisasContext * dc) cpu_fpr[QFPREG(rs2) + 2]); \ tcg_gen_mov_i32(cpu_fpr[QFPREG(rd) + 3], \ cpu_fpr[QFPREG(rs2) + 3]); \ + gen_update_fprs_dirty(QFPREG(rd)); \ gen_set_label(l1); \ tcg_temp_free(r_cond); \ } @@ -3848,6 +3908,7 @@ static void disas_sparc_insn(DisasContext * dc) gen_op_load_fpr_DT1(DFPREG(rs2)); gen_helper_fmul8x16(); gen_op_store_DT0_fpr(DFPREG(rd)); + gen_update_fprs_dirty(DFPREG(rd)); break; case 0x033: /* VIS I fmul8x16au */ CHECK_FPU_FEATURE(dc, VIS1); @@ -3855,6 +3916,7 @@ static void disas_sparc_insn(DisasContext * dc) gen_op_load_fpr_DT1(DFPREG(rs2)); gen_helper_fmul8x16au(); gen_op_store_DT0_fpr(DFPREG(rd)); + gen_update_fprs_dirty(DFPREG(rd)); break; case 0x035: /* VIS I fmul8x16al */ CHECK_FPU_FEATURE(dc, VIS1); @@ -3862,6 +3924,7 @@ static void disas_sparc_insn(DisasContext * dc) gen_op_load_fpr_DT1(DFPREG(rs2)); gen_helper_fmul8x16al(); gen_op_store_DT0_fpr(DFPREG(rd)); + gen_update_fprs_dirty(DFPREG(rd)); break; case 0x036: /* VIS I fmul8sux16 */ CHECK_FPU_FEATURE(dc, VIS1); @@ -3869,6 +3932,7 @@ static void disas_sparc_insn(DisasContext * dc) gen_op_load_fpr_DT1(DFPREG(rs2)); gen_helper_fmul8sux16(); gen_op_store_DT0_fpr(DFPREG(rd)); + gen_update_fprs_dirty(DFPREG(rd)); break; case 0x037: /* VIS I fmul8ulx16 */ CHECK_FPU_FEATURE(dc, VIS1); @@ -3876,6 +3940,7 @@ static void disas_sparc_insn(DisasContext * dc) gen_op_load_fpr_DT1(DFPREG(rs2)); gen_helper_fmul8ulx16(); gen_op_store_DT0_fpr(DFPREG(rd)); + gen_update_fprs_dirty(DFPREG(rd)); break; case 0x038: /* VIS I fmuld8sux16 */ CHECK_FPU_FEATURE(dc, VIS1); @@ -3883,6 +3948,7 @@ static void disas_sparc_insn(DisasContext * dc) gen_op_load_fpr_DT1(DFPREG(rs2)); gen_helper_fmuld8sux16(); gen_op_store_DT0_fpr(DFPREG(rd)); + gen_update_fprs_dirty(DFPREG(rd)); break; case 0x039: /* VIS I fmuld8ulx16 */ CHECK_FPU_FEATURE(dc, VIS1); @@ -3890,6 +3956,7 @@ static void disas_sparc_insn(DisasContext * dc) gen_op_load_fpr_DT1(DFPREG(rs2)); gen_helper_fmuld8ulx16(); gen_op_store_DT0_fpr(DFPREG(rd)); + gen_update_fprs_dirty(DFPREG(rd)); break; case 0x03a: /* VIS I fpack32 */ case 0x03b: /* VIS I fpack16 */ @@ -3903,6 +3970,7 @@ static void disas_sparc_insn(DisasContext * dc) gen_op_load_fpr_DT1(DFPREG(rs2)); gen_helper_faligndata(); gen_op_store_DT0_fpr(DFPREG(rd)); + gen_update_fprs_dirty(DFPREG(rd)); break; case 0x04b: /* VIS I fpmerge */ CHECK_FPU_FEATURE(dc, VIS1); @@ -3910,6 +3978,7 @@ static void disas_sparc_insn(DisasContext * dc) gen_op_load_fpr_DT1(DFPREG(rs2)); gen_helper_fpmerge(); gen_op_store_DT0_fpr(DFPREG(rd)); + gen_update_fprs_dirty(DFPREG(rd)); break; case 0x04c: /* VIS II bshuffle */ // XXX @@ -3920,6 +3989,7 @@ static void disas_sparc_insn(DisasContext * dc) gen_op_load_fpr_DT1(DFPREG(rs2)); gen_helper_fexpand(); gen_op_store_DT0_fpr(DFPREG(rd)); + gen_update_fprs_dirty(DFPREG(rd)); break; case 0x050: /* VIS I fpadd16 */ CHECK_FPU_FEATURE(dc, VIS1); @@ -3927,11 +3997,13 @@ static void disas_sparc_insn(DisasContext * dc) gen_op_load_fpr_DT1(DFPREG(rs2)); gen_helper_fpadd16(); gen_op_store_DT0_fpr(DFPREG(rd)); + gen_update_fprs_dirty(DFPREG(rd)); break; case 0x051: /* VIS I fpadd16s */ CHECK_FPU_FEATURE(dc, VIS1); gen_helper_fpadd16s(cpu_fpr[rd], cpu_fpr[rs1], cpu_fpr[rs2]); + gen_update_fprs_dirty(rd); break; case 0x052: /* VIS I fpadd32 */ CHECK_FPU_FEATURE(dc, VIS1); @@ -3939,11 +4011,13 @@ static void disas_sparc_insn(DisasContext * dc) gen_op_load_fpr_DT1(DFPREG(rs2)); gen_helper_fpadd32(); gen_op_store_DT0_fpr(DFPREG(rd)); + gen_update_fprs_dirty(DFPREG(rd)); break; case 0x053: /* VIS I fpadd32s */ CHECK_FPU_FEATURE(dc, VIS1); gen_helper_fpadd32s(cpu_fpr[rd], cpu_fpr[rs1], cpu_fpr[rs2]); + gen_update_fprs_dirty(rd); break; case 0x054: /* VIS I fpsub16 */ CHECK_FPU_FEATURE(dc, VIS1); @@ -3951,11 +4025,13 @@ static void disas_sparc_insn(DisasContext * dc) gen_op_load_fpr_DT1(DFPREG(rs2)); gen_helper_fpsub16(); gen_op_store_DT0_fpr(DFPREG(rd)); + gen_update_fprs_dirty(DFPREG(rd)); break; case 0x055: /* VIS I fpsub16s */ CHECK_FPU_FEATURE(dc, VIS1); gen_helper_fpsub16s(cpu_fpr[rd], cpu_fpr[rs1], cpu_fpr[rs2]); + gen_update_fprs_dirty(rd); break; case 0x056: /* VIS I fpsub32 */ CHECK_FPU_FEATURE(dc, VIS1); @@ -3963,20 +4039,24 @@ static void disas_sparc_insn(DisasContext * dc) gen_op_load_fpr_DT1(DFPREG(rs2)); gen_helper_fpsub32(); gen_op_store_DT0_fpr(DFPREG(rd)); + gen_update_fprs_dirty(DFPREG(rd)); break; case 0x057: /* VIS I fpsub32s */ CHECK_FPU_FEATURE(dc, VIS1); gen_helper_fpsub32s(cpu_fpr[rd], cpu_fpr[rs1], cpu_fpr[rs2]); + gen_update_fprs_dirty(rd); break; case 0x060: /* VIS I fzero */ CHECK_FPU_FEATURE(dc, VIS1); tcg_gen_movi_i32(cpu_fpr[DFPREG(rd)], 0); tcg_gen_movi_i32(cpu_fpr[DFPREG(rd) + 1], 0); + gen_update_fprs_dirty(DFPREG(rd)); break; case 0x061: /* VIS I fzeros */ CHECK_FPU_FEATURE(dc, VIS1); tcg_gen_movi_i32(cpu_fpr[rd], 0); + gen_update_fprs_dirty(rd); break; case 0x062: /* VIS I fnor */ CHECK_FPU_FEATURE(dc, VIS1); @@ -3985,10 +4065,12 @@ static void disas_sparc_insn(DisasContext * dc) tcg_gen_nor_i32(cpu_fpr[DFPREG(rd) + 1], cpu_fpr[DFPREG(rs1) + 1], cpu_fpr[DFPREG(rs2) + 1]); + gen_update_fprs_dirty(DFPREG(rd)); break; case 0x063: /* VIS I fnors */ CHECK_FPU_FEATURE(dc, VIS1); tcg_gen_nor_i32(cpu_fpr[rd], cpu_fpr[rs1], cpu_fpr[rs2]); + gen_update_fprs_dirty(rd); break; case 0x064: /* VIS I fandnot2 */ CHECK_FPU_FEATURE(dc, VIS1); @@ -3997,20 +4079,24 @@ static void disas_sparc_insn(DisasContext * dc) tcg_gen_andc_i32(cpu_fpr[DFPREG(rd) + 1], cpu_fpr[DFPREG(rs1) + 1], cpu_fpr[DFPREG(rs2) + 1]); + gen_update_fprs_dirty(DFPREG(rd)); break; case 0x065: /* VIS I fandnot2s */ CHECK_FPU_FEATURE(dc, VIS1); tcg_gen_andc_i32(cpu_fpr[rd], cpu_fpr[rs1], cpu_fpr[rs2]); + gen_update_fprs_dirty(rd); break; case 0x066: /* VIS I fnot2 */ CHECK_FPU_FEATURE(dc, VIS1); tcg_gen_not_i32(cpu_fpr[DFPREG(rd)], cpu_fpr[DFPREG(rs2)]); tcg_gen_not_i32(cpu_fpr[DFPREG(rd) + 1], cpu_fpr[DFPREG(rs2) + 1]); + gen_update_fprs_dirty(DFPREG(rd)); break; case 0x067: /* VIS I fnot2s */ CHECK_FPU_FEATURE(dc, VIS1); tcg_gen_not_i32(cpu_fpr[rd], cpu_fpr[rs2]); + gen_update_fprs_dirty(rd); break; case 0x068: /* VIS I fandnot1 */ CHECK_FPU_FEATURE(dc, VIS1); @@ -4019,20 +4105,24 @@ static void disas_sparc_insn(DisasContext * dc) tcg_gen_andc_i32(cpu_fpr[DFPREG(rd) + 1], cpu_fpr[DFPREG(rs2) + 1], cpu_fpr[DFPREG(rs1) + 1]); + gen_update_fprs_dirty(DFPREG(rd)); break; case 0x069: /* VIS I fandnot1s */ CHECK_FPU_FEATURE(dc, VIS1); tcg_gen_andc_i32(cpu_fpr[rd], cpu_fpr[rs2], cpu_fpr[rs1]); + gen_update_fprs_dirty(rd); break; case 0x06a: /* VIS I fnot1 */ CHECK_FPU_FEATURE(dc, VIS1); tcg_gen_not_i32(cpu_fpr[DFPREG(rd)], cpu_fpr[DFPREG(rs1)]); tcg_gen_not_i32(cpu_fpr[DFPREG(rd) + 1], cpu_fpr[DFPREG(rs1) + 1]); + gen_update_fprs_dirty(DFPREG(rd)); break; case 0x06b: /* VIS I fnot1s */ CHECK_FPU_FEATURE(dc, VIS1); tcg_gen_not_i32(cpu_fpr[rd], cpu_fpr[rs1]); + gen_update_fprs_dirty(rd); break; case 0x06c: /* VIS I fxor */ CHECK_FPU_FEATURE(dc, VIS1); @@ -4041,10 +4131,12 @@ static void disas_sparc_insn(DisasContext * dc) tcg_gen_xor_i32(cpu_fpr[DFPREG(rd) + 1], cpu_fpr[DFPREG(rs1) + 1], cpu_fpr[DFPREG(rs2) + 1]); + gen_update_fprs_dirty(DFPREG(rd)); break; case 0x06d: /* VIS I fxors */ CHECK_FPU_FEATURE(dc, VIS1); tcg_gen_xor_i32(cpu_fpr[rd], cpu_fpr[rs1], cpu_fpr[rs2]); + gen_update_fprs_dirty(rd); break; case 0x06e: /* VIS I fnand */ CHECK_FPU_FEATURE(dc, VIS1); @@ -4053,10 +4145,12 @@ static void disas_sparc_insn(DisasContext * dc) tcg_gen_nand_i32(cpu_fpr[DFPREG(rd) + 1], cpu_fpr[DFPREG(rs1) + 1], cpu_fpr[DFPREG(rs2) + 1]); + gen_update_fprs_dirty(DFPREG(rd)); break; case 0x06f: /* VIS I fnands */ CHECK_FPU_FEATURE(dc, VIS1); tcg_gen_nand_i32(cpu_fpr[rd], cpu_fpr[rs1], cpu_fpr[rs2]); + gen_update_fprs_dirty(rd); break; case 0x070: /* VIS I fand */ CHECK_FPU_FEATURE(dc, VIS1); @@ -4065,10 +4159,12 @@ static void disas_sparc_insn(DisasContext * dc) tcg_gen_and_i32(cpu_fpr[DFPREG(rd) + 1], cpu_fpr[DFPREG(rs1) + 1], cpu_fpr[DFPREG(rs2) + 1]); + gen_update_fprs_dirty(DFPREG(rd)); break; case 0x071: /* VIS I fands */ CHECK_FPU_FEATURE(dc, VIS1); tcg_gen_and_i32(cpu_fpr[rd], cpu_fpr[rs1], cpu_fpr[rs2]); + gen_update_fprs_dirty(rd); break; case 0x072: /* VIS I fxnor */ CHECK_FPU_FEATURE(dc, VIS1); @@ -4078,21 +4174,25 @@ static void disas_sparc_insn(DisasContext * dc) tcg_gen_xori_i32(cpu_tmp32, cpu_fpr[DFPREG(rs2) + 1], -1); tcg_gen_xor_i32(cpu_fpr[DFPREG(rd) + 1], cpu_tmp32, cpu_fpr[DFPREG(rs1) + 1]); + gen_update_fprs_dirty(DFPREG(rd)); break; case 0x073: /* VIS I fxnors */ CHECK_FPU_FEATURE(dc, VIS1); tcg_gen_xori_i32(cpu_tmp32, cpu_fpr[rs2], -1); tcg_gen_xor_i32(cpu_fpr[rd], cpu_tmp32, cpu_fpr[rs1]); + gen_update_fprs_dirty(rd); break; case 0x074: /* VIS I fsrc1 */ CHECK_FPU_FEATURE(dc, VIS1); tcg_gen_mov_i32(cpu_fpr[DFPREG(rd)], cpu_fpr[DFPREG(rs1)]); tcg_gen_mov_i32(cpu_fpr[DFPREG(rd) + 1], cpu_fpr[DFPREG(rs1) + 1]); + gen_update_fprs_dirty(DFPREG(rd)); break; case 0x075: /* VIS I fsrc1s */ CHECK_FPU_FEATURE(dc, VIS1); tcg_gen_mov_i32(cpu_fpr[rd], cpu_fpr[rs1]); + gen_update_fprs_dirty(rd); break; case 0x076: /* VIS I fornot2 */ CHECK_FPU_FEATURE(dc, VIS1); @@ -4101,19 +4201,23 @@ static void disas_sparc_insn(DisasContext * dc) tcg_gen_orc_i32(cpu_fpr[DFPREG(rd) + 1], cpu_fpr[DFPREG(rs1) + 1], cpu_fpr[DFPREG(rs2) + 1]); + gen_update_fprs_dirty(DFPREG(rd)); break; case 0x077: /* VIS I fornot2s */ CHECK_FPU_FEATURE(dc, VIS1); tcg_gen_orc_i32(cpu_fpr[rd], cpu_fpr[rs1], cpu_fpr[rs2]); + gen_update_fprs_dirty(rd); break; case 0x078: /* VIS I fsrc2 */ CHECK_FPU_FEATURE(dc, VIS1); gen_op_load_fpr_DT0(DFPREG(rs2)); gen_op_store_DT0_fpr(DFPREG(rd)); + gen_update_fprs_dirty(DFPREG(rd)); break; case 0x079: /* VIS I fsrc2s */ CHECK_FPU_FEATURE(dc, VIS1); tcg_gen_mov_i32(cpu_fpr[rd], cpu_fpr[rs2]); + gen_update_fprs_dirty(rd); break; case 0x07a: /* VIS I fornot1 */ CHECK_FPU_FEATURE(dc, VIS1); @@ -4122,10 +4226,12 @@ static void disas_sparc_insn(DisasContext * dc) tcg_gen_orc_i32(cpu_fpr[DFPREG(rd) + 1], cpu_fpr[DFPREG(rs2) + 1], cpu_fpr[DFPREG(rs1) + 1]); + gen_update_fprs_dirty(DFPREG(rd)); break; case 0x07b: /* VIS I fornot1s */ CHECK_FPU_FEATURE(dc, VIS1); tcg_gen_orc_i32(cpu_fpr[rd], cpu_fpr[rs2], cpu_fpr[rs1]); + gen_update_fprs_dirty(rd); break; case 0x07c: /* VIS I for */ CHECK_FPU_FEATURE(dc, VIS1); @@ -4134,19 +4240,23 @@ static void disas_sparc_insn(DisasContext * dc) tcg_gen_or_i32(cpu_fpr[DFPREG(rd) + 1], cpu_fpr[DFPREG(rs1) + 1], cpu_fpr[DFPREG(rs2) + 1]); + gen_update_fprs_dirty(DFPREG(rd)); break; case 0x07d: /* VIS I fors */ CHECK_FPU_FEATURE(dc, VIS1); tcg_gen_or_i32(cpu_fpr[rd], cpu_fpr[rs1], cpu_fpr[rs2]); + gen_update_fprs_dirty(rd); break; case 0x07e: /* VIS I fone */ CHECK_FPU_FEATURE(dc, VIS1); tcg_gen_movi_i32(cpu_fpr[DFPREG(rd)], -1); tcg_gen_movi_i32(cpu_fpr[DFPREG(rd) + 1], -1); + gen_update_fprs_dirty(DFPREG(rd)); break; case 0x07f: /* VIS I fones */ CHECK_FPU_FEATURE(dc, VIS1); tcg_gen_movi_i32(cpu_fpr[rd], -1); + gen_update_fprs_dirty(rd); break; case 0x080: /* VIS I shutdown */ case 0x081: /* VIS II siam */ @@ -4492,6 +4602,7 @@ static void disas_sparc_insn(DisasContext * dc) } save_state(dc, cpu_cond); gen_ldf_asi(cpu_addr, insn, 4, rd); + gen_update_fprs_dirty(rd); goto skip_move; case 0x33: /* V9 lddfa */ if (gen_trap_ifnofpu(dc, cpu_cond)) { @@ -4499,6 +4610,7 @@ static void disas_sparc_insn(DisasContext * dc) } save_state(dc, cpu_cond); gen_ldf_asi(cpu_addr, insn, 8, DFPREG(rd)); + gen_update_fprs_dirty(DFPREG(rd)); goto skip_move; case 0x3d: /* V9 prefetcha, no effect */ goto skip_move; @@ -4509,6 +4621,7 @@ static void disas_sparc_insn(DisasContext * dc) } save_state(dc, cpu_cond); gen_ldf_asi(cpu_addr, insn, 16, QFPREG(rd)); + gen_update_fprs_dirty(QFPREG(rd)); goto skip_move; #endif default: @@ -4527,6 +4640,7 @@ static void disas_sparc_insn(DisasContext * dc) gen_address_mask(dc, cpu_addr); tcg_gen_qemu_ld32u(cpu_tmp0, cpu_addr, dc->mem_idx); tcg_gen_trunc_tl_i32(cpu_fpr[rd], cpu_tmp0); + gen_update_fprs_dirty(rd); break; case 0x21: /* ldfsr, V9 ldxfsr */ #ifdef TARGET_SPARC64 @@ -4556,6 +4670,7 @@ static void disas_sparc_insn(DisasContext * dc) gen_helper_ldqf(cpu_addr, r_const); tcg_temp_free_i32(r_const); gen_op_store_QT0_fpr(QFPREG(rd)); + gen_update_fprs_dirty(QFPREG(rd)); } break; case 0x23: /* lddf, load double fpreg */ @@ -4567,6 +4682,7 @@ static void disas_sparc_insn(DisasContext * dc) gen_helper_lddf(cpu_addr, r_const); tcg_temp_free_i32(r_const); gen_op_store_DT0_fpr(DFPREG(rd)); + gen_update_fprs_dirty(DFPREG(rd)); } break; default: From 104bf02eb50e080ac9d0de5905f80f9a09730154 Mon Sep 17 00:00:00 2001 From: Michael Tokarev Date: Thu, 12 May 2011 18:44:17 +0400 Subject: [PATCH 138/230] revamp acpitable parsing and allow to specify complete (headerful) table This patch almost rewrites acpi_table_add() function (but still leaves it using old get_param_value() interface). The result is that it's now possible to specify whole table (together with a header) in an external file, instead of just data portion, with a new file= parameter, but at the same time it's still possible to specify header fields as before. Now with the checkpatch.pl formatting fixes, thanks to Stefan Hajnoczi for suggestions, with changes from Isaku Yamahata, and with my further refinements. Signed-off-by: Michael Tokarev Cc: Isaku Yamahata Cc: John Baboval Cc: Blue Swirl [yamahata@valinux.co.jp: fix compile error, comment fallthrough] Signed-off-by: Blue Swirl --- hw/acpi.c | 320 +++++++++++++++++++++++++++--------------------- qemu-options.hx | 7 +- 2 files changed, 189 insertions(+), 138 deletions(-) diff --git a/hw/acpi.c b/hw/acpi.c index ad40fb4c3c..79ec66c147 100644 --- a/hw/acpi.c +++ b/hw/acpi.c @@ -20,19 +20,30 @@ #include "pc.h" #include "acpi.h" -struct acpi_table_header -{ - char signature [4]; /* ACPI signature (4 ASCII characters) */ +struct acpi_table_header { + uint16_t _length; /* our length, not actual part of the hdr */ + /* XXX why we have 2 length fields here? */ + char sig[4]; /* ACPI signature (4 ASCII characters) */ uint32_t length; /* Length of table, in bytes, including header */ uint8_t revision; /* ACPI Specification minor version # */ uint8_t checksum; /* To make sum of entire table == 0 */ - char oem_id [6]; /* OEM identification */ - char oem_table_id [8]; /* OEM table identification */ + char oem_id[6]; /* OEM identification */ + char oem_table_id[8]; /* OEM table identification */ uint32_t oem_revision; /* OEM revision number */ - char asl_compiler_id [4]; /* ASL compiler vendor ID */ + char asl_compiler_id[4]; /* ASL compiler vendor ID */ uint32_t asl_compiler_revision; /* ASL compiler revision number */ } __attribute__((packed)); +#define ACPI_TABLE_HDR_SIZE sizeof(struct acpi_table_header) +#define ACPI_TABLE_PFX_SIZE sizeof(uint16_t) /* size of the extra prefix */ + +static const char dfl_hdr[ACPI_TABLE_HDR_SIZE] = + "\0\0" /* fake _length (2) */ + "QEMU\0\0\0\0\1\0" /* sig (4), len(4), revno (1), csum (1) */ + "QEMUQEQEMUQEMU\1\0\0\0" /* OEM id (6), table (8), revno (4) */ + "QEMU\1\0\0\0" /* ASL compiler ID (4), version (4) */ + ; + char *acpi_tables; size_t acpi_tables_len; @@ -40,163 +51,198 @@ static int acpi_checksum(const uint8_t *data, int len) { int sum, i; sum = 0; - for(i = 0; i < len; i++) + for (i = 0; i < len; i++) { sum += data[i]; + } return (-sum) & 0xff; } +/* like strncpy() but zero-fills the tail of destination */ +static void strzcpy(char *dst, const char *src, size_t size) +{ + size_t len = strlen(src); + if (len >= size) { + len = size; + } else { + memset(dst + len, 0, size - len); + } + memcpy(dst, src, len); +} + +/* XXX fixme: this function uses obsolete argument parsing interface */ int acpi_table_add(const char *t) { - static const char *dfl_id = "QEMUQEMU"; char buf[1024], *p, *f; - struct acpi_table_header acpi_hdr; unsigned long val; - uint32_t length; - struct acpi_table_header *acpi_hdr_p; - size_t off; + size_t len, start, allen; + bool has_header; + int changed; + int r; + struct acpi_table_header hdr; - memset(&acpi_hdr, 0, sizeof(acpi_hdr)); - - if (get_param_value(buf, sizeof(buf), "sig", t)) { - strncpy(acpi_hdr.signature, buf, 4); - } else { - strncpy(acpi_hdr.signature, dfl_id, 4); - } - if (get_param_value(buf, sizeof(buf), "rev", t)) { - val = strtoul(buf, &p, 10); - if (val > 255 || *p != '\0') - goto out; - } else { - val = 1; - } - acpi_hdr.revision = (int8_t)val; - - if (get_param_value(buf, sizeof(buf), "oem_id", t)) { - strncpy(acpi_hdr.oem_id, buf, 6); - } else { - strncpy(acpi_hdr.oem_id, dfl_id, 6); - } - - if (get_param_value(buf, sizeof(buf), "oem_table_id", t)) { - strncpy(acpi_hdr.oem_table_id, buf, 8); - } else { - strncpy(acpi_hdr.oem_table_id, dfl_id, 8); - } - - if (get_param_value(buf, sizeof(buf), "oem_rev", t)) { - val = strtol(buf, &p, 10); - if(*p != '\0') - goto out; - } else { - val = 1; - } - acpi_hdr.oem_revision = cpu_to_le32(val); - - if (get_param_value(buf, sizeof(buf), "asl_compiler_id", t)) { - strncpy(acpi_hdr.asl_compiler_id, buf, 4); - } else { - strncpy(acpi_hdr.asl_compiler_id, dfl_id, 4); - } - - if (get_param_value(buf, sizeof(buf), "asl_compiler_rev", t)) { - val = strtol(buf, &p, 10); - if(*p != '\0') - goto out; - } else { - val = 1; - } - acpi_hdr.asl_compiler_revision = cpu_to_le32(val); - - if (!get_param_value(buf, sizeof(buf), "data", t)) { - buf[0] = '\0'; - } - - length = sizeof(acpi_hdr); - - f = buf; - while (buf[0]) { - struct stat s; - char *n = strchr(f, ':'); - if (n) - *n = '\0'; - if(stat(f, &s) < 0) { - fprintf(stderr, "Can't stat file '%s': %s\n", f, strerror(errno)); - goto out; - } - length += s.st_size; - if (!n) - break; - *n = ':'; - f = n + 1; + r = 0; + r |= get_param_value(buf, sizeof(buf), "data", t) ? 1 : 0; + r |= get_param_value(buf, sizeof(buf), "file", t) ? 2 : 0; + switch (r) { + case 0: + buf[0] = '\0'; + /* fallthrough for default behavior */ + case 1: + has_header = false; + break; + case 2: + has_header = true; + break; + default: + fprintf(stderr, "acpitable: both data and file are specified\n"); + return -1; } if (!acpi_tables) { - acpi_tables_len = sizeof(uint16_t); - acpi_tables = qemu_mallocz(acpi_tables_len); + allen = sizeof(uint16_t); + acpi_tables = qemu_mallocz(allen); + } else { + allen = acpi_tables_len; } - acpi_tables = qemu_realloc(acpi_tables, - acpi_tables_len + sizeof(uint16_t) + length); - p = acpi_tables + acpi_tables_len; - acpi_tables_len += sizeof(uint16_t) + length; - *(uint16_t*)p = cpu_to_le32(length); - p += sizeof(uint16_t); - memcpy(p, &acpi_hdr, sizeof(acpi_hdr)); - off = sizeof(acpi_hdr); + start = allen; + acpi_tables = qemu_realloc(acpi_tables, start + ACPI_TABLE_HDR_SIZE); + allen += has_header ? ACPI_TABLE_PFX_SIZE : ACPI_TABLE_HDR_SIZE; - f = buf; - while (buf[0]) { - struct stat s; - int fd; - char *n = strchr(f, ':'); - if (n) - *n = '\0'; - fd = open(f, O_RDONLY); + /* now read in the data files, reallocating buffer as needed */ - if(fd < 0) - goto out; - if(fstat(fd, &s) < 0) { - close(fd); - goto out; + for (f = strtok(buf, ":"); f; f = strtok(NULL, ":")) { + int fd = open(f, O_RDONLY); + + if (fd < 0) { + fprintf(stderr, "can't open file %s: %s\n", f, strerror(errno)); + return -1; } - /* off < length is necessary because file size can be changed - under our foot */ - while(s.st_size && off < length) { - int r; - r = read(fd, p + off, s.st_size); - if (r > 0) { - off += r; - s.st_size -= r; - } else if ((r < 0 && errno != EINTR) || r == 0) { + for (;;) { + char data[8192]; + r = read(fd, data, sizeof(data)); + if (r == 0) { + break; + } else if (r > 0) { + acpi_tables = qemu_realloc(acpi_tables, allen + r); + memcpy(acpi_tables + allen, data, r); + allen += r; + } else if (errno != EINTR) { + fprintf(stderr, "can't read file %s: %s\n", + f, strerror(errno)); close(fd); - goto out; + return -1; } } close(fd); - if (!n) - break; - f = n + 1; - } - if (off < length) { - /* don't pass random value in process to guest */ - memset(p + off, 0, length - off); } - acpi_hdr_p = (struct acpi_table_header*)p; - acpi_hdr_p->length = cpu_to_le32(length); - acpi_hdr_p->checksum = acpi_checksum((uint8_t*)p, length); - /* increase number of tables */ - (*(uint16_t*)acpi_tables) = - cpu_to_le32(le32_to_cpu(*(uint16_t*)acpi_tables) + 1); - return 0; -out: - if (acpi_tables) { - qemu_free(acpi_tables); - acpi_tables = NULL; + /* now fill in the header fields */ + + f = acpi_tables + start; /* start of the table */ + changed = 0; + + /* copy the header to temp place to align the fields */ + memcpy(&hdr, has_header ? f : dfl_hdr, ACPI_TABLE_HDR_SIZE); + + /* length of the table minus our prefix */ + len = allen - start - ACPI_TABLE_PFX_SIZE; + + hdr._length = cpu_to_le16(len); + + if (get_param_value(buf, sizeof(buf), "sig", t)) { + strzcpy(hdr.sig, buf, sizeof(hdr.sig)); + ++changed; } - return -1; + + /* length of the table including header, in bytes */ + if (has_header) { + /* check if actual length is correct */ + val = le32_to_cpu(hdr.length); + if (val != len) { + fprintf(stderr, + "warning: acpitable has wrong length," + " header says %lu, actual size %zu bytes\n", + val, len); + ++changed; + } + } + /* we may avoid putting length here if has_header is true */ + hdr.length = cpu_to_le32(len); + + if (get_param_value(buf, sizeof(buf), "rev", t)) { + val = strtoul(buf, &p, 0); + if (val > 255 || *p) { + fprintf(stderr, "acpitable: \"rev=%s\" is invalid\n", buf); + return -1; + } + hdr.revision = (uint8_t)val; + ++changed; + } + + if (get_param_value(buf, sizeof(buf), "oem_id", t)) { + strzcpy(hdr.oem_id, buf, sizeof(hdr.oem_id)); + ++changed; + } + + if (get_param_value(buf, sizeof(buf), "oem_table_id", t)) { + strzcpy(hdr.oem_table_id, buf, sizeof(hdr.oem_table_id)); + ++changed; + } + + if (get_param_value(buf, sizeof(buf), "oem_rev", t)) { + val = strtol(buf, &p, 0); + if (*p) { + fprintf(stderr, "acpitable: \"oem_rev=%s\" is invalid\n", buf); + return -1; + } + hdr.oem_revision = cpu_to_le32(val); + ++changed; + } + + if (get_param_value(buf, sizeof(buf), "asl_compiler_id", t)) { + strzcpy(hdr.asl_compiler_id, buf, sizeof(hdr.asl_compiler_id)); + ++changed; + } + + if (get_param_value(buf, sizeof(buf), "asl_compiler_rev", t)) { + val = strtol(buf, &p, 0); + if (*p) { + fprintf(stderr, "acpitable: \"%s=%s\" is invalid\n", + "asl_compiler_rev", buf); + return -1; + } + hdr.asl_compiler_revision = cpu_to_le32(val); + ++changed; + } + + if (!has_header && !changed) { + fprintf(stderr, "warning: acpitable: no table headers are specified\n"); + } + + + /* now calculate checksum of the table, complete with the header */ + /* we may as well leave checksum intact if has_header is true */ + /* alternatively there may be a way to set cksum to a given value */ + hdr.checksum = 0; /* for checksum calculation */ + + /* put header back */ + memcpy(f, &hdr, sizeof(hdr)); + + if (changed || !has_header || 1) { + ((struct acpi_table_header *)f)->checksum = + acpi_checksum((uint8_t *)f + ACPI_TABLE_PFX_SIZE, len); + } + + /* increase number of tables */ + (*(uint16_t *)acpi_tables) = + cpu_to_le32(le32_to_cpu(*(uint16_t *)acpi_tables) + 1); + + acpi_tables_len = allen; + return 0; + } /* ACPI PM1a EVT */ diff --git a/qemu-options.hx b/qemu-options.hx index c77f868d40..d86815dc04 100644 --- a/qemu-options.hx +++ b/qemu-options.hx @@ -1074,12 +1074,17 @@ Enable virtio balloon device (default), optionally with PCI address ETEXI DEF("acpitable", HAS_ARG, QEMU_OPTION_acpitable, - "-acpitable [sig=str][,rev=n][,oem_id=str][,oem_table_id=str][,oem_rev=n][,asl_compiler_id=str][,asl_compiler_rev=n][,data=file1[:file2]...]\n" + "-acpitable [sig=str][,rev=n][,oem_id=str][,oem_table_id=str][,oem_rev=n][,asl_compiler_id=str][,asl_compiler_rev=n][,{data|file}=file1[:file2]...]\n" " ACPI table description\n", QEMU_ARCH_I386) STEXI @item -acpitable [sig=@var{str}][,rev=@var{n}][,oem_id=@var{str}][,oem_table_id=@var{str}][,oem_rev=@var{n}] [,asl_compiler_id=@var{str}][,asl_compiler_rev=@var{n}][,data=@var{file1}[:@var{file2}]...] @findex -acpitable Add ACPI table with specified header fields and context from specified files. +For file=, take whole ACPI table from the specified files, including all +ACPI headers (possible overridden by other options). +For data=, only data +portion of the table is used, all header information is specified in the +command line. ETEXI DEF("smbios", HAS_ARG, QEMU_OPTION_smbios, From 3e4571724fb92c77de81d8b54957de8232be6706 Mon Sep 17 00:00:00 2001 From: Blue Swirl Date: Wed, 13 Jul 2011 12:44:15 +0000 Subject: [PATCH 139/230] exec.h cleanup Move softmmu_exec.h include directives from target-*/exec.h to target-*/op_helper.c. Move also various other stuff only used in op_helper.c there. Define global env in dyngen-exec.h. For i386, move wrappers for segment and FPU helpers from user-exec.c to op_helper.c. Implement raise_exception_err_env() to handle dynamic CPUState. Move the function declarations to cpu.h since they can be used outside of op_helper.c context. LM32, s390x, UniCore32: remove unused cpu_halted(), regs_to_env() and env_to_regs(). ARM: make raise_exception() static. Convert #include "exec.h" to #include "cpu.h" #include "dyngen-exec.h" and remove now unused target-*/exec.h. Signed-off-by: Blue Swirl --- dyngen-exec.h | 2 + hw/spapr_hcall.c | 2 +- target-alpha/exec.h | 39 -------- target-alpha/op_helper.c | 7 +- target-arm/exec.h | 30 ------- target-arm/op_helper.c | 9 +- target-cris/exec.h | 28 ------ target-cris/op_helper.c | 4 +- target-i386/cpu.h | 3 + target-i386/exec.h | 142 ----------------------------- target-i386/helper.c | 2 - target-i386/op_helper.c | 165 +++++++++++++++++++++++++++++++++- target-lm32/exec.h | 38 -------- target-lm32/op_helper.c | 3 +- target-m68k/exec.h | 28 ------ target-m68k/op_helper.c | 5 +- target-microblaze/exec.h | 27 ------ target-microblaze/op_helper.c | 5 +- target-mips/cpu.h | 2 + target-mips/exec.h | 60 ------------- target-mips/op_helper.c | 56 +++++++++++- target-ppc/exec.h | 34 ------- target-ppc/op_helper.c | 7 +- target-s390x/exec.h | 37 -------- target-s390x/op_helper.c | 4 +- target-sh4/exec.h | 33 ------- target-sh4/op_helper.c | 4 +- target-sparc/exec.h | 15 ---- target-sparc/op_helper.c | 7 +- target-unicore32/exec.h | 43 --------- target-unicore32/op_helper.c | 3 +- user-exec.c | 49 +--------- 32 files changed, 273 insertions(+), 620 deletions(-) delete mode 100644 target-alpha/exec.h delete mode 100644 target-arm/exec.h delete mode 100644 target-cris/exec.h delete mode 100644 target-i386/exec.h delete mode 100644 target-lm32/exec.h delete mode 100644 target-m68k/exec.h delete mode 100644 target-microblaze/exec.h delete mode 100644 target-mips/exec.h delete mode 100644 target-ppc/exec.h delete mode 100644 target-s390x/exec.h delete mode 100644 target-sh4/exec.h delete mode 100644 target-sparc/exec.h delete mode 100644 target-unicore32/exec.h diff --git a/dyngen-exec.h b/dyngen-exec.h index db00fbae04..cc1e4fb09d 100644 --- a/dyngen-exec.h +++ b/dyngen-exec.h @@ -64,6 +64,8 @@ typedef void * host_reg_t; #error unsupported CPU #endif +register CPUState *env asm(AREG0); + #define xglue(x, y) x ## y #define glue(x, y) xglue(x, y) #define stringify(s) tostring(s) diff --git a/hw/spapr_hcall.c b/hw/spapr_hcall.c index 5cd8d8f5ae..f7ead04a96 100644 --- a/hw/spapr_hcall.c +++ b/hw/spapr_hcall.c @@ -1,9 +1,9 @@ #include "sysemu.h" #include "cpu.h" +#include "dyngen-exec.h" #include "qemu-char.h" #include "sysemu.h" #include "qemu-char.h" -#include "exec.h" #include "helper_regs.h" #include "hw/spapr.h" diff --git a/target-alpha/exec.h b/target-alpha/exec.h deleted file mode 100644 index afb01d3727..0000000000 --- a/target-alpha/exec.h +++ /dev/null @@ -1,39 +0,0 @@ -/* - * Alpha emulation cpu run-time definitions for qemu. - * - * Copyright (c) 2007 Jocelyn Mayer - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2 of the License, or (at your option) any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, see . - */ - -#if !defined (__ALPHA_EXEC_H__) -#define __ALPHA_EXEC_H__ - -#include "config.h" - -#include "dyngen-exec.h" - -#define TARGET_LONG_BITS 64 - -register struct CPUAlphaState *env asm(AREG0); - -#define FP_STATUS (env->fp_status) - -#include "cpu.h" - -#if !defined(CONFIG_USER_ONLY) -#include "softmmu_exec.h" -#endif /* !defined(CONFIG_USER_ONLY) */ - -#endif /* !defined (__ALPHA_EXEC_H__) */ diff --git a/target-alpha/op_helper.c b/target-alpha/op_helper.c index 8f39154391..c2bb679090 100644 --- a/target-alpha/op_helper.c +++ b/target-alpha/op_helper.c @@ -17,12 +17,15 @@ * License along with this library; if not, see . */ -#include "exec.h" +#include "cpu.h" +#include "dyngen-exec.h" #include "host-utils.h" #include "softfloat.h" #include "helper.h" #include "qemu-timer.h" +#define FP_STATUS (env->fp_status) + /*****************************************************************************/ /* Exceptions processing helpers */ @@ -1311,6 +1314,8 @@ void QEMU_NORETURN cpu_unassigned_access(CPUState *env1, dynamic_excp(EXCP_MCHK, 0); } +#include "softmmu_exec.h" + #define MMUSUFFIX _mmu #define ALIGNED_ONLY diff --git a/target-arm/exec.h b/target-arm/exec.h deleted file mode 100644 index 6793288d43..0000000000 --- a/target-arm/exec.h +++ /dev/null @@ -1,30 +0,0 @@ -/* - * ARM execution defines - * - * Copyright (c) 2003 Fabrice Bellard - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2 of the License, or (at your option) any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, see . - */ -#include "config.h" -#include "dyngen-exec.h" - -register struct CPUARMState *env asm(AREG0); - -#include "cpu.h" - -#if !defined(CONFIG_USER_ONLY) -#include "softmmu_exec.h" -#endif - -void raise_exception(int); diff --git a/target-arm/op_helper.c b/target-arm/op_helper.c index 46358844c5..57e4977cff 100644 --- a/target-arm/op_helper.c +++ b/target-arm/op_helper.c @@ -16,17 +16,20 @@ * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, see . */ -#include "exec.h" +#include "cpu.h" +#include "dyngen-exec.h" #include "helper.h" #define SIGNBIT (uint32_t)0x80000000 #define SIGNBIT64 ((uint64_t)1 << 63) -void raise_exception(int tt) +#if !defined(CONFIG_USER_ONLY) +static void raise_exception(int tt) { env->exception_index = tt; cpu_loop_exit(env); } +#endif uint32_t HELPER(neon_tbl)(uint32_t ireg, uint32_t def, uint32_t rn, uint32_t maxindex) @@ -52,6 +55,8 @@ uint32_t HELPER(neon_tbl)(uint32_t ireg, uint32_t def, #if !defined(CONFIG_USER_ONLY) +#include "softmmu_exec.h" + #define MMUSUFFIX _mmu #define SHIFT 0 diff --git a/target-cris/exec.h b/target-cris/exec.h deleted file mode 100644 index 3294abe393..0000000000 --- a/target-cris/exec.h +++ /dev/null @@ -1,28 +0,0 @@ -/* - * CRIS execution defines - * - * Copyright (c) 2007 AXIS Communications AB - * Written by Edgar E. Iglesias - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2 of the License, or (at your option) any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, see . - */ -#include "dyngen-exec.h" - -register struct CPUCRISState *env asm(AREG0); - -#include "cpu.h" - -#if !defined(CONFIG_USER_ONLY) -#include "softmmu_exec.h" -#endif diff --git a/target-cris/op_helper.c b/target-cris/op_helper.c index b3ddd33e02..246f08fcf6 100644 --- a/target-cris/op_helper.c +++ b/target-cris/op_helper.c @@ -18,7 +18,8 @@ * License along with this library; if not, see . */ -#include "exec.h" +#include "cpu.h" +#include "dyngen-exec.h" #include "mmu.h" #include "helper.h" #include "host-utils.h" @@ -35,6 +36,7 @@ #endif #if !defined(CONFIG_USER_ONLY) +#include "softmmu_exec.h" #define MMUSUFFIX _mmu diff --git a/target-i386/cpu.h b/target-i386/cpu.h index 9819b5fdb9..dd6c5fab3b 100644 --- a/target-i386/cpu.h +++ b/target-i386/cpu.h @@ -1050,6 +1050,9 @@ void cpu_x86_inject_mce(Monitor *mon, CPUState *cenv, int bank, /* op_helper.c */ void do_interrupt(CPUState *env); void do_interrupt_x86_hardirq(CPUState *env, int intno, int is_hw); +void QEMU_NORETURN raise_exception_env(int exception_index, CPUState *nenv); +void QEMU_NORETURN raise_exception_err_env(CPUState *nenv, int exception_index, + int error_code); void do_smm_enter(CPUState *env1); diff --git a/target-i386/exec.h b/target-i386/exec.h deleted file mode 100644 index dd9bce4eed..0000000000 --- a/target-i386/exec.h +++ /dev/null @@ -1,142 +0,0 @@ -/* - * i386 execution defines - * - * Copyright (c) 2003 Fabrice Bellard - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2 of the License, or (at your option) any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, see . - */ -#include "config.h" -#include "dyngen-exec.h" - -/* XXX: factorize this mess */ -#ifdef TARGET_X86_64 -#define TARGET_LONG_BITS 64 -#else -#define TARGET_LONG_BITS 32 -#endif - -#include "cpu-defs.h" - -register struct CPUX86State *env asm(AREG0); - -#include "qemu-common.h" -#include "qemu-log.h" - -#include "cpu.h" - -/* op_helper.c */ -void QEMU_NORETURN raise_exception_err(int exception_index, int error_code); -void QEMU_NORETURN raise_exception(int exception_index); -void QEMU_NORETURN raise_exception_env(int exception_index, CPUState *nenv); - -/* n must be a constant to be efficient */ -static inline target_long lshift(target_long x, int n) -{ - if (n >= 0) - return x << n; - else - return x >> (-n); -} - -#include "helper.h" - -#if !defined(CONFIG_USER_ONLY) - -#include "softmmu_exec.h" - -#endif /* !defined(CONFIG_USER_ONLY) */ - -#define RC_MASK 0xc00 -#define RC_NEAR 0x000 -#define RC_DOWN 0x400 -#define RC_UP 0x800 -#define RC_CHOP 0xc00 - -#define MAXTAN 9223372036854775808.0 - -/* the following deal with x86 long double-precision numbers */ -#define MAXEXPD 0x7fff -#define EXPBIAS 16383 -#define EXPD(fp) (fp.l.upper & 0x7fff) -#define SIGND(fp) ((fp.l.upper) & 0x8000) -#define MANTD(fp) (fp.l.lower) -#define BIASEXPONENT(fp) fp.l.upper = (fp.l.upper & ~(0x7fff)) | EXPBIAS - -static inline void fpush(void) -{ - env->fpstt = (env->fpstt - 1) & 7; - env->fptags[env->fpstt] = 0; /* validate stack entry */ -} - -static inline void fpop(void) -{ - env->fptags[env->fpstt] = 1; /* invvalidate stack entry */ - env->fpstt = (env->fpstt + 1) & 7; -} - -static inline floatx80 helper_fldt(target_ulong ptr) -{ - CPU_LDoubleU temp; - - temp.l.lower = ldq(ptr); - temp.l.upper = lduw(ptr + 8); - return temp.d; -} - -static inline void helper_fstt(floatx80 f, target_ulong ptr) -{ - CPU_LDoubleU temp; - - temp.d = f; - stq(ptr, temp.l.lower); - stw(ptr + 8, temp.l.upper); -} - -#define FPUS_IE (1 << 0) -#define FPUS_DE (1 << 1) -#define FPUS_ZE (1 << 2) -#define FPUS_OE (1 << 3) -#define FPUS_UE (1 << 4) -#define FPUS_PE (1 << 5) -#define FPUS_SF (1 << 6) -#define FPUS_SE (1 << 7) -#define FPUS_B (1 << 15) - -#define FPUC_EM 0x3f - -static inline uint32_t compute_eflags(void) -{ - return env->eflags | helper_cc_compute_all(CC_OP) | (DF & DF_MASK); -} - -/* NOTE: CC_OP must be modified manually to CC_OP_EFLAGS */ -static inline void load_eflags(int eflags, int update_mask) -{ - CC_SRC = eflags & (CC_O | CC_S | CC_Z | CC_A | CC_P | CC_C); - DF = 1 - (2 * ((eflags >> 10) & 1)); - env->eflags = (env->eflags & ~update_mask) | - (eflags & update_mask) | 0x2; -} - -/* load efer and update the corresponding hflags. XXX: do consistency - checks with cpuid bits ? */ -static inline void cpu_load_efer(CPUState *env, uint64_t val) -{ - env->efer = val; - env->hflags &= ~(HF_LMA_MASK | HF_SVME_MASK); - if (env->efer & MSR_EFER_LMA) - env->hflags |= HF_LMA_MASK; - if (env->efer & MSR_EFER_SVME) - env->hflags |= HF_SVME_MASK; -} diff --git a/target-i386/helper.c b/target-i386/helper.c index e9be104293..182009a4c2 100644 --- a/target-i386/helper.c +++ b/target-i386/helper.c @@ -1027,8 +1027,6 @@ int check_hw_breakpoints(CPUState *env, int force_dr6_update) static CPUDebugExcpHandler *prev_debug_excp_handler; -void raise_exception_env(int exception_index, CPUState *env); - static void breakpoint_handler(CPUState *env) { CPUBreakpoint *bp; diff --git a/target-i386/op_helper.c b/target-i386/op_helper.c index 315e18b9a4..138093454d 100644 --- a/target-i386/op_helper.c +++ b/target-i386/op_helper.c @@ -18,13 +18,21 @@ */ #include -#include "exec.h" +#include "cpu.h" +#include "dyngen-exec.h" #include "host-utils.h" #include "ioport.h" +#include "qemu-common.h" +#include "qemu-log.h" +#include "cpu-defs.h" +#include "helper.h" + +#if !defined(CONFIG_USER_ONLY) +#include "softmmu_exec.h" +#endif /* !defined(CONFIG_USER_ONLY) */ //#define DEBUG_PCALL - #ifdef DEBUG_PCALL # define LOG_PCALL(...) qemu_log_mask(CPU_LOG_PCALL, ## __VA_ARGS__) # define LOG_PCALL_STATE(env) \ @@ -34,6 +42,101 @@ # define LOG_PCALL_STATE(env) do { } while (0) #endif +/* n must be a constant to be efficient */ +static inline target_long lshift(target_long x, int n) +{ + if (n >= 0) { + return x << n; + } else { + return x >> (-n); + } +} + +#define RC_MASK 0xc00 +#define RC_NEAR 0x000 +#define RC_DOWN 0x400 +#define RC_UP 0x800 +#define RC_CHOP 0xc00 + +#define MAXTAN 9223372036854775808.0 + +/* the following deal with x86 long double-precision numbers */ +#define MAXEXPD 0x7fff +#define EXPBIAS 16383 +#define EXPD(fp) (fp.l.upper & 0x7fff) +#define SIGND(fp) ((fp.l.upper) & 0x8000) +#define MANTD(fp) (fp.l.lower) +#define BIASEXPONENT(fp) fp.l.upper = (fp.l.upper & ~(0x7fff)) | EXPBIAS + +static inline void fpush(void) +{ + env->fpstt = (env->fpstt - 1) & 7; + env->fptags[env->fpstt] = 0; /* validate stack entry */ +} + +static inline void fpop(void) +{ + env->fptags[env->fpstt] = 1; /* invvalidate stack entry */ + env->fpstt = (env->fpstt + 1) & 7; +} + +static inline floatx80 helper_fldt(target_ulong ptr) +{ + CPU_LDoubleU temp; + + temp.l.lower = ldq(ptr); + temp.l.upper = lduw(ptr + 8); + return temp.d; +} + +static inline void helper_fstt(floatx80 f, target_ulong ptr) +{ + CPU_LDoubleU temp; + + temp.d = f; + stq(ptr, temp.l.lower); + stw(ptr + 8, temp.l.upper); +} + +#define FPUS_IE (1 << 0) +#define FPUS_DE (1 << 1) +#define FPUS_ZE (1 << 2) +#define FPUS_OE (1 << 3) +#define FPUS_UE (1 << 4) +#define FPUS_PE (1 << 5) +#define FPUS_SF (1 << 6) +#define FPUS_SE (1 << 7) +#define FPUS_B (1 << 15) + +#define FPUC_EM 0x3f + +static inline uint32_t compute_eflags(void) +{ + return env->eflags | helper_cc_compute_all(CC_OP) | (DF & DF_MASK); +} + +/* NOTE: CC_OP must be modified manually to CC_OP_EFLAGS */ +static inline void load_eflags(int eflags, int update_mask) +{ + CC_SRC = eflags & (CC_O | CC_S | CC_Z | CC_A | CC_P | CC_C); + DF = 1 - (2 * ((eflags >> 10) & 1)); + env->eflags = (env->eflags & ~update_mask) | + (eflags & update_mask) | 0x2; +} + +/* load efer and update the corresponding hflags. XXX: do consistency + checks with cpuid bits ? */ +static inline void cpu_load_efer(CPUState *env, uint64_t val) +{ + env->efer = val; + env->hflags &= ~(HF_LMA_MASK | HF_SVME_MASK); + if (env->efer & MSR_EFER_LMA) { + env->hflags |= HF_LMA_MASK; + } + if (env->efer & MSR_EFER_SVME) { + env->hflags |= HF_SVME_MASK; + } +} #if 0 #define raise_exception_err(a, b)\ @@ -43,6 +146,9 @@ do {\ } while (0) #endif +static void QEMU_NORETURN raise_exception_err(int exception_index, + int error_code); + static const uint8_t parity_table[256] = { CC_P, 0, 0, CC_P, 0, CC_P, CC_P, 0, 0, CC_P, CC_P, 0, CC_P, 0, 0, CC_P, @@ -1381,12 +1487,20 @@ static void QEMU_NORETURN raise_interrupt(int intno, int is_int, int error_code, /* shortcuts to generate exceptions */ -void raise_exception_err(int exception_index, int error_code) +static void QEMU_NORETURN raise_exception_err(int exception_index, + int error_code) { raise_interrupt(exception_index, 0, error_code, 0); } -void raise_exception(int exception_index) +void raise_exception_err_env(CPUState *nenv, int exception_index, + int error_code) +{ + env = nenv; + raise_interrupt(exception_index, 0, error_code, 0); +} + +static void QEMU_NORETURN raise_exception(int exception_index) { raise_interrupt(exception_index, 0, 0, 0); } @@ -4426,6 +4540,49 @@ void helper_frstor(target_ulong ptr, int data32) } } + +#if defined(CONFIG_USER_ONLY) +void cpu_x86_load_seg(CPUX86State *s, int seg_reg, int selector) +{ + CPUX86State *saved_env; + + saved_env = env; + env = s; + if (!(env->cr[0] & CR0_PE_MASK) || (env->eflags & VM_MASK)) { + selector &= 0xffff; + cpu_x86_load_seg_cache(env, seg_reg, selector, + (selector << 4), 0xffff, 0); + } else { + helper_load_seg(seg_reg, selector); + } + env = saved_env; +} + +void cpu_x86_fsave(CPUX86State *s, target_ulong ptr, int data32) +{ + CPUX86State *saved_env; + + saved_env = env; + env = s; + + helper_fsave(ptr, data32); + + env = saved_env; +} + +void cpu_x86_frstor(CPUX86State *s, target_ulong ptr, int data32) +{ + CPUX86State *saved_env; + + saved_env = env; + env = s; + + helper_frstor(ptr, data32); + + env = saved_env; +} +#endif + void helper_fxsave(target_ulong ptr, int data64) { int fpus, fptag, i, nb_xmm_regs; diff --git a/target-lm32/exec.h b/target-lm32/exec.h deleted file mode 100644 index 2a227b2953..0000000000 --- a/target-lm32/exec.h +++ /dev/null @@ -1,38 +0,0 @@ -/* - * LatticeMico32 execution defines. - * - * Copyright (c) 2010 Michael Walle - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2 of the License, or (at your option) any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, see . - */ - -#include "dyngen-exec.h" - -register struct CPULM32State *env asm(AREG0); - -#include "cpu.h" - -static inline int cpu_halted(CPUState *env) -{ - if (!env->halted) { - return 0; - } - - /* IRQ execeptions wakes us up. */ - if (cpu_has_work(env)) { - env->halted = 0; - return 0; - } - return EXCP_HALTED; -} diff --git a/target-lm32/op_helper.c b/target-lm32/op_helper.c index a34cecd295..32b9a03c0e 100644 --- a/target-lm32/op_helper.c +++ b/target-lm32/op_helper.c @@ -1,5 +1,6 @@ #include -#include "exec.h" +#include "cpu.h" +#include "dyngen-exec.h" #include "helper.h" #include "host-utils.h" diff --git a/target-m68k/exec.h b/target-m68k/exec.h deleted file mode 100644 index 93e7912148..0000000000 --- a/target-m68k/exec.h +++ /dev/null @@ -1,28 +0,0 @@ -/* - * m68k execution defines - * - * Copyright (c) 2005-2006 CodeSourcery - * Written by Paul Brook - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2 of the License, or (at your option) any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, see . - */ -#include "dyngen-exec.h" - -register struct CPUM68KState *env asm(AREG0); - -#include "cpu.h" - -#if !defined(CONFIG_USER_ONLY) -#include "softmmu_exec.h" -#endif diff --git a/target-m68k/op_helper.c b/target-m68k/op_helper.c index 237fc4cb54..764b6a08c6 100644 --- a/target-m68k/op_helper.c +++ b/target-m68k/op_helper.c @@ -16,7 +16,8 @@ * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, see . */ -#include "exec.h" +#include "cpu.h" +#include "dyngen-exec.h" #include "helpers.h" #if defined(CONFIG_USER_ONLY) @@ -34,6 +35,8 @@ void do_interrupt_m68k_hardirq(CPUState *env1) extern int semihosting_enabled; +#include "softmmu_exec.h" + #define MMUSUFFIX _mmu #define SHIFT 0 diff --git a/target-microblaze/exec.h b/target-microblaze/exec.h deleted file mode 100644 index 71b4d397ed..0000000000 --- a/target-microblaze/exec.h +++ /dev/null @@ -1,27 +0,0 @@ -/* - * Microblaze execution defines - * - * Copyright (c) 2009 Edgar E. Iglesias - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2 of the License, or (at your option) any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, see . - */ -#include "dyngen-exec.h" - -register struct CPUMBState *env asm(AREG0); - -#include "cpu.h" - -#if !defined(CONFIG_USER_ONLY) -#include "softmmu_exec.h" -#endif diff --git a/target-microblaze/op_helper.c b/target-microblaze/op_helper.c index 664ffe5990..189c59c9d6 100644 --- a/target-microblaze/op_helper.c +++ b/target-microblaze/op_helper.c @@ -18,13 +18,16 @@ */ #include -#include "exec.h" +#include "cpu.h" +#include "dyngen-exec.h" #include "helper.h" #include "host-utils.h" #define D(x) #if !defined(CONFIG_USER_ONLY) +#include "softmmu_exec.h" + #define MMUSUFFIX _mmu #define SHIFT 0 #include "softmmu_template.h" diff --git a/target-mips/cpu.h b/target-mips/cpu.h index 33be2962a2..030f499bfb 100644 --- a/target-mips/cpu.h +++ b/target-mips/cpu.h @@ -1,6 +1,8 @@ #if !defined (__MIPS_CPU_H__) #define __MIPS_CPU_H__ +//#define DEBUG_OP + #define TARGET_HAS_ICE 1 #define ELF_MACHINE EM_MIPS diff --git a/target-mips/exec.h b/target-mips/exec.h deleted file mode 100644 index e787e9a8ba..0000000000 --- a/target-mips/exec.h +++ /dev/null @@ -1,60 +0,0 @@ -#if !defined(__QEMU_MIPS_EXEC_H__) -#define __QEMU_MIPS_EXEC_H__ - -//#define DEBUG_OP - -#include "config.h" -#include "mips-defs.h" -#include "dyngen-exec.h" -#include "cpu-defs.h" - -register struct CPUMIPSState *env asm(AREG0); - -#include "cpu.h" - -#if !defined(CONFIG_USER_ONLY) -#include "softmmu_exec.h" -#endif /* !defined(CONFIG_USER_ONLY) */ - -static inline void compute_hflags(CPUState *env) -{ - env->hflags &= ~(MIPS_HFLAG_COP1X | MIPS_HFLAG_64 | MIPS_HFLAG_CP0 | - MIPS_HFLAG_F64 | MIPS_HFLAG_FPU | MIPS_HFLAG_KSU | - MIPS_HFLAG_UX); - if (!(env->CP0_Status & (1 << CP0St_EXL)) && - !(env->CP0_Status & (1 << CP0St_ERL)) && - !(env->hflags & MIPS_HFLAG_DM)) { - env->hflags |= (env->CP0_Status >> CP0St_KSU) & MIPS_HFLAG_KSU; - } -#if defined(TARGET_MIPS64) - if (((env->hflags & MIPS_HFLAG_KSU) != MIPS_HFLAG_UM) || - (env->CP0_Status & (1 << CP0St_PX)) || - (env->CP0_Status & (1 << CP0St_UX))) - env->hflags |= MIPS_HFLAG_64; - if (env->CP0_Status & (1 << CP0St_UX)) - env->hflags |= MIPS_HFLAG_UX; -#endif - if ((env->CP0_Status & (1 << CP0St_CU0)) || - !(env->hflags & MIPS_HFLAG_KSU)) - env->hflags |= MIPS_HFLAG_CP0; - if (env->CP0_Status & (1 << CP0St_CU1)) - env->hflags |= MIPS_HFLAG_FPU; - if (env->CP0_Status & (1 << CP0St_FR)) - env->hflags |= MIPS_HFLAG_F64; - if (env->insn_flags & ISA_MIPS32R2) { - if (env->active_fpu.fcr0 & (1 << FCR0_F64)) - env->hflags |= MIPS_HFLAG_COP1X; - } else if (env->insn_flags & ISA_MIPS32) { - if (env->hflags & MIPS_HFLAG_64) - env->hflags |= MIPS_HFLAG_COP1X; - } else if (env->insn_flags & ISA_MIPS4) { - /* All supported MIPS IV CPUs use the XX (CU3) to enable - and disable the MIPS IV extensions to the MIPS III ISA. - Some other MIPS IV CPUs ignore the bit, so the check here - would be too restrictive for them. */ - if (env->CP0_Status & (1 << CP0St_CU3)) - env->hflags |= MIPS_HFLAG_COP1X; - } -} - -#endif /* !defined(__QEMU_MIPS_EXEC_H__) */ diff --git a/target-mips/op_helper.c b/target-mips/op_helper.c index 01315ef0dc..185ae40270 100644 --- a/target-mips/op_helper.c +++ b/target-mips/op_helper.c @@ -17,16 +17,70 @@ * License along with this library; if not, see . */ #include -#include "exec.h" +#include "cpu.h" +#include "dyngen-exec.h" #include "host-utils.h" #include "helper.h" +#if !defined(CONFIG_USER_ONLY) +#include "softmmu_exec.h" +#endif /* !defined(CONFIG_USER_ONLY) */ + #ifndef CONFIG_USER_ONLY static inline void cpu_mips_tlb_flush (CPUState *env, int flush_global); #endif +static inline void compute_hflags(CPUState *env) +{ + env->hflags &= ~(MIPS_HFLAG_COP1X | MIPS_HFLAG_64 | MIPS_HFLAG_CP0 | + MIPS_HFLAG_F64 | MIPS_HFLAG_FPU | MIPS_HFLAG_KSU | + MIPS_HFLAG_UX); + if (!(env->CP0_Status & (1 << CP0St_EXL)) && + !(env->CP0_Status & (1 << CP0St_ERL)) && + !(env->hflags & MIPS_HFLAG_DM)) { + env->hflags |= (env->CP0_Status >> CP0St_KSU) & MIPS_HFLAG_KSU; + } +#if defined(TARGET_MIPS64) + if (((env->hflags & MIPS_HFLAG_KSU) != MIPS_HFLAG_UM) || + (env->CP0_Status & (1 << CP0St_PX)) || + (env->CP0_Status & (1 << CP0St_UX))) { + env->hflags |= MIPS_HFLAG_64; + } + if (env->CP0_Status & (1 << CP0St_UX)) { + env->hflags |= MIPS_HFLAG_UX; + } +#endif + if ((env->CP0_Status & (1 << CP0St_CU0)) || + !(env->hflags & MIPS_HFLAG_KSU)) { + env->hflags |= MIPS_HFLAG_CP0; + } + if (env->CP0_Status & (1 << CP0St_CU1)) { + env->hflags |= MIPS_HFLAG_FPU; + } + if (env->CP0_Status & (1 << CP0St_FR)) { + env->hflags |= MIPS_HFLAG_F64; + } + if (env->insn_flags & ISA_MIPS32R2) { + if (env->active_fpu.fcr0 & (1 << FCR0_F64)) { + env->hflags |= MIPS_HFLAG_COP1X; + } + } else if (env->insn_flags & ISA_MIPS32) { + if (env->hflags & MIPS_HFLAG_64) { + env->hflags |= MIPS_HFLAG_COP1X; + } + } else if (env->insn_flags & ISA_MIPS4) { + /* All supported MIPS IV CPUs use the XX (CU3) to enable + and disable the MIPS IV extensions to the MIPS III ISA. + Some other MIPS IV CPUs ignore the bit, so the check here + would be too restrictive for them. */ + if (env->CP0_Status & (1 << CP0St_CU3)) { + env->hflags |= MIPS_HFLAG_COP1X; + } + } +} + /*****************************************************************************/ /* Exceptions processing helpers */ diff --git a/target-ppc/exec.h b/target-ppc/exec.h deleted file mode 100644 index f4453e4965..0000000000 --- a/target-ppc/exec.h +++ /dev/null @@ -1,34 +0,0 @@ -/* - * PowerPC emulation definitions for qemu. - * - * Copyright (c) 2003-2007 Jocelyn Mayer - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2 of the License, or (at your option) any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, see . - */ -#if !defined (__PPC_H__) -#define __PPC_H__ - -#include "config.h" - -#include "dyngen-exec.h" - -#include "cpu.h" - -register struct CPUPPCState *env asm(AREG0); - -#if !defined(CONFIG_USER_ONLY) -#include "softmmu_exec.h" -#endif /* !defined(CONFIG_USER_ONLY) */ - -#endif /* !defined (__PPC_H__) */ diff --git a/target-ppc/op_helper.c b/target-ppc/op_helper.c index dde7595fda..6e100d987d 100644 --- a/target-ppc/op_helper.c +++ b/target-ppc/op_helper.c @@ -17,12 +17,17 @@ * License along with this library; if not, see . */ #include -#include "exec.h" +#include "cpu.h" +#include "dyngen-exec.h" #include "host-utils.h" #include "helper.h" #include "helper_regs.h" +#if !defined(CONFIG_USER_ONLY) +#include "softmmu_exec.h" +#endif /* !defined(CONFIG_USER_ONLY) */ + //#define DEBUG_OP //#define DEBUG_EXCEPTIONS //#define DEBUG_SOFTWARE_TLB diff --git a/target-s390x/exec.h b/target-s390x/exec.h deleted file mode 100644 index fb73f31804..0000000000 --- a/target-s390x/exec.h +++ /dev/null @@ -1,37 +0,0 @@ -/* - * S/390 execution defines - * - * Copyright (c) 2009 Ulrich Hecht - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2 of the License, or (at your option) any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, see . - */ - -#include "dyngen-exec.h" - -register struct CPUS390XState *env asm(AREG0); - -#include "config.h" -#include "cpu.h" - -#if !defined(CONFIG_USER_ONLY) -#include "softmmu_exec.h" -#endif /* !defined(CONFIG_USER_ONLY) */ - -static inline void regs_to_env(void) -{ -} - -static inline void env_to_regs(void) -{ -} diff --git a/target-s390x/op_helper.c b/target-s390x/op_helper.c index cd33f99d21..25a1e81ef4 100644 --- a/target-s390x/op_helper.c +++ b/target-s390x/op_helper.c @@ -18,7 +18,8 @@ * License along with this library; if not, see . */ -#include "exec.h" +#include "cpu.h" +#include "dyngen-exec.h" #include "host-utils.h" #include "helpers.h" #include @@ -31,6 +32,7 @@ /*****************************************************************************/ /* Softmmu support */ #if !defined (CONFIG_USER_ONLY) +#include "softmmu_exec.h" #define MMUSUFFIX _mmu diff --git a/target-sh4/exec.h b/target-sh4/exec.h deleted file mode 100644 index 4a6ae58898..0000000000 --- a/target-sh4/exec.h +++ /dev/null @@ -1,33 +0,0 @@ -/* - * SH4 emulation - * - * Copyright (c) 2005 Samuel Tardieu - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2 of the License, or (at your option) any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, see . - */ -#ifndef _EXEC_SH4_H -#define _EXEC_SH4_H - -#include "config.h" -#include "dyngen-exec.h" - -register struct CPUSH4State *env asm(AREG0); - -#include "cpu.h" - -#ifndef CONFIG_USER_ONLY -#include "softmmu_exec.h" -#endif - -#endif /* _EXEC_SH4_H */ diff --git a/target-sh4/op_helper.c b/target-sh4/op_helper.c index a932225880..568bf0dba4 100644 --- a/target-sh4/op_helper.c +++ b/target-sh4/op_helper.c @@ -18,7 +18,8 @@ */ #include #include -#include "exec.h" +#include "cpu.h" +#include "dyngen-exec.h" #include "helper.h" static void cpu_restore_state_from_retaddr(void *retaddr) @@ -38,6 +39,7 @@ static void cpu_restore_state_from_retaddr(void *retaddr) } #ifndef CONFIG_USER_ONLY +#include "softmmu_exec.h" #define MMUSUFFIX _mmu diff --git a/target-sparc/exec.h b/target-sparc/exec.h deleted file mode 100644 index 2395b0092f..0000000000 --- a/target-sparc/exec.h +++ /dev/null @@ -1,15 +0,0 @@ -#ifndef EXEC_SPARC_H -#define EXEC_SPARC_H 1 -#include "config.h" -#include "dyngen-exec.h" - -register struct CPUSPARCState *env asm(AREG0); - -#include "cpu.h" -#include "exec-all.h" - -#if !defined(CONFIG_USER_ONLY) -#include "softmmu_exec.h" -#endif /* !defined(CONFIG_USER_ONLY) */ - -#endif diff --git a/target-sparc/op_helper.c b/target-sparc/op_helper.c index 8962e38219..c1c4d4b07e 100644 --- a/target-sparc/op_helper.c +++ b/target-sparc/op_helper.c @@ -1,8 +1,13 @@ -#include "exec.h" +#include "cpu.h" +#include "dyngen-exec.h" #include "host-utils.h" #include "helper.h" #include "sysemu.h" +#if !defined(CONFIG_USER_ONLY) +#include "softmmu_exec.h" +#endif + //#define DEBUG_MMU //#define DEBUG_MXCC //#define DEBUG_UNALIGNED diff --git a/target-unicore32/exec.h b/target-unicore32/exec.h deleted file mode 100644 index 7912105e32..0000000000 --- a/target-unicore32/exec.h +++ /dev/null @@ -1,43 +0,0 @@ -/* - * UniCore32 execution defines - * - * Copyright (C) 2010-2011 GUAN Xue-tao - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - */ -#ifndef __UC32_EXEC_H__ -#define __UC32_EXEC_H__ - -#include "config.h" -#include "dyngen-exec.h" - -register struct CPUState_UniCore32 *env asm(AREG0); - -#include "cpu.h" - -static inline void env_to_regs(void) -{ -} - -static inline void regs_to_env(void) -{ -} - -static inline int cpu_halted(CPUState *env) -{ - if (!env->halted) { - return 0; - } - /* An interrupt wakes the CPU even if the I and R ASR bits are - set. We use EXITTB to silently wake CPU without causing an - actual interrupt. */ - if (cpu_has_work(env)) { - env->halted = 0; - return 0; - } - return EXCP_HALTED; -} - -#endif /* __UC32_EXEC_H__ */ diff --git a/target-unicore32/op_helper.c b/target-unicore32/op_helper.c index 541e6f099d..6cf5255b26 100644 --- a/target-unicore32/op_helper.c +++ b/target-unicore32/op_helper.c @@ -7,7 +7,8 @@ * it under the terms of the GNU General Public License version 2 as * published by the Free Software Foundation. */ -#include "exec.h" +#include "cpu.h" +#include "dyngen-exec.h" #include "helper.h" #define SIGNBIT (uint32_t)0x80000000 diff --git a/user-exec.c b/user-exec.c index 02c2f8ba43..14c0f251b4 100644 --- a/user-exec.c +++ b/user-exec.c @@ -17,7 +17,8 @@ * License along with this library; if not, see . */ #include "config.h" -#include "exec.h" +#include "cpu.h" +#include "dyngen-exec.h" #include "disas.h" #include "tcg.h" @@ -40,7 +41,7 @@ static void exception_action(CPUState *env1) { #if defined(TARGET_I386) - raise_exception_err(env1->exception_index, env1->error_code); + raise_exception_err_env(env1, env1->exception_index, env1->error_code); #else cpu_loop_exit(env1); #endif @@ -628,47 +629,3 @@ int cpu_signal_handler(int host_signum, void *pinfo, #error host CPU specific signal handler needed #endif - -#if defined(TARGET_I386) - -void cpu_x86_load_seg(CPUX86State *s, int seg_reg, int selector) -{ - CPUX86State *saved_env; - - saved_env = env; - env = s; - if (!(env->cr[0] & CR0_PE_MASK) || (env->eflags & VM_MASK)) { - selector &= 0xffff; - cpu_x86_load_seg_cache(env, seg_reg, selector, - (selector << 4), 0xffff, 0); - } else { - helper_load_seg(seg_reg, selector); - } - env = saved_env; -} - -void cpu_x86_fsave(CPUX86State *s, target_ulong ptr, int data32) -{ - CPUX86State *saved_env; - - saved_env = env; - env = s; - - helper_fsave(ptr, data32); - - env = saved_env; -} - -void cpu_x86_frstor(CPUX86State *s, target_ulong ptr, int data32) -{ - CPUX86State *saved_env; - - saved_env = env; - env = s; - - helper_frstor(ptr, data32); - - env = saved_env; -} - -#endif /* TARGET_I386 */ From 8f2e8c07a65c340b525b08e08925b568844d4f3d Mon Sep 17 00:00:00 2001 From: Kirill Batuzov Date: Thu, 7 Jul 2011 16:37:12 +0400 Subject: [PATCH 140/230] Add TCG optimizations stub Added file tcg/optimize.c to hold TCG optimizations. Function tcg_optimize is called from tcg_gen_code_common. It calls other functions performing specific optimizations. Stub for constant folding was added. Signed-off-by: Kirill Batuzov Signed-off-by: Blue Swirl --- Makefile.target | 2 +- tcg/optimize.c | 97 +++++++++++++++++++++++++++++++++++++++++++++++++ tcg/tcg.c | 6 +++ tcg/tcg.h | 3 ++ 4 files changed, 107 insertions(+), 1 deletion(-) create mode 100644 tcg/optimize.c diff --git a/Makefile.target b/Makefile.target index d4ea0428fb..4aacc676d6 100644 --- a/Makefile.target +++ b/Makefile.target @@ -72,7 +72,7 @@ all: $(PROGS) stap ######################################################### # cpu emulator library libobj-y = exec.o translate-all.o cpu-exec.o translate.o -libobj-y += tcg/tcg.o +libobj-y += tcg/tcg.o tcg/optimize.o libobj-y += fpu/softfloat.o libobj-y += op_helper.o helper.o ifeq ($(TARGET_BASE_ARCH), i386) diff --git a/tcg/optimize.c b/tcg/optimize.c new file mode 100644 index 0000000000..c7c7da9e34 --- /dev/null +++ b/tcg/optimize.c @@ -0,0 +1,97 @@ +/* + * Optimizations for Tiny Code Generator for QEMU + * + * Copyright (c) 2010 Samsung Electronics. + * Contributed by Kirill Batuzov + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +#include "config.h" + +#include +#include + +#include "qemu-common.h" +#include "tcg-op.h" + +#if TCG_TARGET_REG_BITS == 64 +#define CASE_OP_32_64(x) \ + glue(glue(case INDEX_op_, x), _i32): \ + glue(glue(case INDEX_op_, x), _i64) +#else +#define CASE_OP_32_64(x) \ + glue(glue(case INDEX_op_, x), _i32) +#endif + +static TCGArg *tcg_constant_folding(TCGContext *s, uint16_t *tcg_opc_ptr, + TCGArg *args, TCGOpDef *tcg_op_defs) +{ + int i, nb_ops, op_index, op, nb_temps, nb_globals; + const TCGOpDef *def; + TCGArg *gen_args; + + nb_temps = s->nb_temps; + nb_globals = s->nb_globals; + + nb_ops = tcg_opc_ptr - gen_opc_buf; + gen_args = args; + for (op_index = 0; op_index < nb_ops; op_index++) { + op = gen_opc_buf[op_index]; + def = &tcg_op_defs[op]; + switch (op) { + case INDEX_op_call: + i = (args[0] >> 16) + (args[0] & 0xffff) + 3; + while (i) { + *gen_args = *args; + args++; + gen_args++; + i--; + } + break; + case INDEX_op_set_label: + case INDEX_op_jmp: + case INDEX_op_br: + CASE_OP_32_64(brcond): + for (i = 0; i < def->nb_args; i++) { + *gen_args = *args; + args++; + gen_args++; + } + break; + default: + for (i = 0; i < def->nb_args; i++) { + gen_args[i] = args[i]; + } + args += def->nb_args; + gen_args += def->nb_args; + break; + } + } + + return gen_args; +} + +TCGArg *tcg_optimize(TCGContext *s, uint16_t *tcg_opc_ptr, + TCGArg *args, TCGOpDef *tcg_op_defs) +{ + TCGArg *res; + res = tcg_constant_folding(s, tcg_opc_ptr, args, tcg_op_defs); + return res; +} diff --git a/tcg/tcg.c b/tcg/tcg.c index c05413baa4..92f1989019 100644 --- a/tcg/tcg.c +++ b/tcg/tcg.c @@ -24,6 +24,7 @@ /* define it to use liveness analysis (better code) */ #define USE_LIVENESS_ANALYSIS +#define USE_TCG_OPTIMIZATIONS #include "config.h" @@ -2035,6 +2036,11 @@ static inline int tcg_gen_code_common(TCGContext *s, uint8_t *gen_code_buf, } #endif +#ifdef USE_TCG_OPTIMIZATIONS + gen_opparam_ptr = + tcg_optimize(s, gen_opc_ptr, gen_opparam_buf, tcg_op_defs); +#endif + #ifdef CONFIG_PROFILER s->la_time -= profile_getclock(); #endif diff --git a/tcg/tcg.h b/tcg/tcg.h index a2dd8b892a..e76f9afecc 100644 --- a/tcg/tcg.h +++ b/tcg/tcg.h @@ -502,6 +502,9 @@ void tcg_gen_callN(TCGContext *s, TCGv_ptr func, unsigned int flags, void tcg_gen_shifti_i64(TCGv_i64 ret, TCGv_i64 arg1, int c, int right, int arith); +TCGArg *tcg_optimize(TCGContext *s, uint16_t *tcg_opc_ptr, TCGArg *args, + TCGOpDef *tcg_op_def); + /* only used for debugging purposes */ void tcg_register_helper(void *func, const char *name); const char *tcg_helper_get_name(TCGContext *s, void *func); From 22613af4a6d9602001e6d0e7b6d98aa40aa018dc Mon Sep 17 00:00:00 2001 From: Kirill Batuzov Date: Thu, 7 Jul 2011 16:37:13 +0400 Subject: [PATCH 141/230] Add copy and constant propagation. Make tcg_constant_folding do copy and constant propagation. It is a preparational work before actual constant folding. Signed-off-by: Kirill Batuzov Signed-off-by: Blue Swirl --- tcg/optimize.c | 182 ++++++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 180 insertions(+), 2 deletions(-) diff --git a/tcg/optimize.c b/tcg/optimize.c index c7c7da9e34..f8afe71842 100644 --- a/tcg/optimize.c +++ b/tcg/optimize.c @@ -40,24 +40,196 @@ glue(glue(case INDEX_op_, x), _i32) #endif +typedef enum { + TCG_TEMP_UNDEF = 0, + TCG_TEMP_CONST, + TCG_TEMP_COPY, + TCG_TEMP_HAS_COPY, + TCG_TEMP_ANY +} tcg_temp_state; + +struct tcg_temp_info { + tcg_temp_state state; + uint16_t prev_copy; + uint16_t next_copy; + tcg_target_ulong val; +}; + +static struct tcg_temp_info temps[TCG_MAX_TEMPS]; + +/* Reset TEMP's state to TCG_TEMP_ANY. If TEMP was a representative of some + class of equivalent temp's, a new representative should be chosen in this + class. */ +static void reset_temp(TCGArg temp, int nb_temps, int nb_globals) +{ + int i; + TCGArg new_base = (TCGArg)-1; + if (temps[temp].state == TCG_TEMP_HAS_COPY) { + for (i = temps[temp].next_copy; i != temp; i = temps[i].next_copy) { + if (i >= nb_globals) { + temps[i].state = TCG_TEMP_HAS_COPY; + new_base = i; + break; + } + } + for (i = temps[temp].next_copy; i != temp; i = temps[i].next_copy) { + if (new_base == (TCGArg)-1) { + temps[i].state = TCG_TEMP_ANY; + } else { + temps[i].val = new_base; + } + } + temps[temps[temp].next_copy].prev_copy = temps[temp].prev_copy; + temps[temps[temp].prev_copy].next_copy = temps[temp].next_copy; + } else if (temps[temp].state == TCG_TEMP_COPY) { + temps[temps[temp].next_copy].prev_copy = temps[temp].prev_copy; + temps[temps[temp].prev_copy].next_copy = temps[temp].next_copy; + new_base = temps[temp].val; + } + temps[temp].state = TCG_TEMP_ANY; + if (new_base != (TCGArg)-1 && temps[new_base].next_copy == new_base) { + temps[new_base].state = TCG_TEMP_ANY; + } +} + +static int op_bits(int op) +{ + switch (op) { + case INDEX_op_mov_i32: + return 32; +#if TCG_TARGET_REG_BITS == 64 + case INDEX_op_mov_i64: + return 64; +#endif + default: + fprintf(stderr, "Unrecognized operation %d in op_bits.\n", op); + tcg_abort(); + } +} + +static int op_to_movi(int op) +{ + switch (op_bits(op)) { + case 32: + return INDEX_op_movi_i32; +#if TCG_TARGET_REG_BITS == 64 + case 64: + return INDEX_op_movi_i64; +#endif + default: + fprintf(stderr, "op_to_movi: unexpected return value of " + "function op_bits.\n"); + tcg_abort(); + } +} + +static void tcg_opt_gen_mov(TCGArg *gen_args, TCGArg dst, TCGArg src, + int nb_temps, int nb_globals) +{ + reset_temp(dst, nb_temps, nb_globals); + assert(temps[src].state != TCG_TEMP_COPY); + if (src >= nb_globals) { + assert(temps[src].state != TCG_TEMP_CONST); + if (temps[src].state != TCG_TEMP_HAS_COPY) { + temps[src].state = TCG_TEMP_HAS_COPY; + temps[src].next_copy = src; + temps[src].prev_copy = src; + } + temps[dst].state = TCG_TEMP_COPY; + temps[dst].val = src; + temps[dst].next_copy = temps[src].next_copy; + temps[dst].prev_copy = src; + temps[temps[dst].next_copy].prev_copy = dst; + temps[src].next_copy = dst; + } + gen_args[0] = dst; + gen_args[1] = src; +} + +static void tcg_opt_gen_movi(TCGArg *gen_args, TCGArg dst, TCGArg val, + int nb_temps, int nb_globals) +{ + reset_temp(dst, nb_temps, nb_globals); + temps[dst].state = TCG_TEMP_CONST; + temps[dst].val = val; + gen_args[0] = dst; + gen_args[1] = val; +} + +/* Propagate constants and copies, fold constant expressions. */ static TCGArg *tcg_constant_folding(TCGContext *s, uint16_t *tcg_opc_ptr, TCGArg *args, TCGOpDef *tcg_op_defs) { - int i, nb_ops, op_index, op, nb_temps, nb_globals; + int i, nb_ops, op_index, op, nb_temps, nb_globals, nb_call_args; const TCGOpDef *def; TCGArg *gen_args; + /* Array VALS has an element for each temp. + If this temp holds a constant then its value is kept in VALS' element. + If this temp is a copy of other ones then this equivalence class' + representative is kept in VALS' element. + If this temp is neither copy nor constant then corresponding VALS' + element is unused. */ nb_temps = s->nb_temps; nb_globals = s->nb_globals; + memset(temps, 0, nb_temps * sizeof(struct tcg_temp_info)); nb_ops = tcg_opc_ptr - gen_opc_buf; gen_args = args; for (op_index = 0; op_index < nb_ops; op_index++) { op = gen_opc_buf[op_index]; def = &tcg_op_defs[op]; + /* Do copy propagation */ + if (!(def->flags & (TCG_OPF_CALL_CLOBBER | TCG_OPF_SIDE_EFFECTS))) { + assert(op != INDEX_op_call); + for (i = def->nb_oargs; i < def->nb_oargs + def->nb_iargs; i++) { + if (temps[args[i]].state == TCG_TEMP_COPY) { + args[i] = temps[args[i]].val; + } + } + } + + /* Propagate constants through copy operations and do constant + folding. Constants will be substituted to arguments by register + allocator where needed and possible. Also detect copies. */ switch (op) { + CASE_OP_32_64(mov): + if ((temps[args[1]].state == TCG_TEMP_COPY + && temps[args[1]].val == args[0]) + || args[0] == args[1]) { + args += 2; + gen_opc_buf[op_index] = INDEX_op_nop; + break; + } + if (temps[args[1]].state != TCG_TEMP_CONST) { + tcg_opt_gen_mov(gen_args, args[0], args[1], + nb_temps, nb_globals); + gen_args += 2; + args += 2; + break; + } + /* Source argument is constant. Rewrite the operation and + let movi case handle it. */ + op = op_to_movi(op); + gen_opc_buf[op_index] = op; + args[1] = temps[args[1]].val; + /* fallthrough */ + CASE_OP_32_64(movi): + tcg_opt_gen_movi(gen_args, args[0], args[1], nb_temps, nb_globals); + gen_args += 2; + args += 2; + break; case INDEX_op_call: - i = (args[0] >> 16) + (args[0] & 0xffff) + 3; + nb_call_args = (args[0] >> 16) + (args[0] & 0xffff); + if (!(args[nb_call_args + 1] & (TCG_CALL_CONST | TCG_CALL_PURE))) { + for (i = 0; i < nb_globals; i++) { + reset_temp(i, nb_temps, nb_globals); + } + } + for (i = 0; i < (args[0] >> 16); i++) { + reset_temp(args[i + 1], nb_temps, nb_globals); + } + i = nb_call_args + 3; while (i) { *gen_args = *args; args++; @@ -69,6 +241,7 @@ static TCGArg *tcg_constant_folding(TCGContext *s, uint16_t *tcg_opc_ptr, case INDEX_op_jmp: case INDEX_op_br: CASE_OP_32_64(brcond): + memset(temps, 0, nb_temps * sizeof(struct tcg_temp_info)); for (i = 0; i < def->nb_args; i++) { *gen_args = *args; args++; @@ -76,6 +249,11 @@ static TCGArg *tcg_constant_folding(TCGContext *s, uint16_t *tcg_opc_ptr, } break; default: + /* Default case: we do know nothing about operation so no + propagation is done. We only trash output args. */ + for (i = 0; i < def->nb_oargs; i++) { + reset_temp(args[i], nb_temps, nb_globals); + } for (i = 0; i < def->nb_args; i++) { gen_args[i] = args[i]; } From 53108fb57413cf6f3d81a71a70257d49a73569c7 Mon Sep 17 00:00:00 2001 From: Kirill Batuzov Date: Thu, 7 Jul 2011 16:37:14 +0400 Subject: [PATCH 142/230] Do constant folding for basic arithmetic operations. Perform actual constant folding for ADD, SUB and MUL operations. Signed-off-by: Kirill Batuzov Signed-off-by: Blue Swirl --- tcg/optimize.c | 125 +++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 125 insertions(+) diff --git a/tcg/optimize.c b/tcg/optimize.c index f8afe71842..42a1bda666 100644 --- a/tcg/optimize.c +++ b/tcg/optimize.c @@ -96,9 +96,15 @@ static int op_bits(int op) { switch (op) { case INDEX_op_mov_i32: + case INDEX_op_add_i32: + case INDEX_op_sub_i32: + case INDEX_op_mul_i32: return 32; #if TCG_TARGET_REG_BITS == 64 case INDEX_op_mov_i64: + case INDEX_op_add_i64: + case INDEX_op_sub_i64: + case INDEX_op_mul_i64: return 64; #endif default: @@ -156,6 +162,52 @@ static void tcg_opt_gen_movi(TCGArg *gen_args, TCGArg dst, TCGArg val, gen_args[1] = val; } +static int op_to_mov(int op) +{ + switch (op_bits(op)) { + case 32: + return INDEX_op_mov_i32; +#if TCG_TARGET_REG_BITS == 64 + case 64: + return INDEX_op_mov_i64; +#endif + default: + fprintf(stderr, "op_to_mov: unexpected return value of " + "function op_bits.\n"); + tcg_abort(); + } +} + +static TCGArg do_constant_folding_2(int op, TCGArg x, TCGArg y) +{ + switch (op) { + CASE_OP_32_64(add): + return x + y; + + CASE_OP_32_64(sub): + return x - y; + + CASE_OP_32_64(mul): + return x * y; + + default: + fprintf(stderr, + "Unrecognized operation %d in do_constant_folding.\n", op); + tcg_abort(); + } +} + +static TCGArg do_constant_folding(int op, TCGArg x, TCGArg y) +{ + TCGArg res = do_constant_folding_2(op, x, y); +#if TCG_TARGET_REG_BITS == 64 + if (op_bits(op) == 32) { + res &= 0xffffffff; + } +#endif + return res; +} + /* Propagate constants and copies, fold constant expressions. */ static TCGArg *tcg_constant_folding(TCGContext *s, uint16_t *tcg_opc_ptr, TCGArg *args, TCGOpDef *tcg_op_defs) @@ -163,6 +215,7 @@ static TCGArg *tcg_constant_folding(TCGContext *s, uint16_t *tcg_opc_ptr, int i, nb_ops, op_index, op, nb_temps, nb_globals, nb_call_args; const TCGOpDef *def; TCGArg *gen_args; + TCGArg tmp; /* Array VALS has an element for each temp. If this temp holds a constant then its value is kept in VALS' element. If this temp is a copy of other ones then this equivalence class' @@ -189,6 +242,57 @@ static TCGArg *tcg_constant_folding(TCGContext *s, uint16_t *tcg_opc_ptr, } } + /* For commutative operations make constant second argument */ + switch (op) { + CASE_OP_32_64(add): + CASE_OP_32_64(mul): + if (temps[args[1]].state == TCG_TEMP_CONST) { + tmp = args[1]; + args[1] = args[2]; + args[2] = tmp; + } + break; + default: + break; + } + + /* Simplify expression if possible. */ + switch (op) { + CASE_OP_32_64(add): + CASE_OP_32_64(sub): + if (temps[args[1]].state == TCG_TEMP_CONST) { + /* Proceed with possible constant folding. */ + break; + } + if (temps[args[2]].state == TCG_TEMP_CONST + && temps[args[2]].val == 0) { + if ((temps[args[0]].state == TCG_TEMP_COPY + && temps[args[0]].val == args[1]) + || args[0] == args[1]) { + args += 3; + gen_opc_buf[op_index] = INDEX_op_nop; + } else { + gen_opc_buf[op_index] = op_to_mov(op); + tcg_opt_gen_mov(gen_args, args[0], args[1], + nb_temps, nb_globals); + gen_args += 2; + args += 3; + } + continue; + } + break; + CASE_OP_32_64(mul): + if ((temps[args[2]].state == TCG_TEMP_CONST + && temps[args[2]].val == 0)) { + gen_opc_buf[op_index] = op_to_movi(op); + tcg_opt_gen_movi(gen_args, args[0], 0, nb_temps, nb_globals); + args += 3; + gen_args += 2; + continue; + } + break; + } + /* Propagate constants through copy operations and do constant folding. Constants will be substituted to arguments by register allocator where needed and possible. Also detect copies. */ @@ -219,6 +323,27 @@ static TCGArg *tcg_constant_folding(TCGContext *s, uint16_t *tcg_opc_ptr, gen_args += 2; args += 2; break; + CASE_OP_32_64(add): + CASE_OP_32_64(sub): + CASE_OP_32_64(mul): + if (temps[args[1]].state == TCG_TEMP_CONST + && temps[args[2]].state == TCG_TEMP_CONST) { + gen_opc_buf[op_index] = op_to_movi(op); + tmp = do_constant_folding(op, temps[args[1]].val, + temps[args[2]].val); + tcg_opt_gen_movi(gen_args, args[0], tmp, nb_temps, nb_globals); + gen_args += 2; + args += 3; + break; + } else { + reset_temp(args[0], nb_temps, nb_globals); + gen_args[0] = args[0]; + gen_args[1] = args[1]; + gen_args[2] = args[2]; + gen_args += 3; + args += 3; + break; + } case INDEX_op_call: nb_call_args = (args[0] >> 16) + (args[0] & 0xffff); if (!(args[nb_call_args + 1] & (TCG_CALL_CONST | TCG_CALL_PURE))) { From 9a81090b12a3141ff1f1b4dc331042efbdfd2ad8 Mon Sep 17 00:00:00 2001 From: Kirill Batuzov Date: Thu, 7 Jul 2011 16:37:15 +0400 Subject: [PATCH 143/230] Do constant folding for boolean operations. Perform constant folding for AND, OR, XOR operations. Signed-off-by: Kirill Batuzov Signed-off-by: Blue Swirl --- tcg/optimize.c | 37 +++++++++++++++++++++++++++++++++++++ 1 file changed, 37 insertions(+) diff --git a/tcg/optimize.c b/tcg/optimize.c index 42a1bda666..c4699529f9 100644 --- a/tcg/optimize.c +++ b/tcg/optimize.c @@ -99,12 +99,18 @@ static int op_bits(int op) case INDEX_op_add_i32: case INDEX_op_sub_i32: case INDEX_op_mul_i32: + case INDEX_op_and_i32: + case INDEX_op_or_i32: + case INDEX_op_xor_i32: return 32; #if TCG_TARGET_REG_BITS == 64 case INDEX_op_mov_i64: case INDEX_op_add_i64: case INDEX_op_sub_i64: case INDEX_op_mul_i64: + case INDEX_op_and_i64: + case INDEX_op_or_i64: + case INDEX_op_xor_i64: return 64; #endif default: @@ -190,6 +196,15 @@ static TCGArg do_constant_folding_2(int op, TCGArg x, TCGArg y) CASE_OP_32_64(mul): return x * y; + CASE_OP_32_64(and): + return x & y; + + CASE_OP_32_64(or): + return x | y; + + CASE_OP_32_64(xor): + return x ^ y; + default: fprintf(stderr, "Unrecognized operation %d in do_constant_folding.\n", op); @@ -246,6 +261,9 @@ static TCGArg *tcg_constant_folding(TCGContext *s, uint16_t *tcg_opc_ptr, switch (op) { CASE_OP_32_64(add): CASE_OP_32_64(mul): + CASE_OP_32_64(and): + CASE_OP_32_64(or): + CASE_OP_32_64(xor): if (temps[args[1]].state == TCG_TEMP_CONST) { tmp = args[1]; args[1] = args[2]; @@ -291,6 +309,22 @@ static TCGArg *tcg_constant_folding(TCGContext *s, uint16_t *tcg_opc_ptr, continue; } break; + CASE_OP_32_64(or): + CASE_OP_32_64(and): + if (args[1] == args[2]) { + if (args[1] == args[0]) { + args += 3; + gen_opc_buf[op_index] = INDEX_op_nop; + } else { + gen_opc_buf[op_index] = op_to_mov(op); + tcg_opt_gen_mov(gen_args, args[0], args[1], nb_temps, + nb_globals); + gen_args += 2; + args += 3; + } + continue; + } + break; } /* Propagate constants through copy operations and do constant @@ -326,6 +360,9 @@ static TCGArg *tcg_constant_folding(TCGContext *s, uint16_t *tcg_opc_ptr, CASE_OP_32_64(add): CASE_OP_32_64(sub): CASE_OP_32_64(mul): + CASE_OP_32_64(or): + CASE_OP_32_64(and): + CASE_OP_32_64(xor): if (temps[args[1]].state == TCG_TEMP_CONST && temps[args[2]].state == TCG_TEMP_CONST) { gen_opc_buf[op_index] = op_to_movi(op); From 55c0975c5b358e948b9ae7bd7b07eff92508e756 Mon Sep 17 00:00:00 2001 From: Kirill Batuzov Date: Thu, 7 Jul 2011 16:37:16 +0400 Subject: [PATCH 144/230] Do constant folding for shift operations. Perform constant forlding for SHR, SHL, SAR, ROTR, ROTL operations. Signed-off-by: Kirill Batuzov Signed-off-by: Blue Swirl --- tcg/optimize.c | 72 ++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 72 insertions(+) diff --git a/tcg/optimize.c b/tcg/optimize.c index c4699529f9..a1bb2870f8 100644 --- a/tcg/optimize.c +++ b/tcg/optimize.c @@ -102,6 +102,11 @@ static int op_bits(int op) case INDEX_op_and_i32: case INDEX_op_or_i32: case INDEX_op_xor_i32: + case INDEX_op_shl_i32: + case INDEX_op_shr_i32: + case INDEX_op_sar_i32: + case INDEX_op_rotl_i32: + case INDEX_op_rotr_i32: return 32; #if TCG_TARGET_REG_BITS == 64 case INDEX_op_mov_i64: @@ -111,6 +116,11 @@ static int op_bits(int op) case INDEX_op_and_i64: case INDEX_op_or_i64: case INDEX_op_xor_i64: + case INDEX_op_shl_i64: + case INDEX_op_shr_i64: + case INDEX_op_sar_i64: + case INDEX_op_rotl_i64: + case INDEX_op_rotr_i64: return 64; #endif default: @@ -205,6 +215,58 @@ static TCGArg do_constant_folding_2(int op, TCGArg x, TCGArg y) CASE_OP_32_64(xor): return x ^ y; + case INDEX_op_shl_i32: + return (uint32_t)x << (uint32_t)y; + +#if TCG_TARGET_REG_BITS == 64 + case INDEX_op_shl_i64: + return (uint64_t)x << (uint64_t)y; +#endif + + case INDEX_op_shr_i32: + return (uint32_t)x >> (uint32_t)y; + +#if TCG_TARGET_REG_BITS == 64 + case INDEX_op_shr_i64: + return (uint64_t)x >> (uint64_t)y; +#endif + + case INDEX_op_sar_i32: + return (int32_t)x >> (int32_t)y; + +#if TCG_TARGET_REG_BITS == 64 + case INDEX_op_sar_i64: + return (int64_t)x >> (int64_t)y; +#endif + + case INDEX_op_rotr_i32: +#if TCG_TARGET_REG_BITS == 64 + x &= 0xffffffff; + y &= 0xffffffff; +#endif + x = (x << (32 - y)) | (x >> y); + return x; + +#if TCG_TARGET_REG_BITS == 64 + case INDEX_op_rotr_i64: + x = (x << (64 - y)) | (x >> y); + return x; +#endif + + case INDEX_op_rotl_i32: +#if TCG_TARGET_REG_BITS == 64 + x &= 0xffffffff; + y &= 0xffffffff; +#endif + x = (x << y) | (x >> (32 - y)); + return x; + +#if TCG_TARGET_REG_BITS == 64 + case INDEX_op_rotl_i64: + x = (x << y) | (x >> (64 - y)); + return x; +#endif + default: fprintf(stderr, "Unrecognized operation %d in do_constant_folding.\n", op); @@ -278,6 +340,11 @@ static TCGArg *tcg_constant_folding(TCGContext *s, uint16_t *tcg_opc_ptr, switch (op) { CASE_OP_32_64(add): CASE_OP_32_64(sub): + CASE_OP_32_64(shl): + CASE_OP_32_64(shr): + CASE_OP_32_64(sar): + CASE_OP_32_64(rotl): + CASE_OP_32_64(rotr): if (temps[args[1]].state == TCG_TEMP_CONST) { /* Proceed with possible constant folding. */ break; @@ -363,6 +430,11 @@ static TCGArg *tcg_constant_folding(TCGContext *s, uint16_t *tcg_opc_ptr, CASE_OP_32_64(or): CASE_OP_32_64(and): CASE_OP_32_64(xor): + CASE_OP_32_64(shl): + CASE_OP_32_64(shr): + CASE_OP_32_64(sar): + CASE_OP_32_64(rotl): + CASE_OP_32_64(rotr): if (temps[args[1]].state == TCG_TEMP_CONST && temps[args[2]].state == TCG_TEMP_CONST) { gen_opc_buf[op_index] = op_to_movi(op); From a640f03178c22355a158fa9378e4f8bfa4f517a6 Mon Sep 17 00:00:00 2001 From: Kirill Batuzov Date: Thu, 7 Jul 2011 16:37:17 +0400 Subject: [PATCH 145/230] Do constant folding for unary operations. Perform constant folding for NOT and EXT{8,16,32}{S,U} operations. Signed-off-by: Kirill Batuzov Signed-off-by: Blue Swirl --- tcg/optimize.c | 59 ++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 59 insertions(+) diff --git a/tcg/optimize.c b/tcg/optimize.c index a1bb2870f8..a324e9872e 100644 --- a/tcg/optimize.c +++ b/tcg/optimize.c @@ -107,6 +107,11 @@ static int op_bits(int op) case INDEX_op_sar_i32: case INDEX_op_rotl_i32: case INDEX_op_rotr_i32: + case INDEX_op_not_i32: + case INDEX_op_ext8s_i32: + case INDEX_op_ext16s_i32: + case INDEX_op_ext8u_i32: + case INDEX_op_ext16u_i32: return 32; #if TCG_TARGET_REG_BITS == 64 case INDEX_op_mov_i64: @@ -121,6 +126,13 @@ static int op_bits(int op) case INDEX_op_sar_i64: case INDEX_op_rotl_i64: case INDEX_op_rotr_i64: + case INDEX_op_not_i64: + case INDEX_op_ext8s_i64: + case INDEX_op_ext16s_i64: + case INDEX_op_ext32s_i64: + case INDEX_op_ext8u_i64: + case INDEX_op_ext16u_i64: + case INDEX_op_ext32u_i64: return 64; #endif default: @@ -267,6 +279,29 @@ static TCGArg do_constant_folding_2(int op, TCGArg x, TCGArg y) return x; #endif + CASE_OP_32_64(not): + return ~x; + + CASE_OP_32_64(ext8s): + return (int8_t)x; + + CASE_OP_32_64(ext16s): + return (int16_t)x; + + CASE_OP_32_64(ext8u): + return (uint8_t)x; + + CASE_OP_32_64(ext16u): + return (uint16_t)x; + +#if TCG_TARGET_REG_BITS == 64 + case INDEX_op_ext32s_i64: + return (int32_t)x; + + case INDEX_op_ext32u_i64: + return (uint32_t)x; +#endif + default: fprintf(stderr, "Unrecognized operation %d in do_constant_folding.\n", op); @@ -424,6 +459,30 @@ static TCGArg *tcg_constant_folding(TCGContext *s, uint16_t *tcg_opc_ptr, gen_args += 2; args += 2; break; + CASE_OP_32_64(not): + CASE_OP_32_64(ext8s): + CASE_OP_32_64(ext16s): + CASE_OP_32_64(ext8u): + CASE_OP_32_64(ext16u): +#if TCG_TARGET_REG_BITS == 64 + case INDEX_op_ext32s_i64: + case INDEX_op_ext32u_i64: +#endif + if (temps[args[1]].state == TCG_TEMP_CONST) { + gen_opc_buf[op_index] = op_to_movi(op); + tmp = do_constant_folding(op, temps[args[1]].val, 0); + tcg_opt_gen_movi(gen_args, args[0], tmp, nb_temps, nb_globals); + gen_args += 2; + args += 2; + break; + } else { + reset_temp(args[0], nb_temps, nb_globals); + gen_args[0] = args[0]; + gen_args[1] = args[1]; + gen_args += 2; + args += 2; + break; + } CASE_OP_32_64(add): CASE_OP_32_64(sub): CASE_OP_32_64(mul): From 1bfd07bdfe56cea43dbe258dcb161e46b0ee29b7 Mon Sep 17 00:00:00 2001 From: Blue Swirl Date: Sat, 30 Jul 2011 12:21:33 +0000 Subject: [PATCH 146/230] TCG: fix breakage on some RISC hosts Fix breakage by a640f03178c22355a158fa9378e4f8bfa4f517a6 and 55c0975c5b358e948b9ae7bd7b07eff92508e756. Some TCG targets don't implement all TCG ops, so make optimizing those conditional. Signed-off-by: Blue Swirl --- tcg/optimize.c | 128 ++++++++++++++++++++++++++++++++++++++++++++----- 1 file changed, 115 insertions(+), 13 deletions(-) diff --git a/tcg/optimize.c b/tcg/optimize.c index a324e9872e..6a0a4ddb33 100644 --- a/tcg/optimize.c +++ b/tcg/optimize.c @@ -105,13 +105,25 @@ static int op_bits(int op) case INDEX_op_shl_i32: case INDEX_op_shr_i32: case INDEX_op_sar_i32: +#ifdef TCG_TARGET_HAS_rot_i32 case INDEX_op_rotl_i32: case INDEX_op_rotr_i32: +#endif +#ifdef TCG_TARGET_HAS_not_i32 case INDEX_op_not_i32: +#endif +#ifdef TCG_TARGET_HAS_ext8s_i32 case INDEX_op_ext8s_i32: +#endif +#ifdef TCG_TARGET_HAS_ext16s_i32 case INDEX_op_ext16s_i32: +#endif +#ifdef TCG_TARGET_HAS_ext8u_i32 case INDEX_op_ext8u_i32: +#endif +#ifdef TCG_TARGET_HAS_ext16u_i32 case INDEX_op_ext16u_i32: +#endif return 32; #if TCG_TARGET_REG_BITS == 64 case INDEX_op_mov_i64: @@ -124,15 +136,31 @@ static int op_bits(int op) case INDEX_op_shl_i64: case INDEX_op_shr_i64: case INDEX_op_sar_i64: +#ifdef TCG_TARGET_HAS_rot_i64 case INDEX_op_rotl_i64: case INDEX_op_rotr_i64: +#endif +#ifdef TCG_TARGET_HAS_not_i64 case INDEX_op_not_i64: +#endif +#ifdef TCG_TARGET_HAS_ext8s_i64 case INDEX_op_ext8s_i64: +#endif +#ifdef TCG_TARGET_HAS_ext16s_i64 case INDEX_op_ext16s_i64: +#endif +#ifdef TCG_TARGET_HAS_ext32s_i64 case INDEX_op_ext32s_i64: +#endif +#ifdef TCG_TARGET_HAS_ext8u_i64 case INDEX_op_ext8u_i64: +#endif +#ifdef TCG_TARGET_HAS_ext16u_i64 case INDEX_op_ext16u_i64: +#endif +#ifdef TCG_TARGET_HAS_ext32u_i64 case INDEX_op_ext32u_i64: +#endif return 64; #endif default: @@ -251,6 +279,7 @@ static TCGArg do_constant_folding_2(int op, TCGArg x, TCGArg y) return (int64_t)x >> (int64_t)y; #endif +#ifdef TCG_TARGET_HAS_rot_i32 case INDEX_op_rotr_i32: #if TCG_TARGET_REG_BITS == 64 x &= 0xffffffff; @@ -258,13 +287,17 @@ static TCGArg do_constant_folding_2(int op, TCGArg x, TCGArg y) #endif x = (x << (32 - y)) | (x >> y); return x; +#endif +#ifdef TCG_TARGET_HAS_rot_i64 #if TCG_TARGET_REG_BITS == 64 case INDEX_op_rotr_i64: x = (x << (64 - y)) | (x >> y); return x; #endif +#endif +#ifdef TCG_TARGET_HAS_rot_i32 case INDEX_op_rotl_i32: #if TCG_TARGET_REG_BITS == 64 x &= 0xffffffff; @@ -272,34 +305,71 @@ static TCGArg do_constant_folding_2(int op, TCGArg x, TCGArg y) #endif x = (x << y) | (x >> (32 - y)); return x; +#endif +#ifdef TCG_TARGET_HAS_rot_i64 #if TCG_TARGET_REG_BITS == 64 case INDEX_op_rotl_i64: x = (x << y) | (x >> (64 - y)); return x; #endif +#endif - CASE_OP_32_64(not): +#if defined(TCG_TARGET_HAS_not_i32) || defined(TCG_TARGET_HAS_not_i64) +#ifdef TCG_TARGET_HAS_not_i32 + case INDEX_op_not_i32: +#else + case INDEX_op_not_i64: +#endif return ~x; +#endif - CASE_OP_32_64(ext8s): +#if defined(TCG_TARGET_HAS_ext8s_i32) || defined(TCG_TARGET_HAS_ext8s_i64) +#ifdef TCG_TARGET_HAS_ext8s_i32 + case INDEX_op_ext8s_i32: +#else + case INDEX_op_ext8s_i64: +#endif return (int8_t)x; +#endif - CASE_OP_32_64(ext16s): +#if defined(TCG_TARGET_HAS_ext16s_i32) || defined(TCG_TARGET_HAS_ext16s_i64) +#ifdef TCG_TARGET_HAS_ext16s_i32 + case INDEX_op_ext16s_i32: +#else + case INDEX_op_ext16s_i64: +#endif return (int16_t)x; +#endif - CASE_OP_32_64(ext8u): +#if defined(TCG_TARGET_HAS_ext8u_i32) || defined(TCG_TARGET_HAS_ext8u_i64) +#ifdef TCG_TARGET_HAS_ext8u_i32 + case INDEX_op_ext8u_i32: +#else + case INDEX_op_ext8u_i64: +#endif return (uint8_t)x; +#endif - CASE_OP_32_64(ext16u): +#if defined(TCG_TARGET_HAS_ext16u_i32) || defined(TCG_TARGET_HAS_ext16u_i64) +#ifdef TCG_TARGET_HAS_ext16u_i32 + case INDEX_op_ext16u_i32: +#else + case INDEX_op_ext16u_i64: +#endif return (uint16_t)x; +#endif #if TCG_TARGET_REG_BITS == 64 +#ifdef TCG_TARGET_HAS_ext32s_i32 case INDEX_op_ext32s_i64: return (int32_t)x; +#endif +#ifdef TCG_TARGET_HAS_ext32u_i32 case INDEX_op_ext32u_i64: return (uint32_t)x; +#endif #endif default: @@ -378,8 +448,14 @@ static TCGArg *tcg_constant_folding(TCGContext *s, uint16_t *tcg_opc_ptr, CASE_OP_32_64(shl): CASE_OP_32_64(shr): CASE_OP_32_64(sar): - CASE_OP_32_64(rotl): - CASE_OP_32_64(rotr): +#ifdef TCG_TARGET_HAS_rot_i32 + case INDEX_op_rotl_i32: + case INDEX_op_rotr_i32: +#endif +#ifdef TCG_TARGET_HAS_rot_i64 + case INDEX_op_rotl_i64: + case INDEX_op_rotr_i64: +#endif if (temps[args[1]].state == TCG_TEMP_CONST) { /* Proceed with possible constant folding. */ break; @@ -460,10 +536,30 @@ static TCGArg *tcg_constant_folding(TCGContext *s, uint16_t *tcg_opc_ptr, args += 2; break; CASE_OP_32_64(not): - CASE_OP_32_64(ext8s): - CASE_OP_32_64(ext16s): - CASE_OP_32_64(ext8u): - CASE_OP_32_64(ext16u): +#ifdef TCG_TARGET_HAS_ext8s_i32 + case INDEX_op_ext8s_i32: +#endif +#ifdef TCG_TARGET_HAS_ext8s_i64 + case INDEX_op_ext8s_i64: +#endif +#ifdef TCG_TARGET_HAS_ext16s_i32 + case INDEX_op_ext16s_i32: +#endif +#ifdef TCG_TARGET_HAS_ext16s_i64 + case INDEX_op_ext16s_i64: +#endif +#ifdef TCG_TARGET_HAS_ext8u_i32 + case INDEX_op_ext8u_i32: +#endif +#ifdef TCG_TARGET_HAS_ext8u_i64 + case INDEX_op_ext8u_i64: +#endif +#ifdef TCG_TARGET_HAS_ext16u_i32 + case INDEX_op_ext16u_i32: +#endif +#ifdef TCG_TARGET_HAS_ext16u_i64 + case INDEX_op_ext16u_i64: +#endif #if TCG_TARGET_REG_BITS == 64 case INDEX_op_ext32s_i64: case INDEX_op_ext32u_i64: @@ -492,8 +588,14 @@ static TCGArg *tcg_constant_folding(TCGContext *s, uint16_t *tcg_opc_ptr, CASE_OP_32_64(shl): CASE_OP_32_64(shr): CASE_OP_32_64(sar): - CASE_OP_32_64(rotl): - CASE_OP_32_64(rotr): +#ifdef TCG_TARGET_HAS_rot_i32 + case INDEX_op_rotl_i32: + case INDEX_op_rotr_i32: +#endif +#ifdef TCG_TARGET_HAS_rot_i64 + case INDEX_op_rotl_i64: + case INDEX_op_rotr_i64: +#endif if (temps[args[1]].state == TCG_TEMP_CONST && temps[args[2]].state == TCG_TEMP_CONST) { gen_opc_buf[op_index] = op_to_movi(op); From 2ec00650f66ea624e06d76fadd0918317de1119f Mon Sep 17 00:00:00 2001 From: Blue Swirl Date: Sat, 30 Jul 2011 18:53:27 +0000 Subject: [PATCH 147/230] TCG: fix breakage by previous patch Fix incorrect logic and typos in previous commit 1bfd07bdfe56cea43dbe258dcb161e46b0ee29b7. Signed-off-by: Blue Swirl --- tcg/optimize.c | 19 ++++++++++++------- 1 file changed, 12 insertions(+), 7 deletions(-) diff --git a/tcg/optimize.c b/tcg/optimize.c index 6a0a4ddb33..a3bfa5e71d 100644 --- a/tcg/optimize.c +++ b/tcg/optimize.c @@ -318,7 +318,8 @@ static TCGArg do_constant_folding_2(int op, TCGArg x, TCGArg y) #if defined(TCG_TARGET_HAS_not_i32) || defined(TCG_TARGET_HAS_not_i64) #ifdef TCG_TARGET_HAS_not_i32 case INDEX_op_not_i32: -#else +#endif +#ifdef TCG_TARGET_HAS_not_i64 case INDEX_op_not_i64: #endif return ~x; @@ -327,7 +328,8 @@ static TCGArg do_constant_folding_2(int op, TCGArg x, TCGArg y) #if defined(TCG_TARGET_HAS_ext8s_i32) || defined(TCG_TARGET_HAS_ext8s_i64) #ifdef TCG_TARGET_HAS_ext8s_i32 case INDEX_op_ext8s_i32: -#else +#endif +#ifdef TCG_TARGET_HAS_ext8s_i64 case INDEX_op_ext8s_i64: #endif return (int8_t)x; @@ -336,7 +338,8 @@ static TCGArg do_constant_folding_2(int op, TCGArg x, TCGArg y) #if defined(TCG_TARGET_HAS_ext16s_i32) || defined(TCG_TARGET_HAS_ext16s_i64) #ifdef TCG_TARGET_HAS_ext16s_i32 case INDEX_op_ext16s_i32: -#else +#endif +#ifdef TCG_TARGET_HAS_ext16s_i64 case INDEX_op_ext16s_i64: #endif return (int16_t)x; @@ -345,7 +348,8 @@ static TCGArg do_constant_folding_2(int op, TCGArg x, TCGArg y) #if defined(TCG_TARGET_HAS_ext8u_i32) || defined(TCG_TARGET_HAS_ext8u_i64) #ifdef TCG_TARGET_HAS_ext8u_i32 case INDEX_op_ext8u_i32: -#else +#endif +#ifdef TCG_TARGET_HAS_ext8u_i64 case INDEX_op_ext8u_i64: #endif return (uint8_t)x; @@ -354,19 +358,20 @@ static TCGArg do_constant_folding_2(int op, TCGArg x, TCGArg y) #if defined(TCG_TARGET_HAS_ext16u_i32) || defined(TCG_TARGET_HAS_ext16u_i64) #ifdef TCG_TARGET_HAS_ext16u_i32 case INDEX_op_ext16u_i32: -#else +#endif +#ifdef TCG_TARGET_HAS_ext16u_i64 case INDEX_op_ext16u_i64: #endif return (uint16_t)x; #endif #if TCG_TARGET_REG_BITS == 64 -#ifdef TCG_TARGET_HAS_ext32s_i32 +#ifdef TCG_TARGET_HAS_ext32s_i64 case INDEX_op_ext32s_i64: return (int32_t)x; #endif -#ifdef TCG_TARGET_HAS_ext32u_i32 +#ifdef TCG_TARGET_HAS_ext32u_i64 case INDEX_op_ext32u_i64: return (uint32_t)x; #endif From 927d721777e73339f73719f36eaf400ab641366c Mon Sep 17 00:00:00 2001 From: "Peter A. G. Crosthwaite" Date: Sun, 31 Jul 2011 06:40:13 +0200 Subject: [PATCH 148/230] microblaze: Add missing call to qemu_init_vcpu. Fixes emulation with io-thread. Signed-off-by: Peter A. G. Crosthwaite Signed-off-by: Edgar E. Iglesias --- target-microblaze/translate.c | 1 + 1 file changed, 1 insertion(+) diff --git a/target-microblaze/translate.c b/target-microblaze/translate.c index 31e8306ef3..41beb0a8e8 100644 --- a/target-microblaze/translate.c +++ b/target-microblaze/translate.c @@ -1850,6 +1850,7 @@ CPUState *cpu_mb_init (const char *cpu_model) cpu_exec_init(env); cpu_reset(env); + qemu_init_vcpu(env); set_float_rounding_mode(float_round_nearest_even, &env->fp_status); if (tcg_initialized) From ea8f942fe46dd10e0946f02ab9d698fb41e958f7 Mon Sep 17 00:00:00 2001 From: Markus Armbruster Date: Wed, 20 Jul 2011 18:23:35 +0200 Subject: [PATCH 149/230] blockdev: Make eject fail for non-removable drives even with -f Ejecting hard disk platters can only end in tears. If you need to revoke access to an image, use drive_del, not eject -f. Signed-off-by: Markus Armbruster Signed-off-by: Kevin Wolf --- blockdev.c | 17 +++++++---------- 1 file changed, 7 insertions(+), 10 deletions(-) diff --git a/blockdev.c b/blockdev.c index 0b8d3a4f83..a25367a9e3 100644 --- a/blockdev.c +++ b/blockdev.c @@ -646,16 +646,13 @@ out: static int eject_device(Monitor *mon, BlockDriverState *bs, int force) { - if (!force) { - if (!bdrv_is_removable(bs)) { - qerror_report(QERR_DEVICE_NOT_REMOVABLE, - bdrv_get_device_name(bs)); - return -1; - } - if (bdrv_is_locked(bs)) { - qerror_report(QERR_DEVICE_LOCKED, bdrv_get_device_name(bs)); - return -1; - } + if (!bdrv_is_removable(bs)) { + qerror_report(QERR_DEVICE_NOT_REMOVABLE, bdrv_get_device_name(bs)); + return -1; + } + if (!force && bdrv_is_locked(bs)) { + qerror_report(QERR_DEVICE_LOCKED, bdrv_get_device_name(bs)); + return -1; } bdrv_close(bs); return 0; From a19712b0dbe43016fb17ec48bfff2f360225fe97 Mon Sep 17 00:00:00 2001 From: Markus Armbruster Date: Wed, 20 Jul 2011 18:23:36 +0200 Subject: [PATCH 150/230] block: Reset device model callbacks on detach BlockDriverState members change_cb and change_opaque are initially null. The device model may set them, with bdrv_set_change_cb(). If the device model gets detached (hot unplug), they're left dangling. Only safe because device hot unplug automatically destroys the BlockDriverState. But that's a questionable feature, best not to rely on it. Signed-off-by: Markus Armbruster Signed-off-by: Kevin Wolf --- block.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/block.c b/block.c index 9549b9eff9..81a82578a2 100644 --- a/block.c +++ b/block.c @@ -730,6 +730,8 @@ void bdrv_detach(BlockDriverState *bs, DeviceState *qdev) { assert(bs->peer == qdev); bs->peer = NULL; + bs->change_cb = NULL; + bs->change_opaque = NULL; } DeviceState *bdrv_get_attached(BlockDriverState *bs) From 02266d547a6c7b10e1ac1574ec69b92f4e28f817 Mon Sep 17 00:00:00 2001 From: Markus Armbruster Date: Wed, 20 Jul 2011 18:23:40 +0200 Subject: [PATCH 151/230] block/raw-win32: Drop disabled code for removable host devices It's been disabled since the start (commit 19cb3738, Aug 2006), and has been untouched except for spelling fixes and such. I don't feel like dragging it along any further. Signed-off-by: Markus Armbruster Signed-off-by: Kevin Wolf --- block/raw-win32.c | 35 ----------------------------------- 1 file changed, 35 deletions(-) diff --git a/block/raw-win32.c b/block/raw-win32.c index 91067e7595..e47cfe0f4a 100644 --- a/block/raw-win32.c +++ b/block/raw-win32.c @@ -393,41 +393,6 @@ static int hdev_open(BlockDriverState *bs, const char *filename, int flags) return 0; } -#if 0 -/***********************************************/ -/* removable device additional commands */ - -static int raw_is_inserted(BlockDriverState *bs) -{ - return 1; -} - -static int raw_media_changed(BlockDriverState *bs) -{ - return -ENOTSUP; -} - -static int raw_eject(BlockDriverState *bs, int eject_flag) -{ - DWORD ret_count; - - if (s->type == FTYPE_FILE) - return -ENOTSUP; - if (eject_flag) { - DeviceIoControl(s->hfile, IOCTL_STORAGE_EJECT_MEDIA, - NULL, 0, NULL, 0, &lpBytesReturned, NULL); - } else { - DeviceIoControl(s->hfile, IOCTL_STORAGE_LOAD_MEDIA, - NULL, 0, NULL, 0, &lpBytesReturned, NULL); - } -} - -static int raw_set_locked(BlockDriverState *bs, int locked) -{ - return -ENOTSUP; -} -#endif - static int hdev_has_zero_init(BlockDriverState *bs) { return 0; From 7bf37feddcfa527304cfdc02bd2db8912ee9bf8c Mon Sep 17 00:00:00 2001 From: Markus Armbruster Date: Wed, 20 Jul 2011 18:23:41 +0200 Subject: [PATCH 152/230] block: Make BlockDriver method bdrv_set_locked() return void The only caller is bdrv_set_locked(), and it ignores the value. Callees always return 0, except for FreeBSD's cdrom_set_locked(), which returns -ENOTSUP when the device is in a terminally wedged state. Signed-off-by: Markus Armbruster Signed-off-by: Kevin Wolf --- block/raw-posix.c | 10 +++------- block/raw.c | 3 +-- block_int.h | 2 +- 3 files changed, 5 insertions(+), 10 deletions(-) diff --git a/block/raw-posix.c b/block/raw-posix.c index cd89c8312a..5241308d23 100644 --- a/block/raw-posix.c +++ b/block/raw-posix.c @@ -1363,7 +1363,7 @@ static int cdrom_eject(BlockDriverState *bs, int eject_flag) return 0; } -static int cdrom_set_locked(BlockDriverState *bs, int locked) +static void cdrom_set_locked(BlockDriverState *bs, int locked) { BDRVRawState *s = bs->opaque; @@ -1374,8 +1374,6 @@ static int cdrom_set_locked(BlockDriverState *bs, int locked) */ /* perror("CDROM_LOCKDOOR"); */ } - - return 0; } static BlockDriver bdrv_host_cdrom = { @@ -1486,12 +1484,12 @@ static int cdrom_eject(BlockDriverState *bs, int eject_flag) return 0; } -static int cdrom_set_locked(BlockDriverState *bs, int locked) +static void cdrom_set_locked(BlockDriverState *bs, int locked) { BDRVRawState *s = bs->opaque; if (s->fd < 0) - return -ENOTSUP; + return; if (ioctl(s->fd, (locked ? CDIOCPREVENT : CDIOCALLOW)) < 0) { /* * Note: an error can happen if the distribution automatically @@ -1499,8 +1497,6 @@ static int cdrom_set_locked(BlockDriverState *bs, int locked) */ /* perror("CDROM_LOCKDOOR"); */ } - - return 0; } static BlockDriver bdrv_host_cdrom = { diff --git a/block/raw.c b/block/raw.c index b0f72d6a62..1398a9c221 100644 --- a/block/raw.c +++ b/block/raw.c @@ -80,10 +80,9 @@ static int raw_eject(BlockDriverState *bs, int eject_flag) return bdrv_eject(bs->file, eject_flag); } -static int raw_set_locked(BlockDriverState *bs, int locked) +static void raw_set_locked(BlockDriverState *bs, int locked) { bdrv_set_locked(bs->file, locked); - return 0; } static int raw_ioctl(BlockDriverState *bs, unsigned long int req, void *buf) diff --git a/block_int.h b/block_int.h index efb68038c4..e0b638c116 100644 --- a/block_int.h +++ b/block_int.h @@ -113,7 +113,7 @@ struct BlockDriver { int (*bdrv_is_inserted)(BlockDriverState *bs); int (*bdrv_media_changed)(BlockDriverState *bs); int (*bdrv_eject)(BlockDriverState *bs, int eject_flag); - int (*bdrv_set_locked)(BlockDriverState *bs, int locked); + void (*bdrv_set_locked)(BlockDriverState *bs, int locked); /* to control generic scsi devices */ int (*bdrv_ioctl)(BlockDriverState *bs, unsigned long int req, void *buf); From 822e1cd17e8fa3ae98d0481c20f042316ace3fbc Mon Sep 17 00:00:00 2001 From: Markus Armbruster Date: Wed, 20 Jul 2011 18:23:42 +0200 Subject: [PATCH 153/230] block: Make BlockDriver method bdrv_eject() return void Callees always return 0, except for FreeBSD's cdrom_eject(), which returns -ENOTSUP when the device is in a terminally wedged state. The only caller is bdrv_eject(), and it maps -ENOTSUP to 0 since commit 4be9762a. Signed-off-by: Markus Armbruster Signed-off-by: Kevin Wolf --- block.c | 17 ++++------------- block/raw-posix.c | 16 +++++----------- block/raw.c | 4 ++-- block_int.h | 2 +- 4 files changed, 12 insertions(+), 27 deletions(-) diff --git a/block.c b/block.c index 81a82578a2..7c25fe4990 100644 --- a/block.c +++ b/block.c @@ -2770,25 +2770,16 @@ int bdrv_media_changed(BlockDriverState *bs) int bdrv_eject(BlockDriverState *bs, int eject_flag) { BlockDriver *drv = bs->drv; - int ret; if (bs->locked) { return -EBUSY; } - if (!drv || !drv->bdrv_eject) { - ret = -ENOTSUP; - } else { - ret = drv->bdrv_eject(bs, eject_flag); + if (drv && drv->bdrv_eject) { + drv->bdrv_eject(bs, eject_flag); } - if (ret == -ENOTSUP) { - ret = 0; - } - if (ret >= 0) { - bs->tray_open = eject_flag; - } - - return ret; + bs->tray_open = eject_flag; + return 0; } int bdrv_is_locked(BlockDriverState *bs) diff --git a/block/raw-posix.c b/block/raw-posix.c index 5241308d23..6672d31da3 100644 --- a/block/raw-posix.c +++ b/block/raw-posix.c @@ -1254,7 +1254,7 @@ static int floppy_media_changed(BlockDriverState *bs) return ret; } -static int floppy_eject(BlockDriverState *bs, int eject_flag) +static void floppy_eject(BlockDriverState *bs, int eject_flag) { BDRVRawState *s = bs->opaque; int fd; @@ -1269,8 +1269,6 @@ static int floppy_eject(BlockDriverState *bs, int eject_flag) perror("FDEJECT"); close(fd); } - - return 0; } static BlockDriver bdrv_host_floppy = { @@ -1348,7 +1346,7 @@ static int cdrom_is_inserted(BlockDriverState *bs) return 0; } -static int cdrom_eject(BlockDriverState *bs, int eject_flag) +static void cdrom_eject(BlockDriverState *bs, int eject_flag) { BDRVRawState *s = bs->opaque; @@ -1359,8 +1357,6 @@ static int cdrom_eject(BlockDriverState *bs, int eject_flag) if (ioctl(s->fd, CDROMCLOSETRAY, NULL) < 0) perror("CDROMEJECT"); } - - return 0; } static void cdrom_set_locked(BlockDriverState *bs, int locked) @@ -1462,12 +1458,12 @@ static int cdrom_is_inserted(BlockDriverState *bs) return raw_getlength(bs) > 0; } -static int cdrom_eject(BlockDriverState *bs, int eject_flag) +static void cdrom_eject(BlockDriverState *bs, int eject_flag) { BDRVRawState *s = bs->opaque; if (s->fd < 0) - return -ENOTSUP; + return; (void) ioctl(s->fd, CDIOCALLOW); @@ -1479,9 +1475,7 @@ static int cdrom_eject(BlockDriverState *bs, int eject_flag) perror("CDIOCCLOSE"); } - if (cdrom_reopen(bs) < 0) - return -ENOTSUP; - return 0; + cdrom_reopen(bs); } static void cdrom_set_locked(BlockDriverState *bs, int locked) diff --git a/block/raw.c b/block/raw.c index 1398a9c221..cb6203eeca 100644 --- a/block/raw.c +++ b/block/raw.c @@ -75,9 +75,9 @@ static int raw_is_inserted(BlockDriverState *bs) return bdrv_is_inserted(bs->file); } -static int raw_eject(BlockDriverState *bs, int eject_flag) +static void raw_eject(BlockDriverState *bs, int eject_flag) { - return bdrv_eject(bs->file, eject_flag); + bdrv_eject(bs->file, eject_flag); } static void raw_set_locked(BlockDriverState *bs, int locked) diff --git a/block_int.h b/block_int.h index e0b638c116..efefbee289 100644 --- a/block_int.h +++ b/block_int.h @@ -112,7 +112,7 @@ struct BlockDriver { /* removable device specific */ int (*bdrv_is_inserted)(BlockDriverState *bs); int (*bdrv_media_changed)(BlockDriverState *bs); - int (*bdrv_eject)(BlockDriverState *bs, int eject_flag); + void (*bdrv_eject)(BlockDriverState *bs, int eject_flag); void (*bdrv_set_locked)(BlockDriverState *bs, int locked); /* to control generic scsi devices */ From 49aa46bb4b894ff8bdb0339ee2a5dd3fcfe93ecd Mon Sep 17 00:00:00 2001 From: Markus Armbruster Date: Wed, 20 Jul 2011 18:23:43 +0200 Subject: [PATCH 154/230] block: Don't let locked flag prevent medium load Commit aea2a33c made bdrv_eject() obey the locked flag. Correct for medium eject (eject_flag set), incorrect for medium load (eject_flag clear). See MMC-5 Table 341 "Actions for Lock/Unlock/Eject". Signed-off-by: Markus Armbruster Signed-off-by: Kevin Wolf --- block.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/block.c b/block.c index 7c25fe4990..8859f9b414 100644 --- a/block.c +++ b/block.c @@ -2771,7 +2771,7 @@ int bdrv_eject(BlockDriverState *bs, int eject_flag) { BlockDriver *drv = bs->drv; - if (bs->locked) { + if (eject_flag && bs->locked) { return -EBUSY; } From efc8243d00ab4cf4fa05a9be93233cb883b7caa0 Mon Sep 17 00:00:00 2001 From: "Serge E. Hallyn" Date: Mon, 25 Jul 2011 18:34:35 +0000 Subject: [PATCH 155/230] block/vpc.c: Detect too-large vpc file VHD files technically can be up to 2Tb, but virtual pc is limited to 127G. Currently qemu-img refused to create vpc files > 127G, but it is failing to return error when converting from a non-vpc VHD file which is >127G. It returns success, but creates a truncated converted image. Also, qemu-img info claims the vpc file is 127G (and clean). This patch detects a too-large vpc file and returns -EFBIG. Without this patch, ============================================================= root@ip-10-38-123-242:~/qemu-fixed# qemu-img info /mnt/140g-dynamic.vhd image: /mnt/140g-dynamic.vhd file format: vpc virtual size: 127G (136899993600 bytes) disk size: 284K root@ip-10-38-123-242:~/qemu-fixed# qemu-img convert -f vpc -O raw /mnt/140g-dynamic.vhd /mnt/y root@ip-10-38-123-242:~/qemu-fixed# echo $? 0 root@ip-10-38-123-242:~/qemu-fixed# qemu-img info /mnt/y image: /mnt/y file format: raw virtual size: 127G (136899993600 bytes) disk size: 0 ============================================================= (The 140G image was truncated with no warning or error.) With the patch, I get: ============================================================= root@ip-10-38-123-242:~/qemu-fixed# ./qemu-img info /mnt/140g-dynamic.vhd qemu-img: Could not open '/mnt/140g-dynamic.vhd': File too large root@ip-10-38-123-242:~/qemu-fixed# ./qemu-img convert -f vpc -O raw /mnt/140g-dynamic.vhd /mnt/y qemu-img: Could not open '/mnt/140g-dynamic.vhd': File too large qemu-img: Could not open '/mnt/140g-dynamic.vhd' ============================================================= See https://bugs.launchpad.net/qemu/+bug/814222 for details. Signed-off-by: Serge Hallyn Signed-off-by: Kevin Wolf --- block/vpc.c | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/block/vpc.c b/block/vpc.c index 56865da5bc..fdd5236892 100644 --- a/block/vpc.c +++ b/block/vpc.c @@ -156,6 +156,7 @@ static int vpc_open(BlockDriverState *bs, int flags) struct vhd_dyndisk_header* dyndisk_header; uint8_t buf[HEADER_SIZE]; uint32_t checksum; + int err = -1; if (bdrv_pread(bs->file, 0, s->footer_buf, HEADER_SIZE) != HEADER_SIZE) goto fail; @@ -176,6 +177,11 @@ static int vpc_open(BlockDriverState *bs, int flags) bs->total_sectors = (int64_t) be16_to_cpu(footer->cyls) * footer->heads * footer->secs_per_cyl; + if (bs->total_sectors >= 65535 * 16 * 255) { + err = -EFBIG; + goto fail; + } + if (bdrv_pread(bs->file, be64_to_cpu(footer->data_offset), buf, HEADER_SIZE) != HEADER_SIZE) goto fail; @@ -222,7 +228,7 @@ static int vpc_open(BlockDriverState *bs, int flags) return 0; fail: - return -1; + return err; } /* From 5f71d32f0da4d1e578738f765b57fbfaf4bd3214 Mon Sep 17 00:00:00 2001 From: Hannes Reinecke Date: Fri, 22 Jul 2011 16:51:12 +0200 Subject: [PATCH 156/230] scsi-disk: Codingstyle fixes Replace tabs with spaces. Signed-off-by: Hannes Reinecke Signed-off-by: Kevin Wolf --- hw/scsi-disk.c | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/hw/scsi-disk.c b/hw/scsi-disk.c index f42a5d1f85..715f2cdec4 100644 --- a/hw/scsi-disk.c +++ b/hw/scsi-disk.c @@ -526,7 +526,7 @@ static int scsi_disk_emulate_inquiry(SCSIRequest *req, uint8_t *outbuf) memset(outbuf, 0, buflen); if (req->lun) { - outbuf[0] = 0x7f; /* LUN not supported */ + outbuf[0] = 0x7f; /* LUN not supported */ return buflen; } @@ -836,7 +836,7 @@ static int scsi_disk_emulate_command(SCSIDiskReq *r, uint8_t *outbuf) case TEST_UNIT_READY: if (!bdrv_is_inserted(s->bs)) goto not_ready; - break; + break; case REQUEST_SENSE: if (req->cmd.xfer < 4) goto illegal_request; @@ -848,7 +848,7 @@ static int scsi_disk_emulate_command(SCSIDiskReq *r, uint8_t *outbuf) buflen = scsi_disk_emulate_inquiry(req, outbuf); if (buflen < 0) goto illegal_request; - break; + break; case MODE_SENSE: case MODE_SENSE_10: buflen = scsi_disk_emulate_mode_sense(req, outbuf); @@ -881,14 +881,14 @@ static int scsi_disk_emulate_command(SCSIDiskReq *r, uint8_t *outbuf) /* load/eject medium */ bdrv_eject(s->bs, !(req->cmd.buf[4] & 1)); } - break; + break; case ALLOW_MEDIUM_REMOVAL: bdrv_set_locked(s->bs, req->cmd.buf[4] & 1); - break; + break; case READ_CAPACITY: /* The normal LEN field for this command is zero. */ - memset(outbuf, 0, 8); - bdrv_get_geometry(s->bs, &nb_sectors); + memset(outbuf, 0, 8); + bdrv_get_geometry(s->bs, &nb_sectors); if (!nb_sectors) goto not_ready; nb_sectors /= s->cluster_size; @@ -908,7 +908,7 @@ static int scsi_disk_emulate_command(SCSIDiskReq *r, uint8_t *outbuf) outbuf[6] = s->cluster_size * 2; outbuf[7] = 0; buflen = 8; - break; + break; case SYNCHRONIZE_CACHE: ret = bdrv_flush(s->bs); if (ret < 0) { From 3790372c963dbc87d4efdf24f8b718c283798fa0 Mon Sep 17 00:00:00 2001 From: Hannes Reinecke Date: Fri, 22 Jul 2011 16:51:13 +0200 Subject: [PATCH 157/230] scsi: Remove references to SET_WINDOW SET_WINDOW command is vendor-specific only. So we shouldn't try to emulate it. Signed-off-by: Hannes Reinecke Signed-off-by: Kevin Wolf --- hw/scsi-bus.c | 2 -- hw/scsi-defs.h | 1 - 2 files changed, 3 deletions(-) diff --git a/hw/scsi-bus.c b/hw/scsi-bus.c index 8b1a412210..facc98d527 100644 --- a/hw/scsi-bus.c +++ b/hw/scsi-bus.c @@ -350,7 +350,6 @@ static void scsi_req_xfer_mode(SCSIRequest *req) case SEARCH_HIGH_12: case SEARCH_EQUAL_12: case SEARCH_LOW_12: - case SET_WINDOW: case MEDIUM_SCAN: case SEND_VOLUME_TAG: case WRITE_LONG_2: @@ -544,7 +543,6 @@ static const char *scsi_command_name(uint8_t cmd) [ SEND_DIAGNOSTIC ] = "SEND_DIAGNOSTIC", [ ALLOW_MEDIUM_REMOVAL ] = "ALLOW_MEDIUM_REMOVAL", - [ SET_WINDOW ] = "SET_WINDOW", [ READ_CAPACITY ] = "READ_CAPACITY", [ READ_10 ] = "READ_10", [ WRITE_10 ] = "WRITE_10", diff --git a/hw/scsi-defs.h b/hw/scsi-defs.h index 413cce07b5..8513983e34 100644 --- a/hw/scsi-defs.h +++ b/hw/scsi-defs.h @@ -49,7 +49,6 @@ #define SEND_DIAGNOSTIC 0x1d #define ALLOW_MEDIUM_REMOVAL 0x1e -#define SET_WINDOW 0x24 #define READ_CAPACITY 0x25 #define READ_10 0x28 #define WRITE_10 0x2a From 8bd3e139c638d9742e12da33007a19c5204302af Mon Sep 17 00:00:00 2001 From: Hannes Reinecke Date: Fri, 22 Jul 2011 16:51:14 +0200 Subject: [PATCH 158/230] scsi: Remove REZERO_UNIT emulation REZERO_UNIT command is obsolete. Remove support for it. Signed-off-by: Hannes Reinecke Signed-off-by: Kevin Wolf --- hw/scsi-bus.c | 3 --- hw/scsi-defs.h | 1 - hw/scsi-disk.c | 7 ------- 3 files changed, 11 deletions(-) diff --git a/hw/scsi-bus.c b/hw/scsi-bus.c index facc98d527..52a67846e7 100644 --- a/hw/scsi-bus.c +++ b/hw/scsi-bus.c @@ -223,7 +223,6 @@ static int scsi_req_length(SCSIRequest *req, uint8_t *cmd) switch(cmd[0]) { case TEST_UNIT_READY: - case REZERO_UNIT: case START_STOP: case SEEK_6: case WRITE_FILEMARKS: @@ -516,8 +515,6 @@ static const char *scsi_command_name(uint8_t cmd) { static const char *names[] = { [ TEST_UNIT_READY ] = "TEST_UNIT_READY", - [ REZERO_UNIT ] = "REZERO_UNIT", - /* REWIND and REZERO_UNIT use the same operation code */ [ REQUEST_SENSE ] = "REQUEST_SENSE", [ FORMAT_UNIT ] = "FORMAT_UNIT", [ READ_BLOCK_LIMITS ] = "READ_BLOCK_LIMITS", diff --git a/hw/scsi-defs.h b/hw/scsi-defs.h index 8513983e34..1f40c5c8a5 100644 --- a/hw/scsi-defs.h +++ b/hw/scsi-defs.h @@ -25,7 +25,6 @@ */ #define TEST_UNIT_READY 0x00 -#define REZERO_UNIT 0x01 #define REQUEST_SENSE 0x03 #define FORMAT_UNIT 0x04 #define READ_BLOCK_LIMITS 0x05 diff --git a/hw/scsi-disk.c b/hw/scsi-disk.c index 715f2cdec4..abf0bd21ec 100644 --- a/hw/scsi-disk.c +++ b/hw/scsi-disk.c @@ -972,12 +972,6 @@ static int scsi_disk_emulate_command(SCSIDiskReq *r, uint8_t *outbuf) break; case VERIFY: break; - case REZERO_UNIT: - DPRINTF("Rezero Unit\n"); - if (!bdrv_is_inserted(s->bs)) { - goto not_ready; - } - break; default: scsi_command_complete(r, CHECK_CONDITION, SENSE_CODE(INVALID_OPCODE)); return -1; @@ -1059,7 +1053,6 @@ static int32_t scsi_send_command(SCSIRequest *req, uint8_t *buf) case SERVICE_ACTION_IN: case REPORT_LUNS: case VERIFY: - case REZERO_UNIT: rc = scsi_disk_emulate_command(r, outbuf); if (rc < 0) { return 0; From 5e30a07d6d70d3073ff61e6db79d61c2b688502f Mon Sep 17 00:00:00 2001 From: Hannes Reinecke Date: Fri, 22 Jul 2011 16:51:15 +0200 Subject: [PATCH 159/230] scsi: Sanitize command definitions Sanitize SCSI command definitions. Add _10 suffix to READ_CAPACITY, WRITE_VERIFY, VERIFY, READ_LONG, WRITE_LONG, and WRITE_SAME. Add new command definitions for LOCATE_10, UNMAP, VARLENGTH_CDB, WRITE_FILEMARKS_16, EXTENDED_COPY, ATA_PASSTHROUGH, ACCESS_CONTROL_IN, ACCESS_CONTROL_OUT, COMPARE_AND_WRITE, VERIFY_16, SYNCHRONIZE_CACHE_16, LOCATE_16, ERASE_16, WRITE_LONG_16, LOAD_UNLOAD, VERIFY_12. Remove invalid definition of WRITE_LONG_2. Signed-off-by: Hannes Reinecke Signed-off-by: Kevin Wolf --- hw/scsi-bus.c | 69 ++++++++++++++++++++++++++++------------------- hw/scsi-defs.h | 54 ++++++++++++++++++++++--------------- hw/scsi-disk.c | 10 +++---- hw/scsi-generic.c | 2 +- 4 files changed, 81 insertions(+), 54 deletions(-) diff --git a/hw/scsi-bus.c b/hw/scsi-bus.c index 52a67846e7..0b0344c1fd 100644 --- a/hw/scsi-bus.c +++ b/hw/scsi-bus.c @@ -223,6 +223,7 @@ static int scsi_req_length(SCSIRequest *req, uint8_t *cmd) switch(cmd[0]) { case TEST_UNIT_READY: + case REWIND: case START_STOP: case SEEK_6: case WRITE_FILEMARKS: @@ -231,24 +232,24 @@ static int scsi_req_length(SCSIRequest *req, uint8_t *cmd) case RELEASE: case ERASE: case ALLOW_MEDIUM_REMOVAL: - case VERIFY: + case VERIFY_10: case SEEK_10: case SYNCHRONIZE_CACHE: case LOCK_UNLOCK_CACHE: case LOAD_UNLOAD: case SET_CD_SPEED: case SET_LIMITS: - case WRITE_LONG: + case WRITE_LONG_10: case MOVE_MEDIUM: case UPDATE_BLOCK: req->cmd.xfer = 0; break; case MODE_SENSE: break; - case WRITE_SAME: + case WRITE_SAME_10: req->cmd.xfer = 1; break; - case READ_CAPACITY: + case READ_CAPACITY_10: req->cmd.xfer = 8; break; case READ_BLOCK_LIMITS: @@ -264,7 +265,7 @@ static int scsi_req_length(SCSIRequest *req, uint8_t *cmd) req->cmd.xfer *= 8; break; case WRITE_10: - case WRITE_VERIFY: + case WRITE_VERIFY_10: case WRITE_6: case WRITE_12: case WRITE_VERIFY_12: @@ -324,7 +325,7 @@ static void scsi_req_xfer_mode(SCSIRequest *req) switch (req->cmd.buf[0]) { case WRITE_6: case WRITE_10: - case WRITE_VERIFY: + case WRITE_VERIFY_10: case WRITE_12: case WRITE_VERIFY_12: case WRITE_16: @@ -344,14 +345,13 @@ static void scsi_req_xfer_mode(SCSIRequest *req) case SEARCH_HIGH: case SEARCH_LOW: case UPDATE_BLOCK: - case WRITE_LONG: - case WRITE_SAME: + case WRITE_LONG_10: + case WRITE_SAME_10: case SEARCH_HIGH_12: case SEARCH_EQUAL_12: case SEARCH_LOW_12: case MEDIUM_SCAN: case SEND_VOLUME_TAG: - case WRITE_LONG_2: case PERSISTENT_RESERVE_OUT: case MAINTENANCE_OUT: req->cmd.mode = SCSI_XFER_TO_DEV; @@ -515,6 +515,7 @@ static const char *scsi_command_name(uint8_t cmd) { static const char *names[] = { [ TEST_UNIT_READY ] = "TEST_UNIT_READY", + [ REWIND ] = "REWIND", [ REQUEST_SENSE ] = "REQUEST_SENSE", [ FORMAT_UNIT ] = "FORMAT_UNIT", [ READ_BLOCK_LIMITS ] = "READ_BLOCK_LIMITS", @@ -539,13 +540,12 @@ static const char *scsi_command_name(uint8_t cmd) [ RECEIVE_DIAGNOSTIC ] = "RECEIVE_DIAGNOSTIC", [ SEND_DIAGNOSTIC ] = "SEND_DIAGNOSTIC", [ ALLOW_MEDIUM_REMOVAL ] = "ALLOW_MEDIUM_REMOVAL", - - [ READ_CAPACITY ] = "READ_CAPACITY", + [ READ_CAPACITY_10 ] = "READ_CAPACITY_10", [ READ_10 ] = "READ_10", [ WRITE_10 ] = "WRITE_10", [ SEEK_10 ] = "SEEK_10", - [ WRITE_VERIFY ] = "WRITE_VERIFY", - [ VERIFY ] = "VERIFY", + [ WRITE_VERIFY_10 ] = "WRITE_VERIFY_10", + [ VERIFY_10 ] = "VERIFY_10", [ SEARCH_HIGH ] = "SEARCH_HIGH", [ SEARCH_EQUAL ] = "SEARCH_EQUAL", [ SEARCH_LOW ] = "SEARCH_LOW", @@ -561,11 +561,14 @@ static const char *scsi_command_name(uint8_t cmd) [ WRITE_BUFFER ] = "WRITE_BUFFER", [ READ_BUFFER ] = "READ_BUFFER", [ UPDATE_BLOCK ] = "UPDATE_BLOCK", - [ READ_LONG ] = "READ_LONG", - [ WRITE_LONG ] = "WRITE_LONG", + [ READ_LONG_10 ] = "READ_LONG_10", + [ WRITE_LONG_10 ] = "WRITE_LONG_10", [ CHANGE_DEFINITION ] = "CHANGE_DEFINITION", - [ WRITE_SAME ] = "WRITE_SAME", + [ WRITE_SAME_10 ] = "WRITE_SAME_10", + [ UNMAP ] = "UNMAP", [ READ_TOC ] = "READ_TOC", + [ REPORT_DENSITY_SUPPORT ] = "REPORT_DENSITY_SUPPORT", + [ GET_CONFIGURATION ] = "GET_CONFIGURATION", [ LOG_SELECT ] = "LOG_SELECT", [ LOG_SENSE ] = "LOG_SENSE", [ MODE_SELECT_10 ] = "MODE_SELECT_10", @@ -574,27 +577,39 @@ static const char *scsi_command_name(uint8_t cmd) [ MODE_SENSE_10 ] = "MODE_SENSE_10", [ PERSISTENT_RESERVE_IN ] = "PERSISTENT_RESERVE_IN", [ PERSISTENT_RESERVE_OUT ] = "PERSISTENT_RESERVE_OUT", + [ WRITE_FILEMARKS_16 ] = "WRITE_FILEMARKS_16", + [ EXTENDED_COPY ] = "EXTENDED_COPY", + [ ATA_PASSTHROUGH ] = "ATA_PASSTHROUGH", + [ ACCESS_CONTROL_IN ] = "ACCESS_CONTROL_IN", + [ ACCESS_CONTROL_OUT ] = "ACCESS_CONTROL_OUT", + [ READ_16 ] = "READ_16", + [ COMPARE_AND_WRITE ] = "COMPARE_AND_WRITE", + [ WRITE_16 ] = "WRITE_16", + [ WRITE_VERIFY_16 ] = "WRITE_VERIFY_16", + [ VERIFY_16 ] = "VERIFY_16", + [ SYNCHRONIZE_CACHE_16 ] = "SYNCHRONIZE_CACHE_16", + [ LOCATE_16 ] = "LOCATE_16", + [ WRITE_SAME_16 ] = "WRITE_SAME_16", + [ ERASE_16 ] = "ERASE_16", + [ SERVICE_ACTION_IN ] = "SERVICE_ACTION_IN", + [ WRITE_LONG_16 ] = "WRITE_LONG_16", + [ REPORT_LUNS ] = "REPORT_LUNS", + [ BLANK ] = "BLANK", + [ MAINTENANCE_IN ] = "MAINTENANCE_IN", + [ MAINTENANCE_OUT ] = "MAINTENANCE_OUT", [ MOVE_MEDIUM ] = "MOVE_MEDIUM", + [ LOAD_UNLOAD ] = "LOAD_UNLOAD", [ READ_12 ] = "READ_12", [ WRITE_12 ] = "WRITE_12", [ WRITE_VERIFY_12 ] = "WRITE_VERIFY_12", + [ VERIFY_12 ] = "VERIFY_12", [ SEARCH_HIGH_12 ] = "SEARCH_HIGH_12", [ SEARCH_EQUAL_12 ] = "SEARCH_EQUAL_12", [ SEARCH_LOW_12 ] = "SEARCH_LOW_12", [ READ_ELEMENT_STATUS ] = "READ_ELEMENT_STATUS", [ SEND_VOLUME_TAG ] = "SEND_VOLUME_TAG", - [ WRITE_LONG_2 ] = "WRITE_LONG_2", - - [ REPORT_DENSITY_SUPPORT ] = "REPORT_DENSITY_SUPPORT", - [ GET_CONFIGURATION ] = "GET_CONFIGURATION", - [ READ_16 ] = "READ_16", - [ WRITE_16 ] = "WRITE_16", - [ WRITE_VERIFY_16 ] = "WRITE_VERIFY_16", - [ SERVICE_ACTION_IN ] = "SERVICE_ACTION_IN", - [ REPORT_LUNS ] = "REPORT_LUNS", - [ LOAD_UNLOAD ] = "LOAD_UNLOAD", + [ READ_DEFECT_DATA_12 ] = "READ_DEFECT_DATA_12", [ SET_CD_SPEED ] = "SET_CD_SPEED", - [ BLANK ] = "BLANK", }; if (cmd >= ARRAY_SIZE(names) || names[cmd] == NULL) diff --git a/hw/scsi-defs.h b/hw/scsi-defs.h index 1f40c5c8a5..f644860831 100644 --- a/hw/scsi-defs.h +++ b/hw/scsi-defs.h @@ -25,6 +25,7 @@ */ #define TEST_UNIT_READY 0x00 +#define REWIND 0x01 #define REQUEST_SENSE 0x03 #define FORMAT_UNIT 0x04 #define READ_BLOCK_LIMITS 0x05 @@ -47,13 +48,13 @@ #define RECEIVE_DIAGNOSTIC 0x1c #define SEND_DIAGNOSTIC 0x1d #define ALLOW_MEDIUM_REMOVAL 0x1e - -#define READ_CAPACITY 0x25 +#define READ_CAPACITY_10 0x25 #define READ_10 0x28 #define WRITE_10 0x2a #define SEEK_10 0x2b -#define WRITE_VERIFY 0x2e -#define VERIFY 0x2f +#define LOCATE_10 0x2b +#define WRITE_VERIFY_10 0x2e +#define VERIFY_10 0x2f #define SEARCH_HIGH 0x30 #define SEARCH_EQUAL 0x31 #define SEARCH_LOW 0x32 @@ -69,11 +70,14 @@ #define WRITE_BUFFER 0x3b #define READ_BUFFER 0x3c #define UPDATE_BLOCK 0x3d -#define READ_LONG 0x3e -#define WRITE_LONG 0x3f +#define READ_LONG_10 0x3e +#define WRITE_LONG_10 0x3f #define CHANGE_DEFINITION 0x40 -#define WRITE_SAME 0x41 +#define WRITE_SAME_10 0x41 +#define UNMAP 0x42 #define READ_TOC 0x43 +#define REPORT_DENSITY_SUPPORT 0x44 +#define GET_CONFIGURATION 0x46 #define LOG_SELECT 0x4c #define LOG_SENSE 0x4d #define MODE_SELECT_10 0x55 @@ -82,32 +86,40 @@ #define MODE_SENSE_10 0x5a #define PERSISTENT_RESERVE_IN 0x5e #define PERSISTENT_RESERVE_OUT 0x5f +#define VARLENGTH_CDB 0x7f +#define WRITE_FILEMARKS_16 0x80 +#define EXTENDED_COPY 0x83 +#define ATA_PASSTHROUGH 0x85 +#define ACCESS_CONTROL_IN 0x86 +#define ACCESS_CONTROL_OUT 0x87 +#define READ_16 0x88 +#define COMPARE_AND_WRITE 0x89 +#define WRITE_16 0x8a +#define WRITE_VERIFY_16 0x8e +#define VERIFY_16 0x8f +#define SYNCHRONIZE_CACHE_16 0x91 +#define LOCATE_16 0x92 #define WRITE_SAME_16 0x93 +#define ERASE_16 0x93 +#define SERVICE_ACTION_IN 0x9e +#define WRITE_LONG_16 0x9f +#define REPORT_LUNS 0xa0 +#define BLANK 0xa1 #define MAINTENANCE_IN 0xa3 #define MAINTENANCE_OUT 0xa4 #define MOVE_MEDIUM 0xa5 +#define LOAD_UNLOAD 0xa6 #define READ_12 0xa8 #define WRITE_12 0xaa #define WRITE_VERIFY_12 0xae +#define VERIFY_12 0xaf #define SEARCH_HIGH_12 0xb0 #define SEARCH_EQUAL_12 0xb1 #define SEARCH_LOW_12 0xb2 #define READ_ELEMENT_STATUS 0xb8 #define SEND_VOLUME_TAG 0xb6 -#define WRITE_LONG_2 0xea - -/* from hw/scsi-generic.c */ -#define REWIND 0x01 -#define REPORT_DENSITY_SUPPORT 0x44 -#define GET_CONFIGURATION 0x46 -#define READ_16 0x88 -#define WRITE_16 0x8a -#define WRITE_VERIFY_16 0x8e -#define SERVICE_ACTION_IN 0x9e -#define REPORT_LUNS 0xa0 -#define LOAD_UNLOAD 0xa6 -#define SET_CD_SPEED 0xbb -#define BLANK 0xa1 +#define READ_DEFECT_DATA_12 0xb7 +#define SET_CD_SPEED 0xbb /* * SAM Status codes diff --git a/hw/scsi-disk.c b/hw/scsi-disk.c index abf0bd21ec..03f244e066 100644 --- a/hw/scsi-disk.c +++ b/hw/scsi-disk.c @@ -885,7 +885,7 @@ static int scsi_disk_emulate_command(SCSIDiskReq *r, uint8_t *outbuf) case ALLOW_MEDIUM_REMOVAL: bdrv_set_locked(s->bs, req->cmd.buf[4] & 1); break; - case READ_CAPACITY: + case READ_CAPACITY_10: /* The normal LEN field for this command is zero. */ memset(outbuf, 0, 8); bdrv_get_geometry(s->bs, &nb_sectors); @@ -970,7 +970,7 @@ static int scsi_disk_emulate_command(SCSIDiskReq *r, uint8_t *outbuf) outbuf[3] = 8; buflen = 16; break; - case VERIFY: + case VERIFY_10: break; default: scsi_command_complete(r, CHECK_CONDITION, SENSE_CODE(INVALID_OPCODE)); @@ -1046,13 +1046,13 @@ static int32_t scsi_send_command(SCSIRequest *req, uint8_t *buf) case RELEASE_10: case START_STOP: case ALLOW_MEDIUM_REMOVAL: - case READ_CAPACITY: + case READ_CAPACITY_10: case SYNCHRONIZE_CACHE: case READ_TOC: case GET_CONFIGURATION: case SERVICE_ACTION_IN: case REPORT_LUNS: - case VERIFY: + case VERIFY_10: rc = scsi_disk_emulate_command(r, outbuf); if (rc < 0) { return 0; @@ -1075,7 +1075,7 @@ static int32_t scsi_send_command(SCSIRequest *req, uint8_t *buf) case WRITE_10: case WRITE_12: case WRITE_16: - case WRITE_VERIFY: + case WRITE_VERIFY_10: case WRITE_VERIFY_12: case WRITE_VERIFY_16: len = r->req.cmd.xfer / s->qdev.blocksize; diff --git a/hw/scsi-generic.c b/hw/scsi-generic.c index 63361b3542..7b0026eb98 100644 --- a/hw/scsi-generic.c +++ b/hw/scsi-generic.c @@ -406,7 +406,7 @@ static int get_blocksize(BlockDriverState *bdrv) memset(cmd, 0, sizeof(cmd)); memset(buf, 0, sizeof(buf)); - cmd[0] = READ_CAPACITY; + cmd[0] = READ_CAPACITY_10; memset(&io_header, 0, sizeof(io_header)); io_header.interface_id = 'S'; From f37bd73b76e7f1e300e6acfe1bb6d3b2bc63714b Mon Sep 17 00:00:00 2001 From: Hannes Reinecke Date: Fri, 22 Jul 2011 16:44:46 +0200 Subject: [PATCH 160/230] scsi-disk: Remove 'drive_kind' Instead of using its own definitions scsi-disk should be using the device type of the parent device. Signed-off-by: Hannes Reinecke Signed-off-by: Kevin Wolf --- hw/scsi-defs.h | 6 +++++- hw/scsi-disk.c | 46 ++++++++++++++++++++++------------------------ 2 files changed, 27 insertions(+), 25 deletions(-) diff --git a/hw/scsi-defs.h b/hw/scsi-defs.h index f644860831..27010b74c0 100644 --- a/hw/scsi-defs.h +++ b/hw/scsi-defs.h @@ -164,6 +164,7 @@ #define TYPE_DISK 0x00 #define TYPE_TAPE 0x01 +#define TYPE_PRINTER 0x02 #define TYPE_PROCESSOR 0x03 /* HP scanners use this */ #define TYPE_WORM 0x04 /* Treated as ROM by our system */ #define TYPE_ROM 0x05 @@ -171,6 +172,9 @@ #define TYPE_MOD 0x07 /* Magneto-optical disk - * - treated as TYPE_DISK */ #define TYPE_MEDIUM_CHANGER 0x08 -#define TYPE_ENCLOSURE 0x0d /* Enclosure Services Device */ +#define TYPE_STORAGE_ARRAY 0x0c /* Storage array device */ +#define TYPE_ENCLOSURE 0x0d /* Enclosure Services Device */ +#define TYPE_RBC 0x0e /* Simplified Direct-Access Device */ +#define TYPE_OSD 0x11 /* Object-storage Device */ #define TYPE_NO_LUN 0x7f diff --git a/hw/scsi-disk.c b/hw/scsi-disk.c index 03f244e066..fa198f928c 100644 --- a/hw/scsi-disk.c +++ b/hw/scsi-disk.c @@ -59,8 +59,6 @@ typedef struct SCSIDiskReq { uint32_t status; } SCSIDiskReq; -typedef enum { SCSI_HD, SCSI_CD } SCSIDriveKind; - struct SCSIDiskState { SCSIDevice qdev; @@ -74,7 +72,6 @@ struct SCSIDiskState char *version; char *serial; SCSISense sense; - SCSIDriveKind drive_kind; }; static int scsi_handle_rw_error(SCSIDiskReq *r, int error, int type); @@ -382,7 +379,7 @@ static int scsi_disk_emulate_inquiry(SCSIRequest *req, uint8_t *outbuf) return -1; } - if (s->drive_kind == SCSI_CD) { + if (s->qdev.type == TYPE_ROM) { outbuf[buflen++] = 5; } else { outbuf[buflen++] = 0; @@ -401,7 +398,7 @@ static int scsi_disk_emulate_inquiry(SCSIRequest *req, uint8_t *outbuf) if (s->serial) outbuf[buflen++] = 0x80; // unit serial number outbuf[buflen++] = 0x83; // device identification - if (s->drive_kind == SCSI_HD) { + if (s->qdev.type == TYPE_DISK) { outbuf[buflen++] = 0xb0; // block limits outbuf[buflen++] = 0xb2; // thin provisioning } @@ -460,7 +457,7 @@ static int scsi_disk_emulate_inquiry(SCSIRequest *req, uint8_t *outbuf) unsigned int opt_io_size = s->qdev.conf.opt_io_size / s->qdev.blocksize; - if (s->drive_kind == SCSI_CD) { + if (s->qdev.type == TYPE_ROM) { DPRINTF("Inquiry (EVPD[%02X] not supported for CDROM\n", page_code); return -1; @@ -530,12 +527,11 @@ static int scsi_disk_emulate_inquiry(SCSIRequest *req, uint8_t *outbuf) return buflen; } - if (s->drive_kind == SCSI_CD) { - outbuf[0] = 5; + outbuf[0] = s->qdev.type & 0x1f; + if (s->qdev.type == TYPE_ROM) { outbuf[1] = 0x80; memcpy(&outbuf[16], "QEMU CD-ROM ", 16); } else { - outbuf[0] = 0; outbuf[1] = s->removable ? 0x80 : 0; memcpy(&outbuf[16], "QEMU HARDDISK ", 16); } @@ -661,7 +657,7 @@ static int mode_sense_page(SCSIRequest *req, int page, uint8_t *p, return p[1] + 2; case 0x2a: /* CD Capabilities and Mechanical Status page. */ - if (s->drive_kind != SCSI_CD) + if (s->qdev.type != TYPE_ROM) return 0; p[0] = 0x2a; p[1] = 0x14; @@ -877,7 +873,7 @@ static int scsi_disk_emulate_command(SCSIDiskReq *r, uint8_t *outbuf) goto illegal_request; break; case START_STOP: - if (s->drive_kind == SCSI_CD && (req->cmd.buf[4] & 2)) { + if (s->qdev.type == TYPE_ROM && (req->cmd.buf[4] & 2)) { /* load/eject medium */ bdrv_eject(s->bs, !(req->cmd.buf[4] & 1)); } @@ -1183,7 +1179,7 @@ static void scsi_destroy(SCSIDevice *dev) blockdev_mark_auto_del(s->qdev.conf.bs); } -static int scsi_initfn(SCSIDevice *dev, SCSIDriveKind kind) +static int scsi_initfn(SCSIDevice *dev, uint8_t scsi_type) { SCSIDiskState *s = DO_UPCAST(SCSIDiskState, qdev, dev); DriveInfo *dinfo; @@ -1193,9 +1189,8 @@ static int scsi_initfn(SCSIDevice *dev, SCSIDriveKind kind) return -1; } s->bs = s->qdev.conf.bs; - s->drive_kind = kind; - if (kind == SCSI_HD && !bdrv_is_inserted(s->bs)) { + if (scsi_type == TYPE_DISK && !bdrv_is_inserted(s->bs)) { error_report("Device needs media, but drive is empty"); return -1; } @@ -1217,44 +1212,47 @@ static int scsi_initfn(SCSIDevice *dev, SCSIDriveKind kind) return -1; } - if (kind == SCSI_CD) { + if (scsi_type == TYPE_ROM) { s->qdev.blocksize = 2048; - } else { + } else if (scsi_type == TYPE_DISK) { s->qdev.blocksize = s->qdev.conf.logical_block_size; + } else { + error_report("scsi-disk: Unhandled SCSI type %02x", scsi_type); + return -1; } s->cluster_size = s->qdev.blocksize / 512; s->bs->buffer_alignment = s->qdev.blocksize; - s->qdev.type = TYPE_DISK; + s->qdev.type = scsi_type; qemu_add_vm_change_state_handler(scsi_dma_restart_cb, s); - bdrv_set_removable(s->bs, kind == SCSI_CD); + bdrv_set_removable(s->bs, scsi_type == TYPE_ROM); add_boot_device_path(s->qdev.conf.bootindex, &dev->qdev, ",0"); return 0; } static int scsi_hd_initfn(SCSIDevice *dev) { - return scsi_initfn(dev, SCSI_HD); + return scsi_initfn(dev, TYPE_DISK); } static int scsi_cd_initfn(SCSIDevice *dev) { - return scsi_initfn(dev, SCSI_CD); + return scsi_initfn(dev, TYPE_ROM); } static int scsi_disk_initfn(SCSIDevice *dev) { - SCSIDriveKind kind; DriveInfo *dinfo; + uint8_t scsi_type; if (!dev->conf.bs) { - kind = SCSI_HD; /* will die in scsi_initfn() */ + scsi_type = TYPE_DISK; /* will die in scsi_initfn() */ } else { dinfo = drive_get_by_blockdev(dev->conf.bs); - kind = dinfo->media_cd ? SCSI_CD : SCSI_HD; + scsi_type = dinfo->media_cd ? TYPE_ROM : TYPE_DISK; } - return scsi_initfn(dev, kind); + return scsi_initfn(dev, scsi_type); } #define DEFINE_SCSI_DISK_PROPERTIES() \ From 5bf3f8e4f71e46f80b76dc8a03cc0c37cad5cde0 Mon Sep 17 00:00:00 2001 From: Frediano Ziglio Date: Wed, 27 Jul 2011 20:12:00 +0200 Subject: [PATCH 161/230] block: Removed unused function bdrv_write_sync Signed-off-by: Frediano Ziglio Signed-off-by: Kevin Wolf --- block.c | 13 ------------- block.h | 2 -- 2 files changed, 15 deletions(-) diff --git a/block.c b/block.c index 8859f9b414..4c66b2cf40 100644 --- a/block.c +++ b/block.c @@ -1110,19 +1110,6 @@ int bdrv_pwrite_sync(BlockDriverState *bs, int64_t offset, return 0; } -/* - * Writes to the file and ensures that no writes are reordered across this - * request (acts as a barrier) - * - * Returns 0 on success, -errno in error cases. - */ -int bdrv_write_sync(BlockDriverState *bs, int64_t sector_num, - const uint8_t *buf, int nb_sectors) -{ - return bdrv_pwrite_sync(bs, BDRV_SECTOR_SIZE * sector_num, - buf, BDRV_SECTOR_SIZE * nb_sectors); -} - /** * Truncate file to 'offset' bytes (needed only for file protocols) */ diff --git a/block.h b/block.h index 59cc410e3b..e672bc669a 100644 --- a/block.h +++ b/block.h @@ -85,8 +85,6 @@ int bdrv_pwrite(BlockDriverState *bs, int64_t offset, const void *buf, int count); int bdrv_pwrite_sync(BlockDriverState *bs, int64_t offset, const void *buf, int count); -int bdrv_write_sync(BlockDriverState *bs, int64_t sector_num, - const uint8_t *buf, int nb_sectors); int bdrv_truncate(BlockDriverState *bs, int64_t offset); int64_t bdrv_getlength(BlockDriverState *bs); int64_t bdrv_get_allocated_file_size(BlockDriverState *bs); From c1ee7d56f3f2c8c5fb90452f4df72fa402f61527 Mon Sep 17 00:00:00 2001 From: Frediano Ziglio Date: Wed, 27 Jul 2011 20:12:01 +0200 Subject: [PATCH 162/230] raw-posix: Typo fix Signed-off-by: Frediano Ziglio Signed-off-by: Kevin Wolf --- block/raw-posix.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/block/raw-posix.c b/block/raw-posix.c index 6672d31da3..6dd708688b 100644 --- a/block/raw-posix.c +++ b/block/raw-posix.c @@ -587,7 +587,7 @@ static BlockDriverAIOCB *raw_aio_submit(BlockDriverState *bs, /* * If O_DIRECT is used the buffer needs to be aligned on a sector - * boundary. Check if this is the case or telll the low-level + * boundary. Check if this is the case or tell the low-level * driver that it needs to copy the buffer. */ if (s->aligned_buf) { From f6e8ffc22fe153ba981f2747e4c52ea7e55f6ecc Mon Sep 17 00:00:00 2001 From: Frediano Ziglio Date: Wed, 27 Jul 2011 20:12:02 +0200 Subject: [PATCH 163/230] raw-posix: Always check paio_init result Signed-off-by: Frediano Ziglio Signed-off-by: Kevin Wolf --- block/raw-posix.c | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/block/raw-posix.c b/block/raw-posix.c index 6dd708688b..c5c99446c0 100644 --- a/block/raw-posix.c +++ b/block/raw-posix.c @@ -230,13 +230,15 @@ static int raw_open_common(BlockDriverState *bs, const char *filename, } } + /* We're falling back to POSIX AIO in some cases so init always */ + if (paio_init() < 0) { + goto out_free_buf; + } + #ifdef CONFIG_LINUX_AIO if ((bdrv_flags & (BDRV_O_NOCACHE|BDRV_O_NATIVE_AIO)) == (BDRV_O_NOCACHE|BDRV_O_NATIVE_AIO)) { - /* We're falling back to POSIX AIO in some cases */ - paio_init(); - s->aio_ctx = laio_init(); if (!s->aio_ctx) { goto out_free_buf; @@ -245,9 +247,6 @@ static int raw_open_common(BlockDriverState *bs, const char *filename, } else #endif { - if (paio_init() < 0) { - goto out_free_buf; - } #ifdef CONFIG_LINUX_AIO s->use_aio = 0; #endif From 00dccaf1f848290d979a4b1e6248281ce1b32aaa Mon Sep 17 00:00:00 2001 From: Kevin Wolf Date: Mon, 17 Jan 2011 16:08:14 +0000 Subject: [PATCH 164/230] coroutine: introduce coroutines Asynchronous code is becoming very complex. At the same time synchronous code is growing because it is convenient to write. Sometimes duplicate code paths are even added, one synchronous and the other asynchronous. This patch introduces coroutines which allow code that looks synchronous but is asynchronous under the covers. A coroutine has its own stack and is therefore able to preserve state across blocking operations, which traditionally require callback functions and manual marshalling of parameters. Creating and starting a coroutine is easy: coroutine = qemu_coroutine_create(my_coroutine); qemu_coroutine_enter(coroutine, my_data); The coroutine then executes until it returns or yields: void coroutine_fn my_coroutine(void *opaque) { MyData *my_data = opaque; /* do some work */ qemu_coroutine_yield(); /* do some more work */ } Yielding switches control back to the caller of qemu_coroutine_enter(). This is typically used to switch back to the main thread's event loop after issuing an asynchronous I/O request. The request callback will then invoke qemu_coroutine_enter() once more to switch back to the coroutine. Note that if coroutines are used only from threads which hold the global mutex they will never execute concurrently. This makes programming with coroutines easier than with threads. Race conditions cannot occur since only one coroutine may be active at any time. Other coroutines can only run across yield. This coroutines implementation is based on the gtk-vnc implementation written by Anthony Liguori but it has been significantly rewritten by Kevin Wolf to use setjmp()/longjmp() instead of the more expensive swapcontext() and by Paolo Bonzini for Windows Fibers support. Signed-off-by: Kevin Wolf Signed-off-by: Stefan Hajnoczi --- Makefile.objs | 7 ++ coroutine-ucontext.c | 230 +++++++++++++++++++++++++++++++++++++++++++ coroutine-win32.c | 92 +++++++++++++++++ qemu-coroutine-int.h | 48 +++++++++ qemu-coroutine.c | 75 ++++++++++++++ qemu-coroutine.h | 95 ++++++++++++++++++ trace-events | 5 + 7 files changed, 552 insertions(+) create mode 100644 coroutine-ucontext.c create mode 100644 coroutine-win32.c create mode 100644 qemu-coroutine-int.h create mode 100644 qemu-coroutine.c create mode 100644 qemu-coroutine.h diff --git a/Makefile.objs b/Makefile.objs index 6991a9f52a..28e1762463 100644 --- a/Makefile.objs +++ b/Makefile.objs @@ -10,6 +10,12 @@ oslib-obj-y = osdep.o oslib-obj-$(CONFIG_WIN32) += oslib-win32.o qemu-thread-win32.o oslib-obj-$(CONFIG_POSIX) += oslib-posix.o qemu-thread-posix.o +####################################################################### +# coroutines +coroutine-obj-y = qemu-coroutine.o +coroutine-obj-$(CONFIG_POSIX) += coroutine-ucontext.o +coroutine-obj-$(CONFIG_WIN32) += coroutine-win32.o + ####################################################################### # block-obj-y is code used by both qemu system emulation and qemu-img @@ -69,6 +75,7 @@ common-obj-y += readline.o console.o cursor.o qemu-error.o common-obj-y += $(oslib-obj-y) common-obj-$(CONFIG_WIN32) += os-win32.o common-obj-$(CONFIG_POSIX) += os-posix.o +common-obj-y += $(coroutine-obj-y) common-obj-y += tcg-runtime.o host-utils.o common-obj-y += irq.o ioport.o input.o diff --git a/coroutine-ucontext.c b/coroutine-ucontext.c new file mode 100644 index 0000000000..41c2379a2a --- /dev/null +++ b/coroutine-ucontext.c @@ -0,0 +1,230 @@ +/* + * ucontext coroutine initialization code + * + * Copyright (C) 2006 Anthony Liguori + * Copyright (C) 2011 Kevin Wolf + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.0 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, see . + */ + +/* XXX Is there a nicer way to disable glibc's stack check for longjmp? */ +#ifdef _FORTIFY_SOURCE +#undef _FORTIFY_SOURCE +#endif +#include +#include +#include +#include +#include +#include "qemu-common.h" +#include "qemu-coroutine-int.h" + +enum { + /* Maximum free pool size prevents holding too many freed coroutines */ + POOL_MAX_SIZE = 64, +}; + +typedef struct { + Coroutine base; + void *stack; + jmp_buf env; +} CoroutineUContext; + +/** + * Per-thread coroutine bookkeeping + */ +typedef struct { + /** Currently executing coroutine */ + Coroutine *current; + + /** Free list to speed up creation */ + QLIST_HEAD(, Coroutine) pool; + unsigned int pool_size; + + /** The default coroutine */ + CoroutineUContext leader; +} CoroutineThreadState; + +static pthread_key_t thread_state_key; + +/* + * va_args to makecontext() must be type 'int', so passing + * the pointer we need may require several int args. This + * union is a quick hack to let us do that + */ +union cc_arg { + void *p; + int i[2]; +}; + +static CoroutineThreadState *coroutine_get_thread_state(void) +{ + CoroutineThreadState *s = pthread_getspecific(thread_state_key); + + if (!s) { + s = qemu_mallocz(sizeof(*s)); + s->current = &s->leader.base; + QLIST_INIT(&s->pool); + pthread_setspecific(thread_state_key, s); + } + return s; +} + +static void qemu_coroutine_thread_cleanup(void *opaque) +{ + CoroutineThreadState *s = opaque; + Coroutine *co; + Coroutine *tmp; + + QLIST_FOREACH_SAFE(co, &s->pool, pool_next, tmp) { + qemu_free(DO_UPCAST(CoroutineUContext, base, co)->stack); + qemu_free(co); + } + qemu_free(s); +} + +static void __attribute__((constructor)) coroutine_init(void) +{ + int ret; + + ret = pthread_key_create(&thread_state_key, qemu_coroutine_thread_cleanup); + if (ret != 0) { + fprintf(stderr, "unable to create leader key: %s\n", strerror(errno)); + abort(); + } +} + +static void coroutine_trampoline(int i0, int i1) +{ + union cc_arg arg; + CoroutineUContext *self; + Coroutine *co; + + arg.i[0] = i0; + arg.i[1] = i1; + self = arg.p; + co = &self->base; + + /* Initialize longjmp environment and switch back the caller */ + if (!setjmp(self->env)) { + longjmp(*(jmp_buf *)co->entry_arg, 1); + } + + while (true) { + co->entry(co->entry_arg); + qemu_coroutine_switch(co, co->caller, COROUTINE_TERMINATE); + } +} + +static Coroutine *coroutine_new(void) +{ + const size_t stack_size = 1 << 20; + CoroutineUContext *co; + ucontext_t old_uc, uc; + jmp_buf old_env; + union cc_arg arg; + + /* The ucontext functions preserve signal masks which incurs a system call + * overhead. setjmp()/longjmp() does not preserve signal masks but only + * works on the current stack. Since we need a way to create and switch to + * a new stack, use the ucontext functions for that but setjmp()/longjmp() + * for everything else. + */ + + if (getcontext(&uc) == -1) { + abort(); + } + + co = qemu_mallocz(sizeof(*co)); + co->stack = qemu_malloc(stack_size); + co->base.entry_arg = &old_env; /* stash away our jmp_buf */ + + uc.uc_link = &old_uc; + uc.uc_stack.ss_sp = co->stack; + uc.uc_stack.ss_size = stack_size; + uc.uc_stack.ss_flags = 0; + + arg.p = co; + + makecontext(&uc, (void (*)(void))coroutine_trampoline, + 2, arg.i[0], arg.i[1]); + + /* swapcontext() in, longjmp() back out */ + if (!setjmp(old_env)) { + swapcontext(&old_uc, &uc); + } + return &co->base; +} + +Coroutine *qemu_coroutine_new(void) +{ + CoroutineThreadState *s = coroutine_get_thread_state(); + Coroutine *co; + + co = QLIST_FIRST(&s->pool); + if (co) { + QLIST_REMOVE(co, pool_next); + s->pool_size--; + } else { + co = coroutine_new(); + } + return co; +} + +void qemu_coroutine_delete(Coroutine *co_) +{ + CoroutineThreadState *s = coroutine_get_thread_state(); + CoroutineUContext *co = DO_UPCAST(CoroutineUContext, base, co_); + + if (s->pool_size < POOL_MAX_SIZE) { + QLIST_INSERT_HEAD(&s->pool, &co->base, pool_next); + co->base.caller = NULL; + s->pool_size++; + return; + } + + qemu_free(co->stack); + qemu_free(co); +} + +CoroutineAction qemu_coroutine_switch(Coroutine *from_, Coroutine *to_, + CoroutineAction action) +{ + CoroutineUContext *from = DO_UPCAST(CoroutineUContext, base, from_); + CoroutineUContext *to = DO_UPCAST(CoroutineUContext, base, to_); + CoroutineThreadState *s = coroutine_get_thread_state(); + int ret; + + s->current = to_; + + ret = setjmp(from->env); + if (ret == 0) { + longjmp(to->env, action); + } + return ret; +} + +Coroutine *qemu_coroutine_self(void) +{ + CoroutineThreadState *s = coroutine_get_thread_state(); + + return s->current; +} + +bool qemu_in_coroutine(void) +{ + CoroutineThreadState *s = pthread_getspecific(thread_state_key); + + return s && s->current->caller; +} diff --git a/coroutine-win32.c b/coroutine-win32.c new file mode 100644 index 0000000000..0e29448473 --- /dev/null +++ b/coroutine-win32.c @@ -0,0 +1,92 @@ +/* + * Win32 coroutine initialization code + * + * Copyright (c) 2011 Kevin Wolf + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +#include "qemu-common.h" +#include "qemu-coroutine-int.h" + +typedef struct +{ + Coroutine base; + + LPVOID fiber; + CoroutineAction action; +} CoroutineWin32; + +static __thread CoroutineWin32 leader; +static __thread Coroutine *current; + +CoroutineAction qemu_coroutine_switch(Coroutine *from_, Coroutine *to_, + CoroutineAction action) +{ + CoroutineWin32 *from = DO_UPCAST(CoroutineWin32, base, from_); + CoroutineWin32 *to = DO_UPCAST(CoroutineWin32, base, to_); + + current = to_; + + to->action = action; + SwitchToFiber(to->fiber); + return from->action; +} + +static void CALLBACK coroutine_trampoline(void *co_) +{ + Coroutine *co = co_; + + while (true) { + co->entry(co->entry_arg); + qemu_coroutine_switch(co, co->caller, COROUTINE_TERMINATE); + } +} + +Coroutine *qemu_coroutine_new(void) +{ + const size_t stack_size = 1 << 20; + CoroutineWin32 *co; + + co = qemu_mallocz(sizeof(*co)); + co->fiber = CreateFiber(stack_size, coroutine_trampoline, &co->base); + return &co->base; +} + +void qemu_coroutine_delete(Coroutine *co_) +{ + CoroutineWin32 *co = DO_UPCAST(CoroutineWin32, base, co_); + + DeleteFiber(co->fiber); + qemu_free(co); +} + +Coroutine *qemu_coroutine_self(void) +{ + if (!current) { + current = &leader.base; + leader.fiber = ConvertThreadToFiber(NULL); + } + return current; +} + +bool qemu_in_coroutine(void) +{ + return current && current->caller; +} diff --git a/qemu-coroutine-int.h b/qemu-coroutine-int.h new file mode 100644 index 0000000000..64915c2fa5 --- /dev/null +++ b/qemu-coroutine-int.h @@ -0,0 +1,48 @@ +/* + * Coroutine internals + * + * Copyright (c) 2011 Kevin Wolf + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +#ifndef QEMU_COROUTINE_INT_H +#define QEMU_COROUTINE_INT_H + +#include "qemu-queue.h" +#include "qemu-coroutine.h" + +typedef enum { + COROUTINE_YIELD = 1, + COROUTINE_TERMINATE = 2, +} CoroutineAction; + +struct Coroutine { + CoroutineEntry *entry; + void *entry_arg; + Coroutine *caller; + QLIST_ENTRY(Coroutine) pool_next; +}; + +Coroutine *qemu_coroutine_new(void); +void qemu_coroutine_delete(Coroutine *co); +CoroutineAction qemu_coroutine_switch(Coroutine *from, Coroutine *to, + CoroutineAction action); + +#endif diff --git a/qemu-coroutine.c b/qemu-coroutine.c new file mode 100644 index 0000000000..600be2643c --- /dev/null +++ b/qemu-coroutine.c @@ -0,0 +1,75 @@ +/* + * QEMU coroutines + * + * Copyright IBM, Corp. 2011 + * + * Authors: + * Stefan Hajnoczi + * Kevin Wolf + * + * This work is licensed under the terms of the GNU LGPL, version 2 or later. + * See the COPYING.LIB file in the top-level directory. + * + */ + +#include "trace.h" +#include "qemu-common.h" +#include "qemu-coroutine.h" +#include "qemu-coroutine-int.h" + +Coroutine *qemu_coroutine_create(CoroutineEntry *entry) +{ + Coroutine *co = qemu_coroutine_new(); + co->entry = entry; + return co; +} + +static void coroutine_swap(Coroutine *from, Coroutine *to) +{ + CoroutineAction ret; + + ret = qemu_coroutine_switch(from, to, COROUTINE_YIELD); + + switch (ret) { + case COROUTINE_YIELD: + return; + case COROUTINE_TERMINATE: + trace_qemu_coroutine_terminate(to); + qemu_coroutine_delete(to); + return; + default: + abort(); + } +} + +void qemu_coroutine_enter(Coroutine *co, void *opaque) +{ + Coroutine *self = qemu_coroutine_self(); + + trace_qemu_coroutine_enter(self, co, opaque); + + if (co->caller) { + fprintf(stderr, "Co-routine re-entered recursively\n"); + abort(); + } + + co->caller = self; + co->entry_arg = opaque; + coroutine_swap(self, co); +} + +void coroutine_fn qemu_coroutine_yield(void) +{ + Coroutine *self = qemu_coroutine_self(); + Coroutine *to = self->caller; + + trace_qemu_coroutine_yield(self, to); + + if (!to) { + fprintf(stderr, "Co-routine is yielding to no one\n"); + abort(); + } + + self->caller = NULL; + coroutine_swap(self, to); +} diff --git a/qemu-coroutine.h b/qemu-coroutine.h new file mode 100644 index 0000000000..08255c7c41 --- /dev/null +++ b/qemu-coroutine.h @@ -0,0 +1,95 @@ +/* + * QEMU coroutine implementation + * + * Copyright IBM, Corp. 2011 + * + * Authors: + * Stefan Hajnoczi + * + * This work is licensed under the terms of the GNU LGPL, version 2 or later. + * See the COPYING.LIB file in the top-level directory. + * + */ + +#ifndef QEMU_COROUTINE_H +#define QEMU_COROUTINE_H + +#include + +/** + * Coroutines are a mechanism for stack switching and can be used for + * cooperative userspace threading. These functions provide a simple but + * useful flavor of coroutines that is suitable for writing sequential code, + * rather than callbacks, for operations that need to give up control while + * waiting for events to complete. + * + * These functions are re-entrant and may be used outside the global mutex. + */ + +/** + * Mark a function that executes in coroutine context + * + * Functions that execute in coroutine context cannot be called directly from + * normal functions. In the future it would be nice to enable compiler or + * static checker support for catching such errors. This annotation might make + * it possible and in the meantime it serves as documentation. + * + * For example: + * + * static void coroutine_fn foo(void) { + * .... + * } + */ +#define coroutine_fn + +typedef struct Coroutine Coroutine; + +/** + * Coroutine entry point + * + * When the coroutine is entered for the first time, opaque is passed in as an + * argument. + * + * When this function returns, the coroutine is destroyed automatically and + * execution continues in the caller who last entered the coroutine. + */ +typedef void coroutine_fn CoroutineEntry(void *opaque); + +/** + * Create a new coroutine + * + * Use qemu_coroutine_enter() to actually transfer control to the coroutine. + */ +Coroutine *qemu_coroutine_create(CoroutineEntry *entry); + +/** + * Transfer control to a coroutine + * + * The opaque argument is passed as the argument to the entry point when + * entering the coroutine for the first time. It is subsequently ignored. + */ +void qemu_coroutine_enter(Coroutine *coroutine, void *opaque); + +/** + * Transfer control back to a coroutine's caller + * + * This function does not return until the coroutine is re-entered using + * qemu_coroutine_enter(). + */ +void coroutine_fn qemu_coroutine_yield(void); + +/** + * Get the currently executing coroutine + */ +Coroutine *coroutine_fn qemu_coroutine_self(void); + +/** + * Return whether or not currently inside a coroutine + * + * This can be used to write functions that work both when in coroutine context + * and when not in coroutine context. Note that such functions cannot use the + * coroutine_fn annotation since they work outside coroutine context. + */ +bool qemu_in_coroutine(void); + +#endif /* QEMU_COROUTINE_H */ diff --git a/trace-events b/trace-events index 713f042081..136f7759f2 100644 --- a/trace-events +++ b/trace-events @@ -425,3 +425,8 @@ disable qemu_put_ram_ptr(void* addr) "%p" # hw/xen_platform.c disable xen_platform_log(char *s) "xen platform: %s" + +# qemu-coroutine.c +disable qemu_coroutine_enter(void *from, void *to, void *opaque) "from %p to %p opaque %p" +disable qemu_coroutine_yield(void *from, void *to) "from %p to %p" +disable qemu_coroutine_terminate(void *co) "self %p" From d0e2fce5366bff40cf2bf48d2f6ff72b4d8de124 Mon Sep 17 00:00:00 2001 From: "Aneesh Kumar K.V" Date: Thu, 9 Jun 2011 23:11:06 +0530 Subject: [PATCH 165/230] coroutine: implement coroutines using gthread MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit On platforms that don't support makecontext(3) use gthread based coroutine implementation. Darwin has makecontext(3) but getcontext(3) is stubbed out to return ENOTSUP. Andreas Färber debugged this and contributed the ./configure test which solves the issue for Darwin/ppc64 (and ppc) v10.5. [Original patch by Aneesh, made consistent with coroutine-ucontext.c and switched to GStaticPrivate by Stefan. Tested on Linux and OpenBSD.] Signed-off-by: Aneesh Kumar K.V Signed-off-by: Stefan Hajnoczi --- Makefile.objs | 4 ++ configure | 18 ++++++ coroutine-gthread.c | 131 ++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 153 insertions(+) create mode 100644 coroutine-gthread.c diff --git a/Makefile.objs b/Makefile.objs index 28e1762463..5679e1fa06 100644 --- a/Makefile.objs +++ b/Makefile.objs @@ -13,7 +13,11 @@ oslib-obj-$(CONFIG_POSIX) += oslib-posix.o qemu-thread-posix.o ####################################################################### # coroutines coroutine-obj-y = qemu-coroutine.o +ifeq ($(CONFIG_UCONTEXT_COROUTINE),y) coroutine-obj-$(CONFIG_POSIX) += coroutine-ucontext.o +else +coroutine-obj-$(CONFIG_POSIX) += coroutine-gthread.o +endif coroutine-obj-$(CONFIG_WIN32) += coroutine-win32.o ####################################################################### diff --git a/configure b/configure index 77194cf9a7..1eed0cd585 100755 --- a/configure +++ b/configure @@ -2540,6 +2540,20 @@ EOF fi fi +########################################## +# check if we have makecontext + +ucontext_coroutine=no +if test "$darwin" != "yes"; then + cat > $TMPC << EOF +#include +int main(void) { makecontext(0, 0, 0); } +EOF + if compile_prog "" "" ; then + ucontext_coroutine=yes + fi +fi + ########################################## # End of CC checks # After here, no more $cc or $ld runs @@ -3015,6 +3029,10 @@ if test "$rbd" = "yes" ; then echo "CONFIG_RBD=y" >> $config_host_mak fi +if test "$ucontext_coroutine" = "yes" ; then + echo "CONFIG_UCONTEXT_COROUTINE=y" >> $config_host_mak +fi + # USB host support case "$usb" in linux) diff --git a/coroutine-gthread.c b/coroutine-gthread.c new file mode 100644 index 0000000000..f09877e14f --- /dev/null +++ b/coroutine-gthread.c @@ -0,0 +1,131 @@ +/* + * GThread coroutine initialization code + * + * Copyright (C) 2006 Anthony Liguori + * Copyright (C) 2011 Aneesh Kumar K.V + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.0 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, see . + */ + +#include +#include "qemu-common.h" +#include "qemu-coroutine-int.h" + +typedef struct { + Coroutine base; + GThread *thread; + bool runnable; + CoroutineAction action; +} CoroutineGThread; + +static GCond *coroutine_cond; +static GStaticMutex coroutine_lock = G_STATIC_MUTEX_INIT; +static GStaticPrivate coroutine_key = G_STATIC_PRIVATE_INIT; + +static void __attribute__((constructor)) coroutine_init(void) +{ + if (!g_thread_supported()) { + g_thread_init(NULL); + } + + coroutine_cond = g_cond_new(); +} + +static void coroutine_wait_runnable_locked(CoroutineGThread *co) +{ + while (!co->runnable) { + g_cond_wait(coroutine_cond, g_static_mutex_get_mutex(&coroutine_lock)); + } +} + +static void coroutine_wait_runnable(CoroutineGThread *co) +{ + g_static_mutex_lock(&coroutine_lock); + coroutine_wait_runnable_locked(co); + g_static_mutex_unlock(&coroutine_lock); +} + +static gpointer coroutine_thread(gpointer opaque) +{ + CoroutineGThread *co = opaque; + + g_static_private_set(&coroutine_key, co, NULL); + coroutine_wait_runnable(co); + co->base.entry(co->base.entry_arg); + qemu_coroutine_switch(&co->base, co->base.caller, COROUTINE_TERMINATE); + return NULL; +} + +Coroutine *qemu_coroutine_new(void) +{ + CoroutineGThread *co; + + co = qemu_mallocz(sizeof(*co)); + co->thread = g_thread_create_full(coroutine_thread, co, 0, TRUE, TRUE, + G_THREAD_PRIORITY_NORMAL, NULL); + if (!co->thread) { + qemu_free(co); + return NULL; + } + return &co->base; +} + +void qemu_coroutine_delete(Coroutine *co_) +{ + CoroutineGThread *co = DO_UPCAST(CoroutineGThread, base, co_); + + g_thread_join(co->thread); + qemu_free(co); +} + +CoroutineAction qemu_coroutine_switch(Coroutine *from_, + Coroutine *to_, + CoroutineAction action) +{ + CoroutineGThread *from = DO_UPCAST(CoroutineGThread, base, from_); + CoroutineGThread *to = DO_UPCAST(CoroutineGThread, base, to_); + + g_static_mutex_lock(&coroutine_lock); + from->runnable = false; + from->action = action; + to->runnable = true; + to->action = action; + g_cond_broadcast(coroutine_cond); + + if (action != COROUTINE_TERMINATE) { + coroutine_wait_runnable_locked(from); + } + g_static_mutex_unlock(&coroutine_lock); + return from->action; +} + +Coroutine *qemu_coroutine_self(void) +{ + CoroutineGThread *co = g_static_private_get(&coroutine_key); + + if (!co) { + co = qemu_mallocz(sizeof(*co)); + co->runnable = true; + g_static_private_set(&coroutine_key, co, (GDestroyNotify)qemu_free); + } + + return &co->base; +} + +bool qemu_in_coroutine(void) +{ + CoroutineGThread *co = g_static_private_get(&coroutine_key); + + return co && co->base.caller; +} From 5c59d118161cccf6952b013f71387515f3c91e0a Mon Sep 17 00:00:00 2001 From: Gerd Hoffmann Date: Wed, 20 Jul 2011 12:20:50 +0300 Subject: [PATCH 166/230] spice: add worker wrapper functions. Add wrapper functions for all spice worker calls. Signed-off-by: Gerd Hoffmann --- hw/qxl-render.c | 4 +- hw/qxl.c | 32 ++++++++-------- ui/spice-display.c | 95 +++++++++++++++++++++++++++++++++++++++++++--- ui/spice-display.h | 22 +++++++++++ 4 files changed, 129 insertions(+), 24 deletions(-) diff --git a/hw/qxl-render.c b/hw/qxl-render.c index 1316066599..bef5f148e4 100644 --- a/hw/qxl-render.c +++ b/hw/qxl-render.c @@ -124,8 +124,8 @@ void qxl_render_update(PCIQXLDevice *qxl) update.bottom = qxl->guest_primary.surface.height; memset(dirty, 0, sizeof(dirty)); - qxl->ssd.worker->update_area(qxl->ssd.worker, 0, &update, - dirty, ARRAY_SIZE(dirty), 1); + qemu_spice_update_area(&qxl->ssd, 0, &update, + dirty, ARRAY_SIZE(dirty), 1); for (i = 0; i < ARRAY_SIZE(dirty); i++) { if (qemu_spice_rect_is_empty(dirty+i)) { diff --git a/hw/qxl.c b/hw/qxl.c index a6fb7f0acb..5deb776580 100644 --- a/hw/qxl.c +++ b/hw/qxl.c @@ -684,8 +684,8 @@ static void qxl_hard_reset(PCIQXLDevice *d, int loadvm) dprint(d, 1, "%s: start%s\n", __FUNCTION__, loadvm ? " (loadvm)" : ""); - d->ssd.worker->reset_cursor(d->ssd.worker); - d->ssd.worker->reset_image_cache(d->ssd.worker); + qemu_spice_reset_cursor(&d->ssd); + qemu_spice_reset_image_cache(&d->ssd); qxl_reset_surfaces(d); qxl_reset_memslots(d); @@ -790,7 +790,7 @@ static void qxl_add_memslot(PCIQXLDevice *d, uint32_t slot_id, uint64_t delta) __FUNCTION__, memslot.slot_id, memslot.virt_start, memslot.virt_end); - d->ssd.worker->add_memslot(d->ssd.worker, &memslot); + qemu_spice_add_memslot(&d->ssd, &memslot); d->guest_slots[slot_id].ptr = (void*)memslot.virt_start; d->guest_slots[slot_id].size = memslot.virt_end - memslot.virt_start; d->guest_slots[slot_id].delta = delta; @@ -800,14 +800,14 @@ static void qxl_add_memslot(PCIQXLDevice *d, uint32_t slot_id, uint64_t delta) static void qxl_del_memslot(PCIQXLDevice *d, uint32_t slot_id) { dprint(d, 1, "%s: slot %d\n", __FUNCTION__, slot_id); - d->ssd.worker->del_memslot(d->ssd.worker, MEMSLOT_GROUP_HOST, slot_id); + qemu_spice_del_memslot(&d->ssd, MEMSLOT_GROUP_HOST, slot_id); d->guest_slots[slot_id].active = 0; } static void qxl_reset_memslots(PCIQXLDevice *d) { dprint(d, 1, "%s:\n", __FUNCTION__); - d->ssd.worker->reset_memslots(d->ssd.worker); + qemu_spice_reset_memslots(&d->ssd); memset(&d->guest_slots, 0, sizeof(d->guest_slots)); } @@ -815,7 +815,7 @@ static void qxl_reset_surfaces(PCIQXLDevice *d) { dprint(d, 1, "%s:\n", __FUNCTION__); d->mode = QXL_MODE_UNDEFINED; - d->ssd.worker->destroy_surfaces(d->ssd.worker); + qemu_spice_destroy_surfaces(&d->ssd); memset(&d->guest_surfaces.cmds, 0, sizeof(d->guest_surfaces.cmds)); } @@ -869,7 +869,7 @@ static void qxl_create_guest_primary(PCIQXLDevice *qxl, int loadvm) qxl->mode = QXL_MODE_NATIVE; qxl->cmdflags = 0; - qxl->ssd.worker->create_primary_surface(qxl->ssd.worker, 0, &surface); + qemu_spice_create_primary_surface(&qxl->ssd, 0, &surface); /* for local rendering */ qxl_render_resize(qxl); @@ -884,7 +884,7 @@ static void qxl_destroy_primary(PCIQXLDevice *d) dprint(d, 1, "%s\n", __FUNCTION__); d->mode = QXL_MODE_UNDEFINED; - d->ssd.worker->destroy_primary_surface(d->ssd.worker, 0); + qemu_spice_destroy_primary_surface(&d->ssd, 0); } static void qxl_set_mode(PCIQXLDevice *d, int modenr, int loadvm) @@ -956,15 +956,15 @@ static void ioport_write(void *opaque, uint32_t addr, uint32_t val) case QXL_IO_UPDATE_AREA: { QXLRect update = d->ram->update_area; - d->ssd.worker->update_area(d->ssd.worker, d->ram->update_surface, - &update, NULL, 0, 0); + qemu_spice_update_area(&d->ssd, d->ram->update_surface, + &update, NULL, 0, 0); break; } case QXL_IO_NOTIFY_CMD: - d->ssd.worker->wakeup(d->ssd.worker); + qemu_spice_wakeup(&d->ssd); break; case QXL_IO_NOTIFY_CURSOR: - d->ssd.worker->wakeup(d->ssd.worker); + qemu_spice_wakeup(&d->ssd); break; case QXL_IO_UPDATE_IRQ: qxl_set_irq(d); @@ -978,7 +978,7 @@ static void ioport_write(void *opaque, uint32_t addr, uint32_t val) break; } d->oom_running = 1; - d->ssd.worker->oom(d->ssd.worker); + qemu_spice_oom(&d->ssd); d->oom_running = 0; break; case QXL_IO_SET_MODE: @@ -1016,10 +1016,10 @@ static void ioport_write(void *opaque, uint32_t addr, uint32_t val) qxl_destroy_primary(d); break; case QXL_IO_DESTROY_SURFACE_WAIT: - d->ssd.worker->destroy_surface_wait(d->ssd.worker, val); + qemu_spice_destroy_surface_wait(&d->ssd, val); break; case QXL_IO_DESTROY_ALL_SURFACES: - d->ssd.worker->destroy_surfaces(d->ssd.worker); + qemu_spice_destroy_surfaces(&d->ssd); break; default: fprintf(stderr, "%s: ioport=0x%x, abort()\n", __FUNCTION__, io_port); @@ -1424,7 +1424,7 @@ static int qxl_post_load(void *opaque, int version) cmds[out].cmd.type = QXL_CMD_CURSOR; cmds[out].group_id = MEMSLOT_GROUP_GUEST; out++; - d->ssd.worker->loadvm_commands(d->ssd.worker, cmds, out); + qemu_spice_loadvm_commands(&d->ssd, cmds, out); qemu_free(cmds); break; diff --git a/ui/spice-display.c b/ui/spice-display.c index feeee73dcc..1e6a38f86c 100644 --- a/ui/spice-display.c +++ b/ui/spice-display.c @@ -62,6 +62,89 @@ void qemu_spice_rect_union(QXLRect *dest, const QXLRect *r) dest->right = MAX(dest->right, r->right); } + +void qemu_spice_update_area(SimpleSpiceDisplay *ssd, uint32_t surface_id, + struct QXLRect *area, struct QXLRect *dirty_rects, + uint32_t num_dirty_rects, + uint32_t clear_dirty_region) +{ + ssd->worker->update_area(ssd->worker, surface_id, area, dirty_rects, + num_dirty_rects, clear_dirty_region); +} + +void qemu_spice_add_memslot(SimpleSpiceDisplay *ssd, QXLDevMemSlot *memslot) +{ + ssd->worker->add_memslot(ssd->worker, memslot); +} + +void qemu_spice_del_memslot(SimpleSpiceDisplay *ssd, uint32_t gid, uint32_t sid) +{ + ssd->worker->del_memslot(ssd->worker, gid, sid); +} + +void qemu_spice_create_primary_surface(SimpleSpiceDisplay *ssd, uint32_t id, + QXLDevSurfaceCreate *surface) +{ + ssd->worker->create_primary_surface(ssd->worker, id, surface); +} + +void qemu_spice_destroy_primary_surface(SimpleSpiceDisplay *ssd, uint32_t id) +{ + ssd->worker->destroy_primary_surface(ssd->worker, id); +} + +void qemu_spice_destroy_surface_wait(SimpleSpiceDisplay *ssd, uint32_t id) +{ + ssd->worker->destroy_surface_wait(ssd->worker, id); +} + +void qemu_spice_loadvm_commands(SimpleSpiceDisplay *ssd, + struct QXLCommandExt *ext, uint32_t count) +{ + ssd->worker->loadvm_commands(ssd->worker, ext, count); +} + +void qemu_spice_wakeup(SimpleSpiceDisplay *ssd) +{ + ssd->worker->wakeup(ssd->worker); +} + +void qemu_spice_oom(SimpleSpiceDisplay *ssd) +{ + ssd->worker->oom(ssd->worker); +} + +void qemu_spice_start(SimpleSpiceDisplay *ssd) +{ + ssd->worker->start(ssd->worker); +} + +void qemu_spice_stop(SimpleSpiceDisplay *ssd) +{ + ssd->worker->stop(ssd->worker); +} + +void qemu_spice_reset_memslots(SimpleSpiceDisplay *ssd) +{ + ssd->worker->reset_memslots(ssd->worker); +} + +void qemu_spice_destroy_surfaces(SimpleSpiceDisplay *ssd) +{ + ssd->worker->destroy_surfaces(ssd->worker); +} + +void qemu_spice_reset_image_cache(SimpleSpiceDisplay *ssd) +{ + ssd->worker->reset_image_cache(ssd->worker); +} + +void qemu_spice_reset_cursor(SimpleSpiceDisplay *ssd) +{ + ssd->worker->reset_cursor(ssd->worker); +} + + static SimpleSpiceUpdate *qemu_spice_create_update(SimpleSpiceDisplay *ssd) { SimpleSpiceUpdate *update; @@ -161,7 +244,7 @@ void qemu_spice_create_host_memslot(SimpleSpiceDisplay *ssd) memset(&memslot, 0, sizeof(memslot)); memslot.slot_group_id = MEMSLOT_GROUP_HOST; memslot.virt_end = ~0; - ssd->worker->add_memslot(ssd->worker, &memslot); + qemu_spice_add_memslot(ssd, &memslot); } void qemu_spice_create_host_primary(SimpleSpiceDisplay *ssd) @@ -181,14 +264,14 @@ void qemu_spice_create_host_primary(SimpleSpiceDisplay *ssd) surface.mem = (intptr_t)ssd->buf; surface.group_id = MEMSLOT_GROUP_HOST; - ssd->worker->create_primary_surface(ssd->worker, 0, &surface); + qemu_spice_create_primary_surface(ssd, 0, &surface); } void qemu_spice_destroy_host_primary(SimpleSpiceDisplay *ssd) { dprint(1, "%s:\n", __FUNCTION__); - ssd->worker->destroy_primary_surface(ssd->worker, 0); + qemu_spice_destroy_primary_surface(ssd, 0); } void qemu_spice_vm_change_state_handler(void *opaque, int running, int reason) @@ -196,9 +279,9 @@ void qemu_spice_vm_change_state_handler(void *opaque, int running, int reason) SimpleSpiceDisplay *ssd = opaque; if (running) { - ssd->worker->start(ssd->worker); + qemu_spice_start(ssd); } else { - ssd->worker->stop(ssd->worker); + qemu_spice_stop(ssd); } ssd->running = running; } @@ -267,7 +350,7 @@ void qemu_spice_display_refresh(SimpleSpiceDisplay *ssd) if (ssd->notify) { ssd->notify = 0; - ssd->worker->wakeup(ssd->worker); + qemu_spice_wakeup(ssd); dprint(2, "%s: notify\n", __FUNCTION__); } } diff --git a/ui/spice-display.h b/ui/spice-display.h index 2f95f68aad..5b06b11ec5 100644 --- a/ui/spice-display.h +++ b/ui/spice-display.h @@ -80,3 +80,25 @@ void qemu_spice_display_update(SimpleSpiceDisplay *ssd, int x, int y, int w, int h); void qemu_spice_display_resize(SimpleSpiceDisplay *ssd); void qemu_spice_display_refresh(SimpleSpiceDisplay *ssd); + +void qemu_spice_update_area(SimpleSpiceDisplay *ssd, uint32_t surface_id, + struct QXLRect *area, struct QXLRect *dirty_rects, + uint32_t num_dirty_rects, + uint32_t clear_dirty_region); +void qemu_spice_add_memslot(SimpleSpiceDisplay *ssd, QXLDevMemSlot *memslot); +void qemu_spice_del_memslot(SimpleSpiceDisplay *ssd, uint32_t gid, + uint32_t sid); +void qemu_spice_create_primary_surface(SimpleSpiceDisplay *ssd, uint32_t id, + QXLDevSurfaceCreate *surface); +void qemu_spice_destroy_primary_surface(SimpleSpiceDisplay *ssd, uint32_t id); +void qemu_spice_destroy_surface_wait(SimpleSpiceDisplay *ssd, uint32_t id); +void qemu_spice_loadvm_commands(SimpleSpiceDisplay *ssd, + struct QXLCommandExt *ext, uint32_t count); +void qemu_spice_wakeup(SimpleSpiceDisplay *ssd); +void qemu_spice_oom(SimpleSpiceDisplay *ssd); +void qemu_spice_start(SimpleSpiceDisplay *ssd); +void qemu_spice_stop(SimpleSpiceDisplay *ssd); +void qemu_spice_reset_memslots(SimpleSpiceDisplay *ssd); +void qemu_spice_destroy_surfaces(SimpleSpiceDisplay *ssd); +void qemu_spice_reset_image_cache(SimpleSpiceDisplay *ssd); +void qemu_spice_reset_cursor(SimpleSpiceDisplay *ssd); From a963f876c80e07a195850a6ab243371b6f93756e Mon Sep 17 00:00:00 2001 From: Gerd Hoffmann Date: Wed, 20 Jul 2011 12:20:51 +0300 Subject: [PATCH 167/230] spice: add qemu_spice_display_init_common Factor out SimpleSpiceDisplay initialization into qemu_spice_display_init_common() and call it from both qxl.c (for vga mode) and spice-display.c Signed-off-by: Gerd Hoffmann --- hw/qxl.c | 7 +------ ui/spice-display.c | 17 +++++++++++------ ui/spice-display.h | 1 + 3 files changed, 13 insertions(+), 12 deletions(-) diff --git a/hw/qxl.c b/hw/qxl.c index 5deb776580..2127fa346b 100644 --- a/hw/qxl.c +++ b/hw/qxl.c @@ -1315,12 +1315,7 @@ static int qxl_init_primary(PCIDevice *dev) vga->ds = graphic_console_init(qxl_hw_update, qxl_hw_invalidate, qxl_hw_screen_dump, qxl_hw_text_update, qxl); - qxl->ssd.ds = vga->ds; - qemu_mutex_init(&qxl->ssd.lock); - qxl->ssd.mouse_x = -1; - qxl->ssd.mouse_y = -1; - qxl->ssd.bufsize = (16 * 1024 * 1024); - qxl->ssd.buf = qemu_malloc(qxl->ssd.bufsize); + qemu_spice_display_init_common(&qxl->ssd, vga->ds); qxl0 = qxl; register_displaychangelistener(vga->ds, &display_listener); diff --git a/ui/spice-display.c b/ui/spice-display.c index 1e6a38f86c..93e25bf1ef 100644 --- a/ui/spice-display.c +++ b/ui/spice-display.c @@ -286,6 +286,16 @@ void qemu_spice_vm_change_state_handler(void *opaque, int running, int reason) ssd->running = running; } +void qemu_spice_display_init_common(SimpleSpiceDisplay *ssd, DisplayState *ds) +{ + ssd->ds = ds; + qemu_mutex_init(&ssd->lock); + ssd->mouse_x = -1; + ssd->mouse_y = -1; + ssd->bufsize = (16 * 1024 * 1024); + ssd->buf = qemu_malloc(ssd->bufsize); +} + /* display listener callbacks */ void qemu_spice_display_update(SimpleSpiceDisplay *ssd, @@ -499,12 +509,7 @@ static DisplayChangeListener display_listener = { void qemu_spice_display_init(DisplayState *ds) { assert(sdpy.ds == NULL); - sdpy.ds = ds; - qemu_mutex_init(&sdpy.lock); - sdpy.mouse_x = -1; - sdpy.mouse_y = -1; - sdpy.bufsize = (16 * 1024 * 1024); - sdpy.buf = qemu_malloc(sdpy.bufsize); + qemu_spice_display_init_common(&sdpy, ds); register_displaychangelistener(ds, &display_listener); sdpy.qxl.base.sif = &dpy_interface.base; diff --git a/ui/spice-display.h b/ui/spice-display.h index 5b06b11ec5..eb7a573855 100644 --- a/ui/spice-display.h +++ b/ui/spice-display.h @@ -75,6 +75,7 @@ void qemu_spice_create_host_memslot(SimpleSpiceDisplay *ssd); void qemu_spice_create_host_primary(SimpleSpiceDisplay *ssd); void qemu_spice_destroy_host_primary(SimpleSpiceDisplay *ssd); void qemu_spice_vm_change_state_handler(void *opaque, int running, int reason); +void qemu_spice_display_init_common(SimpleSpiceDisplay *ssd, DisplayState *ds); void qemu_spice_display_update(SimpleSpiceDisplay *ssd, int x, int y, int w, int h); From aee32bf333b13994d9eafb768129e4c2c8688887 Mon Sep 17 00:00:00 2001 From: Gerd Hoffmann Date: Wed, 20 Jul 2011 12:20:52 +0300 Subject: [PATCH 168/230] spice/qxl: move worker wrappers Move the wrapper functions which are used by qxl only to qxl.c. Rename them from qemu_spice_* to qxl_spice_*. Also pass in a qxl state pointer instead of a SimpleSpiceDisplay pointer. Signed-off-by: Gerd Hoffmann --- hw/qxl-render.c | 4 +-- hw/qxl.c | 67 +++++++++++++++++++++++++++++++++++++++------- hw/qxl.h | 13 +++++++++ ui/spice-display.c | 46 ------------------------------- ui/spice-display.h | 12 --------- 5 files changed, 72 insertions(+), 70 deletions(-) diff --git a/hw/qxl-render.c b/hw/qxl-render.c index bef5f148e4..60b822d8c0 100644 --- a/hw/qxl-render.c +++ b/hw/qxl-render.c @@ -124,8 +124,8 @@ void qxl_render_update(PCIQXLDevice *qxl) update.bottom = qxl->guest_primary.surface.height; memset(dirty, 0, sizeof(dirty)); - qemu_spice_update_area(&qxl->ssd, 0, &update, - dirty, ARRAY_SIZE(dirty), 1); + qxl_spice_update_area(qxl, 0, &update, + dirty, ARRAY_SIZE(dirty), 1); for (i = 0; i < ARRAY_SIZE(dirty); i++) { if (qemu_spice_rect_is_empty(dirty+i)) { diff --git a/hw/qxl.c b/hw/qxl.c index 2127fa346b..803a364c99 100644 --- a/hw/qxl.c +++ b/hw/qxl.c @@ -125,6 +125,53 @@ static void qxl_reset_memslots(PCIQXLDevice *d); static void qxl_reset_surfaces(PCIQXLDevice *d); static void qxl_ring_set_dirty(PCIQXLDevice *qxl); + +void qxl_spice_update_area(PCIQXLDevice *qxl, uint32_t surface_id, + struct QXLRect *area, struct QXLRect *dirty_rects, + uint32_t num_dirty_rects, + uint32_t clear_dirty_region) +{ + qxl->ssd.worker->update_area(qxl->ssd.worker, surface_id, area, dirty_rects, + num_dirty_rects, clear_dirty_region); +} + +void qxl_spice_destroy_surface_wait(PCIQXLDevice *qxl, uint32_t id) +{ + qxl->ssd.worker->destroy_surface_wait(qxl->ssd.worker, id); +} + +void qxl_spice_loadvm_commands(PCIQXLDevice *qxl, struct QXLCommandExt *ext, + uint32_t count) +{ + qxl->ssd.worker->loadvm_commands(qxl->ssd.worker, ext, count); +} + +void qxl_spice_oom(PCIQXLDevice *qxl) +{ + qxl->ssd.worker->oom(qxl->ssd.worker); +} + +void qxl_spice_reset_memslots(PCIQXLDevice *qxl) +{ + qxl->ssd.worker->reset_memslots(qxl->ssd.worker); +} + +void qxl_spice_destroy_surfaces(PCIQXLDevice *qxl) +{ + qxl->ssd.worker->destroy_surfaces(qxl->ssd.worker); +} + +void qxl_spice_reset_image_cache(PCIQXLDevice *qxl) +{ + qxl->ssd.worker->reset_image_cache(qxl->ssd.worker); +} + +void qxl_spice_reset_cursor(PCIQXLDevice *qxl) +{ + qxl->ssd.worker->reset_cursor(qxl->ssd.worker); +} + + static inline uint32_t msb_mask(uint32_t val) { uint32_t mask; @@ -684,8 +731,8 @@ static void qxl_hard_reset(PCIQXLDevice *d, int loadvm) dprint(d, 1, "%s: start%s\n", __FUNCTION__, loadvm ? " (loadvm)" : ""); - qemu_spice_reset_cursor(&d->ssd); - qemu_spice_reset_image_cache(&d->ssd); + qxl_spice_reset_cursor(d); + qxl_spice_reset_image_cache(d); qxl_reset_surfaces(d); qxl_reset_memslots(d); @@ -807,7 +854,7 @@ static void qxl_del_memslot(PCIQXLDevice *d, uint32_t slot_id) static void qxl_reset_memslots(PCIQXLDevice *d) { dprint(d, 1, "%s:\n", __FUNCTION__); - qemu_spice_reset_memslots(&d->ssd); + qxl_spice_reset_memslots(d); memset(&d->guest_slots, 0, sizeof(d->guest_slots)); } @@ -815,7 +862,7 @@ static void qxl_reset_surfaces(PCIQXLDevice *d) { dprint(d, 1, "%s:\n", __FUNCTION__); d->mode = QXL_MODE_UNDEFINED; - qemu_spice_destroy_surfaces(&d->ssd); + qxl_spice_destroy_surfaces(d); memset(&d->guest_surfaces.cmds, 0, sizeof(d->guest_surfaces.cmds)); } @@ -956,8 +1003,8 @@ static void ioport_write(void *opaque, uint32_t addr, uint32_t val) case QXL_IO_UPDATE_AREA: { QXLRect update = d->ram->update_area; - qemu_spice_update_area(&d->ssd, d->ram->update_surface, - &update, NULL, 0, 0); + qxl_spice_update_area(d, d->ram->update_surface, + &update, NULL, 0, 0); break; } case QXL_IO_NOTIFY_CMD: @@ -978,7 +1025,7 @@ static void ioport_write(void *opaque, uint32_t addr, uint32_t val) break; } d->oom_running = 1; - qemu_spice_oom(&d->ssd); + qxl_spice_oom(d); d->oom_running = 0; break; case QXL_IO_SET_MODE: @@ -1016,10 +1063,10 @@ static void ioport_write(void *opaque, uint32_t addr, uint32_t val) qxl_destroy_primary(d); break; case QXL_IO_DESTROY_SURFACE_WAIT: - qemu_spice_destroy_surface_wait(&d->ssd, val); + qxl_spice_destroy_surface_wait(d, val); break; case QXL_IO_DESTROY_ALL_SURFACES: - qemu_spice_destroy_surfaces(&d->ssd); + qxl_spice_destroy_surfaces(d); break; default: fprintf(stderr, "%s: ioport=0x%x, abort()\n", __FUNCTION__, io_port); @@ -1419,7 +1466,7 @@ static int qxl_post_load(void *opaque, int version) cmds[out].cmd.type = QXL_CMD_CURSOR; cmds[out].group_id = MEMSLOT_GROUP_GUEST; out++; - qemu_spice_loadvm_commands(&d->ssd, cmds, out); + qxl_spice_loadvm_commands(d, cmds, out); qemu_free(cmds); break; diff --git a/hw/qxl.h b/hw/qxl.h index f6c450d32d..e62b9d00b2 100644 --- a/hw/qxl.h +++ b/hw/qxl.h @@ -98,6 +98,19 @@ typedef struct PCIQXLDevice { /* qxl.c */ void *qxl_phys2virt(PCIQXLDevice *qxl, QXLPHYSICAL phys, int group_id); +void qxl_spice_update_area(PCIQXLDevice *qxl, uint32_t surface_id, + struct QXLRect *area, struct QXLRect *dirty_rects, + uint32_t num_dirty_rects, + uint32_t clear_dirty_region); +void qxl_spice_destroy_surface_wait(PCIQXLDevice *qxl, uint32_t id); +void qxl_spice_loadvm_commands(PCIQXLDevice *qxl, struct QXLCommandExt *ext, + uint32_t count); +void qxl_spice_oom(PCIQXLDevice *qxl); +void qxl_spice_reset_memslots(PCIQXLDevice *qxl); +void qxl_spice_destroy_surfaces(PCIQXLDevice *qxl); +void qxl_spice_reset_image_cache(PCIQXLDevice *qxl); +void qxl_spice_reset_cursor(PCIQXLDevice *qxl); + /* qxl-logger.c */ void qxl_log_cmd_cursor(PCIQXLDevice *qxl, QXLCursorCmd *cmd, int group_id); void qxl_log_command(PCIQXLDevice *qxl, const char *ring, QXLCommandExt *ext); diff --git a/ui/spice-display.c b/ui/spice-display.c index 93e25bf1ef..af10ae8a6f 100644 --- a/ui/spice-display.c +++ b/ui/spice-display.c @@ -63,15 +63,6 @@ void qemu_spice_rect_union(QXLRect *dest, const QXLRect *r) } -void qemu_spice_update_area(SimpleSpiceDisplay *ssd, uint32_t surface_id, - struct QXLRect *area, struct QXLRect *dirty_rects, - uint32_t num_dirty_rects, - uint32_t clear_dirty_region) -{ - ssd->worker->update_area(ssd->worker, surface_id, area, dirty_rects, - num_dirty_rects, clear_dirty_region); -} - void qemu_spice_add_memslot(SimpleSpiceDisplay *ssd, QXLDevMemSlot *memslot) { ssd->worker->add_memslot(ssd->worker, memslot); @@ -93,27 +84,11 @@ void qemu_spice_destroy_primary_surface(SimpleSpiceDisplay *ssd, uint32_t id) ssd->worker->destroy_primary_surface(ssd->worker, id); } -void qemu_spice_destroy_surface_wait(SimpleSpiceDisplay *ssd, uint32_t id) -{ - ssd->worker->destroy_surface_wait(ssd->worker, id); -} - -void qemu_spice_loadvm_commands(SimpleSpiceDisplay *ssd, - struct QXLCommandExt *ext, uint32_t count) -{ - ssd->worker->loadvm_commands(ssd->worker, ext, count); -} - void qemu_spice_wakeup(SimpleSpiceDisplay *ssd) { ssd->worker->wakeup(ssd->worker); } -void qemu_spice_oom(SimpleSpiceDisplay *ssd) -{ - ssd->worker->oom(ssd->worker); -} - void qemu_spice_start(SimpleSpiceDisplay *ssd) { ssd->worker->start(ssd->worker); @@ -124,27 +99,6 @@ void qemu_spice_stop(SimpleSpiceDisplay *ssd) ssd->worker->stop(ssd->worker); } -void qemu_spice_reset_memslots(SimpleSpiceDisplay *ssd) -{ - ssd->worker->reset_memslots(ssd->worker); -} - -void qemu_spice_destroy_surfaces(SimpleSpiceDisplay *ssd) -{ - ssd->worker->destroy_surfaces(ssd->worker); -} - -void qemu_spice_reset_image_cache(SimpleSpiceDisplay *ssd) -{ - ssd->worker->reset_image_cache(ssd->worker); -} - -void qemu_spice_reset_cursor(SimpleSpiceDisplay *ssd) -{ - ssd->worker->reset_cursor(ssd->worker); -} - - static SimpleSpiceUpdate *qemu_spice_create_update(SimpleSpiceDisplay *ssd) { SimpleSpiceUpdate *update; diff --git a/ui/spice-display.h b/ui/spice-display.h index eb7a573855..abe99c7d33 100644 --- a/ui/spice-display.h +++ b/ui/spice-display.h @@ -82,24 +82,12 @@ void qemu_spice_display_update(SimpleSpiceDisplay *ssd, void qemu_spice_display_resize(SimpleSpiceDisplay *ssd); void qemu_spice_display_refresh(SimpleSpiceDisplay *ssd); -void qemu_spice_update_area(SimpleSpiceDisplay *ssd, uint32_t surface_id, - struct QXLRect *area, struct QXLRect *dirty_rects, - uint32_t num_dirty_rects, - uint32_t clear_dirty_region); void qemu_spice_add_memslot(SimpleSpiceDisplay *ssd, QXLDevMemSlot *memslot); void qemu_spice_del_memslot(SimpleSpiceDisplay *ssd, uint32_t gid, uint32_t sid); void qemu_spice_create_primary_surface(SimpleSpiceDisplay *ssd, uint32_t id, QXLDevSurfaceCreate *surface); void qemu_spice_destroy_primary_surface(SimpleSpiceDisplay *ssd, uint32_t id); -void qemu_spice_destroy_surface_wait(SimpleSpiceDisplay *ssd, uint32_t id); -void qemu_spice_loadvm_commands(SimpleSpiceDisplay *ssd, - struct QXLCommandExt *ext, uint32_t count); void qemu_spice_wakeup(SimpleSpiceDisplay *ssd); -void qemu_spice_oom(SimpleSpiceDisplay *ssd); void qemu_spice_start(SimpleSpiceDisplay *ssd); void qemu_spice_stop(SimpleSpiceDisplay *ssd); -void qemu_spice_reset_memslots(SimpleSpiceDisplay *ssd); -void qemu_spice_destroy_surfaces(SimpleSpiceDisplay *ssd); -void qemu_spice_reset_image_cache(SimpleSpiceDisplay *ssd); -void qemu_spice_reset_cursor(SimpleSpiceDisplay *ssd); From 14898cf6e9994319e7947b223f637f964f9256e0 Mon Sep 17 00:00:00 2001 From: Gerd Hoffmann Date: Wed, 20 Jul 2011 12:20:53 +0300 Subject: [PATCH 169/230] qxl: fix surface tracking & locking Surface tracking needs proper locking since it is used from vcpu and spice worker threads, add it. Also reset the surface counter when zapping all surfaces. Signed-off-by: Gerd Hoffmann --- hw/qxl.c | 13 ++++++++++++- hw/qxl.h | 2 ++ 2 files changed, 14 insertions(+), 1 deletion(-) diff --git a/hw/qxl.c b/hw/qxl.c index 803a364c99..416bd48028 100644 --- a/hw/qxl.c +++ b/hw/qxl.c @@ -137,7 +137,12 @@ void qxl_spice_update_area(PCIQXLDevice *qxl, uint32_t surface_id, void qxl_spice_destroy_surface_wait(PCIQXLDevice *qxl, uint32_t id) { + qemu_mutex_lock(&qxl->track_lock); + PANIC_ON(id >= NUM_SURFACES); qxl->ssd.worker->destroy_surface_wait(qxl->ssd.worker, id); + qxl->guest_surfaces.cmds[id] = 0; + qxl->guest_surfaces.count--; + qemu_mutex_unlock(&qxl->track_lock); } void qxl_spice_loadvm_commands(PCIQXLDevice *qxl, struct QXLCommandExt *ext, @@ -158,7 +163,11 @@ void qxl_spice_reset_memslots(PCIQXLDevice *qxl) void qxl_spice_destroy_surfaces(PCIQXLDevice *qxl) { + qemu_mutex_lock(&qxl->track_lock); qxl->ssd.worker->destroy_surfaces(qxl->ssd.worker); + memset(&qxl->guest_surfaces.cmds, 0, sizeof(qxl->guest_surfaces.cmds)); + qxl->guest_surfaces.count = 0; + qemu_mutex_unlock(&qxl->track_lock); } void qxl_spice_reset_image_cache(PCIQXLDevice *qxl) @@ -317,6 +326,7 @@ static void qxl_track_command(PCIQXLDevice *qxl, struct QXLCommandExt *ext) QXLSurfaceCmd *cmd = qxl_phys2virt(qxl, ext->cmd.data, ext->group_id); uint32_t id = le32_to_cpu(cmd->surface_id); PANIC_ON(id >= NUM_SURFACES); + qemu_mutex_lock(&qxl->track_lock); if (cmd->type == QXL_SURFACE_CMD_CREATE) { qxl->guest_surfaces.cmds[id] = ext->cmd.data; qxl->guest_surfaces.count++; @@ -327,6 +337,7 @@ static void qxl_track_command(PCIQXLDevice *qxl, struct QXLCommandExt *ext) qxl->guest_surfaces.cmds[id] = 0; qxl->guest_surfaces.count--; } + qemu_mutex_unlock(&qxl->track_lock); break; } case QXL_CMD_CURSOR: @@ -863,7 +874,6 @@ static void qxl_reset_surfaces(PCIQXLDevice *d) dprint(d, 1, "%s:\n", __FUNCTION__); d->mode = QXL_MODE_UNDEFINED; qxl_spice_destroy_surfaces(d); - memset(&d->guest_surfaces.cmds, 0, sizeof(d->guest_surfaces.cmds)); } /* called from spice server thread context only */ @@ -1283,6 +1293,7 @@ static int qxl_init_common(PCIQXLDevice *qxl) qxl->generation = 1; qxl->num_memslots = NUM_MEMSLOTS; qxl->num_surfaces = NUM_SURFACES; + qemu_mutex_init(&qxl->track_lock); switch (qxl->revision) { case 1: /* spice 0.4 -- qxl-1 */ diff --git a/hw/qxl.h b/hw/qxl.h index e62b9d00b2..5d0e85edb3 100644 --- a/hw/qxl.h +++ b/hw/qxl.h @@ -55,6 +55,8 @@ typedef struct PCIQXLDevice { } guest_surfaces; QXLPHYSICAL guest_cursor; + QemuMutex track_lock; + /* thread signaling */ pthread_t main; int pipe[2]; From 8b92e2989eddaca0bef5076135d2dee3c06f6700 Mon Sep 17 00:00:00 2001 From: Alon Levy Date: Wed, 20 Jul 2011 12:20:54 +0300 Subject: [PATCH 170/230] qxl: add io_port_to_string Signed-off-by: Alon Levy Signed-off-by: Gerd Hoffmann --- hw/qxl.c | 40 +++++++++++++++++++++++++++++++++++++++- 1 file changed, 39 insertions(+), 1 deletion(-) diff --git a/hw/qxl.c b/hw/qxl.c index 416bd48028..6e66021722 100644 --- a/hw/qxl.c +++ b/hw/qxl.c @@ -409,6 +409,43 @@ static const char *qxl_mode_to_string(int mode) return "INVALID"; } +static const char *io_port_to_string(uint32_t io_port) +{ + if (io_port >= QXL_IO_RANGE_SIZE) { + return "out of range"; + } + static const char *io_port_to_string[QXL_IO_RANGE_SIZE + 1] = { + [QXL_IO_NOTIFY_CMD] = "QXL_IO_NOTIFY_CMD", + [QXL_IO_NOTIFY_CURSOR] = "QXL_IO_NOTIFY_CURSOR", + [QXL_IO_UPDATE_AREA] = "QXL_IO_UPDATE_AREA", + [QXL_IO_UPDATE_IRQ] = "QXL_IO_UPDATE_IRQ", + [QXL_IO_NOTIFY_OOM] = "QXL_IO_NOTIFY_OOM", + [QXL_IO_RESET] = "QXL_IO_RESET", + [QXL_IO_SET_MODE] = "QXL_IO_SET_MODE", + [QXL_IO_LOG] = "QXL_IO_LOG", + [QXL_IO_MEMSLOT_ADD] = "QXL_IO_MEMSLOT_ADD", + [QXL_IO_MEMSLOT_DEL] = "QXL_IO_MEMSLOT_DEL", + [QXL_IO_DETACH_PRIMARY] = "QXL_IO_DETACH_PRIMARY", + [QXL_IO_ATTACH_PRIMARY] = "QXL_IO_ATTACH_PRIMARY", + [QXL_IO_CREATE_PRIMARY] = "QXL_IO_CREATE_PRIMARY", + [QXL_IO_DESTROY_PRIMARY] = "QXL_IO_DESTROY_PRIMARY", + [QXL_IO_DESTROY_SURFACE_WAIT] = "QXL_IO_DESTROY_SURFACE_WAIT", + [QXL_IO_DESTROY_ALL_SURFACES] = "QXL_IO_DESTROY_ALL_SURFACES", +#if SPICE_INTERFACE_QXL_MINOR >= 1 + [QXL_IO_UPDATE_AREA_ASYNC] = "QXL_IO_UPDATE_AREA_ASYNC", + [QXL_IO_MEMSLOT_ADD_ASYNC] = "QXL_IO_MEMSLOT_ADD_ASYNC", + [QXL_IO_CREATE_PRIMARY_ASYNC] = "QXL_IO_CREATE_PRIMARY_ASYNC", + [QXL_IO_DESTROY_PRIMARY_ASYNC] = "QXL_IO_DESTROY_PRIMARY_ASYNC", + [QXL_IO_DESTROY_SURFACE_ASYNC] = "QXL_IO_DESTROY_SURFACE_ASYNC", + [QXL_IO_DESTROY_ALL_SURFACES_ASYNC] + = "QXL_IO_DESTROY_ALL_SURFACES_ASYNC", + [QXL_IO_FLUSH_SURFACES_ASYNC] = "QXL_IO_FLUSH_SURFACES_ASYNC", + [QXL_IO_FLUSH_RELEASE] = "QXL_IO_FLUSH_RELEASE", +#endif + }; + return io_port_to_string[io_port]; +} + /* called from spice server thread context only */ static int interface_get_command(QXLInstance *sin, struct QXLCommandExt *ext) { @@ -1005,7 +1042,8 @@ static void ioport_write(void *opaque, uint32_t addr, uint32_t val) default: if (d->mode == QXL_MODE_NATIVE || d->mode == QXL_MODE_COMPAT) break; - dprint(d, 1, "%s: unexpected port 0x%x in vga mode\n", __FUNCTION__, io_port); + dprint(d, 1, "%s: unexpected port 0x%x (%s) in vga mode\n", + __func__, io_port, io_port_to_string(io_port)); return; } From 2bce0400579f58ccb33d201cde9e63c39750faf4 Mon Sep 17 00:00:00 2001 From: Gerd Hoffmann Date: Wed, 20 Jul 2011 12:20:55 +0300 Subject: [PATCH 171/230] qxl: error handling fixes and cleanups. Add qxl_guest_bug() function which is supposed to be called in case sanity checks of guest requests fail. It raises an error IRQ and logs a message in case guest debugging is enabled. Make PANIC_ON() abort instead of exit. That macro should be used for qemu bugs only, any guest-triggerable stuff should use the new qxl_guest_bug() function instead. Convert a few easy cases from PANIC_ON() to qxl_guest_bug() to show intended usage. Signed-off-by: Gerd Hoffmann --- hw/qxl.c | 34 ++++++++++++++++++++++++++++++---- hw/qxl.h | 3 ++- 2 files changed, 32 insertions(+), 5 deletions(-) diff --git a/hw/qxl.c b/hw/qxl.c index 6e66021722..28c8b5d0d9 100644 --- a/hw/qxl.c +++ b/hw/qxl.c @@ -125,6 +125,16 @@ static void qxl_reset_memslots(PCIQXLDevice *d); static void qxl_reset_surfaces(PCIQXLDevice *d); static void qxl_ring_set_dirty(PCIQXLDevice *qxl); +void qxl_guest_bug(PCIQXLDevice *qxl, const char *msg) +{ +#if SPICE_INTERFACE_QXL_MINOR >= 1 + qxl_send_events(qxl, QXL_INTERRUPT_ERROR); +#endif + if (qxl->guestdebug) { + fprintf(stderr, "qxl-%d: guest bug: %s\n", qxl->id, msg); + } +} + void qxl_spice_update_area(PCIQXLDevice *qxl, uint32_t surface_id, struct QXLRect *area, struct QXLRect *dirty_rects, @@ -1091,22 +1101,38 @@ static void ioport_write(void *opaque, uint32_t addr, uint32_t val) qxl_hard_reset(d, 0); break; case QXL_IO_MEMSLOT_ADD: - PANIC_ON(val >= NUM_MEMSLOTS); - PANIC_ON(d->guest_slots[val].active); + if (val >= NUM_MEMSLOTS) { + qxl_guest_bug(d, "QXL_IO_MEMSLOT_ADD: val out of range"); + break; + } + if (d->guest_slots[val].active) { + qxl_guest_bug(d, "QXL_IO_MEMSLOT_ADD: memory slot already active"); + break; + } d->guest_slots[val].slot = d->ram->mem_slot; qxl_add_memslot(d, val, 0); break; case QXL_IO_MEMSLOT_DEL: + if (val >= NUM_MEMSLOTS) { + qxl_guest_bug(d, "QXL_IO_MEMSLOT_DEL: val out of range"); + break; + } qxl_del_memslot(d, val); break; case QXL_IO_CREATE_PRIMARY: - PANIC_ON(val != 0); + if (val != 0) { + qxl_guest_bug(d, "QXL_IO_CREATE_PRIMARY: val != 0"); + break; + } dprint(d, 1, "QXL_IO_CREATE_PRIMARY\n"); d->guest_primary.surface = d->ram->create_surface; qxl_create_guest_primary(d, 0); break; case QXL_IO_DESTROY_PRIMARY: - PANIC_ON(val != 0); + if (val != 0) { + qxl_guest_bug(d, "QXL_IO_DESTROY_PRIMARY: val != 0"); + break; + } dprint(d, 1, "QXL_IO_DESTROY_PRIMARY (%s)\n", qxl_mode_to_string(d->mode)); qxl_destroy_primary(d); break; diff --git a/hw/qxl.h b/hw/qxl.h index 5d0e85edb3..5db9aaee2f 100644 --- a/hw/qxl.h +++ b/hw/qxl.h @@ -86,7 +86,7 @@ typedef struct PCIQXLDevice { #define PANIC_ON(x) if ((x)) { \ printf("%s: PANIC %s failed\n", __FUNCTION__, #x); \ - exit(-1); \ + abort(); \ } #define dprint(_qxl, _level, _fmt, ...) \ @@ -99,6 +99,7 @@ typedef struct PCIQXLDevice { /* qxl.c */ void *qxl_phys2virt(PCIQXLDevice *qxl, QXLPHYSICAL phys, int group_id); +void qxl_guest_bug(PCIQXLDevice *qxl, const char *msg); void qxl_spice_update_area(PCIQXLDevice *qxl, uint32_t surface_id, struct QXLRect *area, struct QXLRect *dirty_rects, From 7635392ce6844702b4e0faadfa558a6972e16098 Mon Sep 17 00:00:00 2001 From: Alon Levy Date: Wed, 20 Jul 2011 12:20:56 +0300 Subject: [PATCH 172/230] qxl: make qxl_guest_bug take variable arguments Signed-off-by: Alon Levy Signed-off-by: Gerd Hoffmann --- hw/qxl.c | 9 +++++++-- hw/qxl.h | 2 +- 2 files changed, 8 insertions(+), 3 deletions(-) diff --git a/hw/qxl.c b/hw/qxl.c index 28c8b5d0d9..c50eaf9ac1 100644 --- a/hw/qxl.c +++ b/hw/qxl.c @@ -125,13 +125,18 @@ static void qxl_reset_memslots(PCIQXLDevice *d); static void qxl_reset_surfaces(PCIQXLDevice *d); static void qxl_ring_set_dirty(PCIQXLDevice *qxl); -void qxl_guest_bug(PCIQXLDevice *qxl, const char *msg) +void qxl_guest_bug(PCIQXLDevice *qxl, const char *msg, ...) { #if SPICE_INTERFACE_QXL_MINOR >= 1 qxl_send_events(qxl, QXL_INTERRUPT_ERROR); #endif if (qxl->guestdebug) { - fprintf(stderr, "qxl-%d: guest bug: %s\n", qxl->id, msg); + va_list ap; + va_start(ap, msg); + fprintf(stderr, "qxl-%d: guest bug: ", qxl->id); + vfprintf(stderr, msg, ap); + fprintf(stderr, "\n"); + va_end(ap); } } diff --git a/hw/qxl.h b/hw/qxl.h index 5db9aaee2f..32ca5a0895 100644 --- a/hw/qxl.h +++ b/hw/qxl.h @@ -99,7 +99,7 @@ typedef struct PCIQXLDevice { /* qxl.c */ void *qxl_phys2virt(PCIQXLDevice *qxl, QXLPHYSICAL phys, int group_id); -void qxl_guest_bug(PCIQXLDevice *qxl, const char *msg); +void qxl_guest_bug(PCIQXLDevice *qxl, const char *msg, ...); void qxl_spice_update_area(PCIQXLDevice *qxl, uint32_t surface_id, struct QXLRect *area, struct QXLRect *dirty_rects, From e21a298a7b7a5c5e8edc4912dec3b497497c347d Mon Sep 17 00:00:00 2001 From: Alon Levy Date: Wed, 20 Jul 2011 12:20:57 +0300 Subject: [PATCH 173/230] qxl: only disallow specific io's in vga mode Since the driver is still in operation even after moving to UNDEFINED, i.e. by destroying primary in any way. Signed-off-by: Alon Levy Signed-off-by: Gerd Hoffmann --- hw/qxl.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/hw/qxl.c b/hw/qxl.c index c50eaf9ac1..23e3240379 100644 --- a/hw/qxl.c +++ b/hw/qxl.c @@ -1055,8 +1055,9 @@ static void ioport_write(void *opaque, uint32_t addr, uint32_t val) case QXL_IO_LOG: break; default: - if (d->mode == QXL_MODE_NATIVE || d->mode == QXL_MODE_COMPAT) + if (d->mode != QXL_MODE_VGA) { break; + } dprint(d, 1, "%s: unexpected port 0x%x (%s) in vga mode\n", __func__, io_port, io_port_to_string(io_port)); return; From 67494323f2c782fe3e65c60529fe9dfa613fc500 Mon Sep 17 00:00:00 2001 From: Blue Swirl Date: Mon, 1 Aug 2011 21:26:03 +0000 Subject: [PATCH 174/230] Sparc: fix non-faulting unassigned memory accesses Commit b14ef7c9ab41ea824c3ccadb070ad95567cca84e introduced cpu_unassigned_access() function. On Sparc, the function does not restore AREG0 used for global CPUState on function exit, causing bugs with non-faulting unassigned memory accesses. Alpha, Microblaze and MIPS are not affected. Fix by restoring AREG0 on exit. Remove excess saving by do_unassigned_access() functions. Also ignore unassigned accesses outside of CPU context. Reported-by: Bob Breuer Tested-by: Bob Breuer Signed-off-by: Blue Swirl --- target-sparc/op_helper.c | 25 ++++++++----------------- 1 file changed, 8 insertions(+), 17 deletions(-) diff --git a/target-sparc/op_helper.c b/target-sparc/op_helper.c index c1c4d4b07e..5aeca2b06d 100644 --- a/target-sparc/op_helper.c +++ b/target-sparc/op_helper.c @@ -4252,13 +4252,8 @@ void tlb_fill(target_ulong addr, int is_write, int mmu_idx, void *retaddr) static void do_unassigned_access(target_phys_addr_t addr, int is_write, int is_exec, int is_asi, int size) { - CPUState *saved_env; int fault_type; - /* XXX: hack to restore env in all cases, even if not called from - generated code */ - saved_env = env; - env = cpu_single_env; #ifdef DEBUG_UNASSIGNED if (is_asi) printf("Unassigned mem %s access of %d byte%s to " TARGET_FMT_plx @@ -4306,8 +4301,6 @@ static void do_unassigned_access(target_phys_addr_t addr, int is_write, if (env->mmuregs[0] & MMU_NF) { tlb_flush(env, 1); } - - env = saved_env; } #endif #else @@ -4319,13 +4312,6 @@ static void do_unassigned_access(target_phys_addr_t addr, int is_write, int is_exec, int is_asi, int size) #endif { - CPUState *saved_env; - - /* XXX: hack to restore env in all cases, even if not called from - generated code */ - saved_env = env; - env = cpu_single_env; - #ifdef DEBUG_UNASSIGNED printf("Unassigned mem access to " TARGET_FMT_plx " from " TARGET_FMT_lx "\n", addr, env->pc); @@ -4335,8 +4321,6 @@ static void do_unassigned_access(target_phys_addr_t addr, int is_write, raise_exception(TT_CODE_ACCESS); else raise_exception(TT_DATA_ACCESS); - - env = saved_env; } #endif @@ -4370,7 +4354,14 @@ void helper_tick_set_limit(void *opaque, uint64_t limit) void cpu_unassigned_access(CPUState *env1, target_phys_addr_t addr, int is_write, int is_exec, int is_asi, int size) { + CPUState *saved_env; + + saved_env = env; env = env1; - do_unassigned_access(addr, is_write, is_exec, is_asi, size); + /* Ignore unassigned accesses outside of CPU context */ + if (env1) { + do_unassigned_access(addr, is_write, is_exec, is_asi, size); + } + env = saved_env; } #endif From 4995f0d6212c65312f746c0d0076429f25e68415 Mon Sep 17 00:00:00 2001 From: Alon Levy Date: Thu, 28 Jul 2011 12:34:13 +0300 Subject: [PATCH 175/230] libcacard: use INSTALL_DATA for data Signed-off-by: Alon Levy --- libcacard/Makefile | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/libcacard/Makefile b/libcacard/Makefile index bc34bf2449..5cd759444b 100644 --- a/libcacard/Makefile +++ b/libcacard/Makefile @@ -55,9 +55,8 @@ install-libcacard: libcacard.pc libcacard.la vscclient $(INSTALL_DIR) "$(DESTDIR)$(bindir)" libtool --mode=install $(INSTALL_PROG) vscclient "$(DESTDIR)$(bindir)" libtool --mode=install $(INSTALL_PROG) libcacard.la "$(DESTDIR)$(libdir)" - libtool --mode=install $(INSTALL_PROG) libcacard.pc "$(DESTDIR)$(libdir)/pkgconfig" + libtool --mode=install $(INSTALL_DATA) libcacard.pc "$(DESTDIR)$(libdir)/pkgconfig" for inc in *.h; do \ - libtool --mode=install $(INSTALL_PROG) $(libcacard_srcpath)/$$inc "$(DESTDIR)$(libcacard_includedir)"; \ + libtool --mode=install $(INSTALL_DATA) $(libcacard_srcpath)/$$inc "$(DESTDIR)$(libcacard_includedir)"; \ done - endif From aa7ee42ed3b2c196de9f390fc9a5e339dd7417e3 Mon Sep 17 00:00:00 2001 From: Stefan Hajnoczi Date: Tue, 10 May 2011 10:21:18 +0100 Subject: [PATCH 176/230] coroutine: add test-coroutine automated tests To run automated tests for coroutines: make test-coroutine ./test-coroutine On success the program terminates with exit status 0. On failure an error message is written to stderr and the program exits with exit status 1. Signed-off-by: Stefan Hajnoczi --- .gitignore | 1 + Makefile | 3 +- test-coroutine.c | 162 +++++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 165 insertions(+), 1 deletion(-) create mode 100644 test-coroutine.c diff --git a/.gitignore b/.gitignore index 54835bcb97..59c343c414 100644 --- a/.gitignore +++ b/.gitignore @@ -36,6 +36,7 @@ qemu-io qemu-ga qemu-monitor.texi QMP/qmp-commands.txt +test-coroutine .gdbinit *.a *.aux diff --git a/Makefile b/Makefile index 48552512d6..2becedcf88 100644 --- a/Makefile +++ b/Makefile @@ -151,7 +151,7 @@ qemu-io$(EXESUF): qemu-io.o cmd.o qemu-tool.o qemu-error.o $(oslib-obj-y) $(trac qemu-img-cmds.h: $(SRC_PATH)/qemu-img-cmds.hx $(call quiet-command,sh $(SRC_PATH)/scripts/hxtool -h < $< > $@," GEN $@") -check-qint.o check-qstring.o check-qdict.o check-qlist.o check-qfloat.o check-qjson.o: $(GENERATED_HEADERS) +check-qint.o check-qstring.o check-qdict.o check-qlist.o check-qfloat.o check-qjson.o test-coroutine.o: $(GENERATED_HEADERS) CHECK_PROG_DEPS = qemu-malloc.o $(oslib-obj-y) $(trace-obj-y) qemu-tool.o @@ -161,6 +161,7 @@ check-qdict: check-qdict.o qdict.o qfloat.o qint.o qstring.o qbool.o qlist.o $(C check-qlist: check-qlist.o qlist.o qint.o $(CHECK_PROG_DEPS) check-qfloat: check-qfloat.o qfloat.o $(CHECK_PROG_DEPS) check-qjson: check-qjson.o qfloat.o qint.o qdict.o qstring.o qlist.o qbool.o qjson.o json-streamer.o json-lexer.o json-parser.o error.o qerror.o qemu-error.o $(CHECK_PROG_DEPS) +test-coroutine: test-coroutine.o qemu-timer-common.o async.o $(coroutine-obj-y) $(CHECK_PROG_DEPS) $(qapi-obj-y): $(GENERATED_HEADERS) qapi-dir := qapi-generated diff --git a/test-coroutine.c b/test-coroutine.c new file mode 100644 index 0000000000..9e9d3c95bc --- /dev/null +++ b/test-coroutine.c @@ -0,0 +1,162 @@ +/* + * Coroutine tests + * + * Copyright IBM, Corp. 2011 + * + * Authors: + * Stefan Hajnoczi + * + * This work is licensed under the terms of the GNU LGPL, version 2 or later. + * See the COPYING.LIB file in the top-level directory. + * + */ + +#include +#include "qemu-coroutine.h" + +/* + * Check that qemu_in_coroutine() works + */ + +static void coroutine_fn verify_in_coroutine(void *opaque) +{ + g_assert(qemu_in_coroutine()); +} + +static void test_in_coroutine(void) +{ + Coroutine *coroutine; + + g_assert(!qemu_in_coroutine()); + + coroutine = qemu_coroutine_create(verify_in_coroutine); + qemu_coroutine_enter(coroutine, NULL); +} + +/* + * Check that qemu_coroutine_self() works + */ + +static void coroutine_fn verify_self(void *opaque) +{ + g_assert(qemu_coroutine_self() == opaque); +} + +static void test_self(void) +{ + Coroutine *coroutine; + + coroutine = qemu_coroutine_create(verify_self); + qemu_coroutine_enter(coroutine, coroutine); +} + +/* + * Check that coroutines may nest multiple levels + */ + +typedef struct { + unsigned int n_enter; /* num coroutines entered */ + unsigned int n_return; /* num coroutines returned */ + unsigned int max; /* maximum level of nesting */ +} NestData; + +static void coroutine_fn nest(void *opaque) +{ + NestData *nd = opaque; + + nd->n_enter++; + + if (nd->n_enter < nd->max) { + Coroutine *child; + + child = qemu_coroutine_create(nest); + qemu_coroutine_enter(child, nd); + } + + nd->n_return++; +} + +static void test_nesting(void) +{ + Coroutine *root; + NestData nd = { + .n_enter = 0, + .n_return = 0, + .max = 128, + }; + + root = qemu_coroutine_create(nest); + qemu_coroutine_enter(root, &nd); + + /* Must enter and return from max nesting level */ + g_assert_cmpint(nd.n_enter, ==, nd.max); + g_assert_cmpint(nd.n_return, ==, nd.max); +} + +/* + * Check that yield/enter transfer control correctly + */ + +static void coroutine_fn yield_5_times(void *opaque) +{ + bool *done = opaque; + int i; + + for (i = 0; i < 5; i++) { + qemu_coroutine_yield(); + } + *done = true; +} + +static void test_yield(void) +{ + Coroutine *coroutine; + bool done = false; + int i = -1; /* one extra time to return from coroutine */ + + coroutine = qemu_coroutine_create(yield_5_times); + while (!done) { + qemu_coroutine_enter(coroutine, &done); + i++; + } + g_assert_cmpint(i, ==, 5); /* coroutine must yield 5 times */ +} + +/* + * Check that creation, enter, and return work + */ + +static void coroutine_fn set_and_exit(void *opaque) +{ + bool *done = opaque; + + *done = true; +} + +static void test_lifecycle(void) +{ + Coroutine *coroutine; + bool done = false; + + /* Create, enter, and return from coroutine */ + coroutine = qemu_coroutine_create(set_and_exit); + qemu_coroutine_enter(coroutine, &done); + g_assert(done); /* expect done to be true (first time) */ + + /* Repeat to check that no state affects this test */ + done = false; + coroutine = qemu_coroutine_create(set_and_exit); + qemu_coroutine_enter(coroutine, &done); + g_assert(done); /* expect done to be true (second time) */ +} + +int main(int argc, char **argv) +{ + g_test_init(&argc, &argv, NULL); + g_test_add_func("/basic/lifecycle", test_lifecycle); + g_test_add_func("/basic/yield", test_yield); + g_test_add_func("/basic/nesting", test_nesting); + g_test_add_func("/basic/self", test_self); + g_test_add_func("/basic/in_coroutine", test_in_coroutine); + return g_test_run(); +} From 5e3840ce24040cbd1957008489cbc136c43ca391 Mon Sep 17 00:00:00 2001 From: Stefan Hajnoczi Date: Thu, 12 May 2011 08:27:39 +0100 Subject: [PATCH 177/230] coroutine: add test-coroutine --benchmark-lifecycle Add a microbenchmark for coroutine create, enter, and return (aka lifecycle). This is a useful benchmark because users are expected to create many coroutines, one per I/O request for example, and we therefore need to provide good performance in that scenario. To run: make test-coroutine ./test-coroutine --benchmark-lifecycle 20000000 This will do 20,000,000 coroutine create, enter, return iterations and print the resulting time. Signed-off-by: Stefan Hajnoczi --- test-coroutine.c | 30 ++++++++++++++++++++++++++++++ 1 file changed, 30 insertions(+) diff --git a/test-coroutine.c b/test-coroutine.c index 9e9d3c95bc..bf9f3e91b5 100644 --- a/test-coroutine.c +++ b/test-coroutine.c @@ -150,6 +150,33 @@ static void test_lifecycle(void) g_assert(done); /* expect done to be true (second time) */ } +/* + * Lifecycle benchmark + */ + +static void coroutine_fn empty_coroutine(void *opaque) +{ + /* Do nothing */ +} + +static void perf_lifecycle(void) +{ + Coroutine *coroutine; + unsigned int i, max; + double duration; + + max = 1000000; + + g_test_timer_start(); + for (i = 0; i < max; i++) { + coroutine = qemu_coroutine_create(empty_coroutine); + qemu_coroutine_enter(coroutine, NULL); + } + duration = g_test_timer_elapsed(); + + g_test_message("Lifecycle %u iterations: %f s\n", max, duration); +} + int main(int argc, char **argv) { g_test_init(&argc, &argv, NULL); @@ -158,5 +185,8 @@ int main(int argc, char **argv) g_test_add_func("/basic/nesting", test_nesting); g_test_add_func("/basic/self", test_self); g_test_add_func("/basic/in_coroutine", test_in_coroutine); + if (g_test_perf()) { + g_test_add_func("/perf/lifecycle", perf_lifecycle); + } return g_test_run(); } From da1fa91d6cca8a6d3da9c2b222fa485429db297c Mon Sep 17 00:00:00 2001 From: Kevin Wolf Date: Thu, 14 Jul 2011 17:27:13 +0200 Subject: [PATCH 178/230] block: Add bdrv_co_readv/writev Add new block driver callbacks bdrv_co_readv/writev, which work on a QEMUIOVector like bdrv_aio_*, but don't need a callback. The function may only be called inside a coroutine, so a block driver implementing this interface can yield instead of blocking during I/O. Signed-off-by: Kevin Wolf --- Makefile.objs | 2 +- block.c | 45 +++++++++++++++++++++++++++++++++++++++++++++ block.h | 5 +++++ block_int.h | 6 ++++++ trace-events | 2 ++ 5 files changed, 59 insertions(+), 1 deletion(-) diff --git a/Makefile.objs b/Makefile.objs index 5679e1fa06..9549e2a16f 100644 --- a/Makefile.objs +++ b/Makefile.objs @@ -25,6 +25,7 @@ coroutine-obj-$(CONFIG_WIN32) += coroutine-win32.o block-obj-y = cutils.o cache-utils.o qemu-malloc.o qemu-option.o module.o async.o block-obj-y += nbd.o block.o aio.o aes.o qemu-config.o qemu-progress.o qemu-sockets.o +block-obj-y += $(coroutine-obj-y) block-obj-$(CONFIG_POSIX) += posix-aio-compat.o block-obj-$(CONFIG_LINUX_AIO) += linux-aio.o @@ -79,7 +80,6 @@ common-obj-y += readline.o console.o cursor.o qemu-error.o common-obj-y += $(oslib-obj-y) common-obj-$(CONFIG_WIN32) += os-win32.o common-obj-$(CONFIG_POSIX) += os-posix.o -common-obj-y += $(coroutine-obj-y) common-obj-y += tcg-runtime.o host-utils.o common-obj-y += irq.o ioport.o input.o diff --git a/block.c b/block.c index 4c66b2cf40..1329299ed7 100644 --- a/block.c +++ b/block.c @@ -1110,6 +1110,51 @@ int bdrv_pwrite_sync(BlockDriverState *bs, int64_t offset, return 0; } +int coroutine_fn bdrv_co_readv(BlockDriverState *bs, int64_t sector_num, + int nb_sectors, QEMUIOVector *qiov) +{ + BlockDriver *drv = bs->drv; + + trace_bdrv_co_readv(bs, sector_num, nb_sectors); + + if (!drv) { + return -ENOMEDIUM; + } + if (bdrv_check_request(bs, sector_num, nb_sectors)) { + return -EIO; + } + + return drv->bdrv_co_readv(bs, sector_num, nb_sectors, qiov); +} + +int coroutine_fn bdrv_co_writev(BlockDriverState *bs, int64_t sector_num, + int nb_sectors, QEMUIOVector *qiov) +{ + BlockDriver *drv = bs->drv; + + trace_bdrv_co_writev(bs, sector_num, nb_sectors); + + if (!bs->drv) { + return -ENOMEDIUM; + } + if (bs->read_only) { + return -EACCES; + } + if (bdrv_check_request(bs, sector_num, nb_sectors)) { + return -EIO; + } + + if (bs->dirty_bitmap) { + set_dirty_bitmap(bs, sector_num, nb_sectors, 1); + } + + if (bs->wr_highest_sector < sector_num + nb_sectors - 1) { + bs->wr_highest_sector = sector_num + nb_sectors - 1; + } + + return drv->bdrv_co_writev(bs, sector_num, nb_sectors, qiov); +} + /** * Truncate file to 'offset' bytes (needed only for file protocols) */ diff --git a/block.h b/block.h index e672bc669a..a3bfaafef0 100644 --- a/block.h +++ b/block.h @@ -4,6 +4,7 @@ #include "qemu-aio.h" #include "qemu-common.h" #include "qemu-option.h" +#include "qemu-coroutine.h" #include "qobject.h" /* block.c */ @@ -85,6 +86,10 @@ int bdrv_pwrite(BlockDriverState *bs, int64_t offset, const void *buf, int count); int bdrv_pwrite_sync(BlockDriverState *bs, int64_t offset, const void *buf, int count); +int coroutine_fn bdrv_co_readv(BlockDriverState *bs, int64_t sector_num, + int nb_sectors, QEMUIOVector *qiov); +int coroutine_fn bdrv_co_writev(BlockDriverState *bs, int64_t sector_num, + int nb_sectors, QEMUIOVector *qiov); int bdrv_truncate(BlockDriverState *bs, int64_t offset); int64_t bdrv_getlength(BlockDriverState *bs); int64_t bdrv_get_allocated_file_size(BlockDriverState *bs); diff --git a/block_int.h b/block_int.h index efefbee289..f6d02b38a7 100644 --- a/block_int.h +++ b/block_int.h @@ -27,6 +27,7 @@ #include "block.h" #include "qemu-option.h" #include "qemu-queue.h" +#include "qemu-coroutine.h" #define BLOCK_FLAG_ENCRYPT 1 #define BLOCK_FLAG_COMPAT6 4 @@ -77,6 +78,11 @@ struct BlockDriver { int (*bdrv_discard)(BlockDriverState *bs, int64_t sector_num, int nb_sectors); + int coroutine_fn (*bdrv_co_readv)(BlockDriverState *bs, + int64_t sector_num, int nb_sectors, QEMUIOVector *qiov); + int coroutine_fn (*bdrv_co_writev)(BlockDriverState *bs, + int64_t sector_num, int nb_sectors, QEMUIOVector *qiov); + int (*bdrv_aio_multiwrite)(BlockDriverState *bs, BlockRequest *reqs, int num_reqs); int (*bdrv_merge_requests)(BlockDriverState *bs, BlockRequest* a, diff --git a/trace-events b/trace-events index 136f7759f2..46bceca1bc 100644 --- a/trace-events +++ b/trace-events @@ -66,6 +66,8 @@ disable bdrv_aio_flush(void *bs, void *opaque) "bs %p opaque %p" disable bdrv_aio_readv(void *bs, int64_t sector_num, int nb_sectors, void *opaque) "bs %p sector_num %"PRId64" nb_sectors %d opaque %p" disable bdrv_aio_writev(void *bs, int64_t sector_num, int nb_sectors, void *opaque) "bs %p sector_num %"PRId64" nb_sectors %d opaque %p" disable bdrv_set_locked(void *bs, int locked) "bs %p locked %d" +disable bdrv_co_readv(void *bs, int64_t sector_num, int nb_sector) "bs %p sector_num %"PRId64" nb_sectors %d" +disable bdrv_co_writev(void *bs, int64_t sector_num, int nb_sector) "bs %p sector_num %"PRId64" nb_sectors %d" # hw/virtio-blk.c disable virtio_blk_req_complete(void *req, int status) "req %p status %d" From 68485420187094c26f86faee5c7f68b5d6a03603 Mon Sep 17 00:00:00 2001 From: Kevin Wolf Date: Thu, 30 Jun 2011 10:05:46 +0200 Subject: [PATCH 179/230] block: Emulate AIO functions with bdrv_co_readv/writev Use the bdrv_co_readv/writev callbacks to implement bdrv_aio_readv/writev and bdrv_read/write if a driver provides the coroutine version instead of the synchronous or AIO version. Signed-off-by: Kevin Wolf --- block.c | 98 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 97 insertions(+), 1 deletion(-) diff --git a/block.c b/block.c index 1329299ed7..0d973e6e8e 100644 --- a/block.c +++ b/block.c @@ -28,6 +28,7 @@ #include "block_int.h" #include "module.h" #include "qemu-objects.h" +#include "qemu-coroutine.h" #ifdef CONFIG_BSD #include @@ -57,6 +58,12 @@ static int bdrv_read_em(BlockDriverState *bs, int64_t sector_num, uint8_t *buf, int nb_sectors); static int bdrv_write_em(BlockDriverState *bs, int64_t sector_num, const uint8_t *buf, int nb_sectors); +static BlockDriverAIOCB *bdrv_co_aio_readv_em(BlockDriverState *bs, + int64_t sector_num, QEMUIOVector *qiov, int nb_sectors, + BlockDriverCompletionFunc *cb, void *opaque); +static BlockDriverAIOCB *bdrv_co_aio_writev_em(BlockDriverState *bs, + int64_t sector_num, QEMUIOVector *qiov, int nb_sectors, + BlockDriverCompletionFunc *cb, void *opaque); static QTAILQ_HEAD(, BlockDriverState) bdrv_states = QTAILQ_HEAD_INITIALIZER(bdrv_states); @@ -169,7 +176,13 @@ void path_combine(char *dest, int dest_size, void bdrv_register(BlockDriver *bdrv) { - if (!bdrv->bdrv_aio_readv) { + if (bdrv->bdrv_co_readv) { + /* Emulate AIO by coroutines, and sync by AIO */ + bdrv->bdrv_aio_readv = bdrv_co_aio_readv_em; + bdrv->bdrv_aio_writev = bdrv_co_aio_writev_em; + bdrv->bdrv_read = bdrv_read_em; + bdrv->bdrv_write = bdrv_write_em; + } else if (!bdrv->bdrv_aio_readv) { /* add AIO emulation layer */ bdrv->bdrv_aio_readv = bdrv_aio_readv_em; bdrv->bdrv_aio_writev = bdrv_aio_writev_em; @@ -2614,6 +2627,89 @@ static BlockDriverAIOCB *bdrv_aio_writev_em(BlockDriverState *bs, return bdrv_aio_rw_vector(bs, sector_num, qiov, nb_sectors, cb, opaque, 1); } + +typedef struct BlockDriverAIOCBCoroutine { + BlockDriverAIOCB common; + BlockRequest req; + bool is_write; + QEMUBH* bh; +} BlockDriverAIOCBCoroutine; + +static void bdrv_aio_co_cancel_em(BlockDriverAIOCB *blockacb) +{ + qemu_aio_flush(); +} + +static AIOPool bdrv_em_co_aio_pool = { + .aiocb_size = sizeof(BlockDriverAIOCBCoroutine), + .cancel = bdrv_aio_co_cancel_em, +}; + +static void bdrv_co_rw_bh(void *opaque) +{ + BlockDriverAIOCBCoroutine *acb = opaque; + + acb->common.cb(acb->common.opaque, acb->req.error); + qemu_bh_delete(acb->bh); + qemu_aio_release(acb); +} + +static void coroutine_fn bdrv_co_rw(void *opaque) +{ + BlockDriverAIOCBCoroutine *acb = opaque; + BlockDriverState *bs = acb->common.bs; + + if (!acb->is_write) { + acb->req.error = bs->drv->bdrv_co_readv(bs, acb->req.sector, + acb->req.nb_sectors, acb->req.qiov); + } else { + acb->req.error = bs->drv->bdrv_co_writev(bs, acb->req.sector, + acb->req.nb_sectors, acb->req.qiov); + } + + acb->bh = qemu_bh_new(bdrv_co_rw_bh, acb); + qemu_bh_schedule(acb->bh); +} + +static BlockDriverAIOCB *bdrv_co_aio_rw_vector(BlockDriverState *bs, + int64_t sector_num, + QEMUIOVector *qiov, + int nb_sectors, + BlockDriverCompletionFunc *cb, + void *opaque, + bool is_write) +{ + Coroutine *co; + BlockDriverAIOCBCoroutine *acb; + + acb = qemu_aio_get(&bdrv_em_co_aio_pool, bs, cb, opaque); + acb->req.sector = sector_num; + acb->req.nb_sectors = nb_sectors; + acb->req.qiov = qiov; + acb->is_write = is_write; + + co = qemu_coroutine_create(bdrv_co_rw); + qemu_coroutine_enter(co, acb); + + return &acb->common; +} + +static BlockDriverAIOCB *bdrv_co_aio_readv_em(BlockDriverState *bs, + int64_t sector_num, QEMUIOVector *qiov, int nb_sectors, + BlockDriverCompletionFunc *cb, void *opaque) +{ + return bdrv_co_aio_rw_vector(bs, sector_num, qiov, nb_sectors, cb, opaque, + false); +} + +static BlockDriverAIOCB *bdrv_co_aio_writev_em(BlockDriverState *bs, + int64_t sector_num, QEMUIOVector *qiov, int nb_sectors, + BlockDriverCompletionFunc *cb, void *opaque) +{ + return bdrv_co_aio_rw_vector(bs, sector_num, qiov, nb_sectors, cb, opaque, + true); +} + static BlockDriverAIOCB *bdrv_aio_flush_em(BlockDriverState *bs, BlockDriverCompletionFunc *cb, void *opaque) { From f9f05dc58c50d19ad762e6c1ce6b5def9814a4ed Mon Sep 17 00:00:00 2001 From: Kevin Wolf Date: Fri, 15 Jul 2011 13:50:26 +0200 Subject: [PATCH 180/230] block: Add bdrv_co_readv/writev emulation In order to be able to call bdrv_co_readv/writev for drivers that don't implement the functions natively, add an emulation that uses the AIO functions to implement them. Signed-off-by: Kevin Wolf --- block.c | 83 +++++++++++++++++++++++++++++++++++++++++++++++----- trace-events | 1 + 2 files changed, 76 insertions(+), 8 deletions(-) diff --git a/block.c b/block.c index 0d973e6e8e..e6abea85df 100644 --- a/block.c +++ b/block.c @@ -64,6 +64,12 @@ static BlockDriverAIOCB *bdrv_co_aio_readv_em(BlockDriverState *bs, static BlockDriverAIOCB *bdrv_co_aio_writev_em(BlockDriverState *bs, int64_t sector_num, QEMUIOVector *qiov, int nb_sectors, BlockDriverCompletionFunc *cb, void *opaque); +static int coroutine_fn bdrv_co_readv_em(BlockDriverState *bs, + int64_t sector_num, int nb_sectors, + QEMUIOVector *iov); +static int coroutine_fn bdrv_co_writev_em(BlockDriverState *bs, + int64_t sector_num, int nb_sectors, + QEMUIOVector *iov); static QTAILQ_HEAD(, BlockDriverState) bdrv_states = QTAILQ_HEAD_INITIALIZER(bdrv_states); @@ -182,14 +188,19 @@ void bdrv_register(BlockDriver *bdrv) bdrv->bdrv_aio_writev = bdrv_co_aio_writev_em; bdrv->bdrv_read = bdrv_read_em; bdrv->bdrv_write = bdrv_write_em; - } else if (!bdrv->bdrv_aio_readv) { - /* add AIO emulation layer */ - bdrv->bdrv_aio_readv = bdrv_aio_readv_em; - bdrv->bdrv_aio_writev = bdrv_aio_writev_em; - } else if (!bdrv->bdrv_read) { - /* add synchronous IO emulation layer */ - bdrv->bdrv_read = bdrv_read_em; - bdrv->bdrv_write = bdrv_write_em; + } else { + bdrv->bdrv_co_readv = bdrv_co_readv_em; + bdrv->bdrv_co_writev = bdrv_co_writev_em; + + if (!bdrv->bdrv_aio_readv) { + /* add AIO emulation layer */ + bdrv->bdrv_aio_readv = bdrv_aio_readv_em; + bdrv->bdrv_aio_writev = bdrv_aio_writev_em; + } else if (!bdrv->bdrv_read) { + /* add synchronous IO emulation layer */ + bdrv->bdrv_read = bdrv_read_em; + bdrv->bdrv_write = bdrv_write_em; + } } if (!bdrv->bdrv_aio_flush) @@ -2855,6 +2866,62 @@ void qemu_aio_release(void *p) pool->free_aiocb = acb; } +/**************************************************************/ +/* Coroutine block device emulation */ + +typedef struct CoroutineIOCompletion { + Coroutine *coroutine; + int ret; +} CoroutineIOCompletion; + +static void bdrv_co_io_em_complete(void *opaque, int ret) +{ + CoroutineIOCompletion *co = opaque; + + co->ret = ret; + qemu_coroutine_enter(co->coroutine, NULL); +} + +static int coroutine_fn bdrv_co_io_em(BlockDriverState *bs, int64_t sector_num, + int nb_sectors, QEMUIOVector *iov, + bool is_write) +{ + CoroutineIOCompletion co = { + .coroutine = qemu_coroutine_self(), + }; + BlockDriverAIOCB *acb; + + if (is_write) { + acb = bdrv_aio_writev(bs, sector_num, iov, nb_sectors, + bdrv_co_io_em_complete, &co); + } else { + acb = bdrv_aio_readv(bs, sector_num, iov, nb_sectors, + bdrv_co_io_em_complete, &co); + } + + trace_bdrv_co_io(is_write, acb); + if (!acb) { + return -EIO; + } + qemu_coroutine_yield(); + + return co.ret; +} + +static int coroutine_fn bdrv_co_readv_em(BlockDriverState *bs, + int64_t sector_num, int nb_sectors, + QEMUIOVector *iov) +{ + return bdrv_co_io_em(bs, sector_num, nb_sectors, iov, false); +} + +static int coroutine_fn bdrv_co_writev_em(BlockDriverState *bs, + int64_t sector_num, int nb_sectors, + QEMUIOVector *iov) +{ + return bdrv_co_io_em(bs, sector_num, nb_sectors, iov, true); +} + /**************************************************************/ /* removable device support */ diff --git a/trace-events b/trace-events index 46bceca1bc..bc9be3010b 100644 --- a/trace-events +++ b/trace-events @@ -68,6 +68,7 @@ disable bdrv_aio_writev(void *bs, int64_t sector_num, int nb_sectors, void *opaq disable bdrv_set_locked(void *bs, int locked) "bs %p locked %d" disable bdrv_co_readv(void *bs, int64_t sector_num, int nb_sector) "bs %p sector_num %"PRId64" nb_sectors %d" disable bdrv_co_writev(void *bs, int64_t sector_num, int nb_sector) "bs %p sector_num %"PRId64" nb_sectors %d" +disable bdrv_co_io(int is_write, void *acb) "is_write %d acb %p" # hw/virtio-blk.c disable virtio_blk_req_complete(void *req, int status) "req %p status %d" From b96e92470ab4a87268e8b174602eaea6c508003b Mon Sep 17 00:00:00 2001 From: Kevin Wolf Date: Thu, 30 Jun 2011 17:56:46 +0200 Subject: [PATCH 181/230] coroutines: Locks Signed-off-by: Kevin Wolf --- Makefile.objs | 2 +- qemu-coroutine-int.h | 1 + qemu-coroutine-lock.c | 124 ++++++++++++++++++++++++++++++++++++++++++ qemu-coroutine.h | 64 ++++++++++++++++++++++ trace-events | 8 +++ 5 files changed, 198 insertions(+), 1 deletion(-) create mode 100644 qemu-coroutine-lock.c diff --git a/Makefile.objs b/Makefile.objs index 9549e2a16f..89ca3611b3 100644 --- a/Makefile.objs +++ b/Makefile.objs @@ -12,7 +12,7 @@ oslib-obj-$(CONFIG_POSIX) += oslib-posix.o qemu-thread-posix.o ####################################################################### # coroutines -coroutine-obj-y = qemu-coroutine.o +coroutine-obj-y = qemu-coroutine.o qemu-coroutine-lock.o ifeq ($(CONFIG_UCONTEXT_COROUTINE),y) coroutine-obj-$(CONFIG_POSIX) += coroutine-ucontext.o else diff --git a/qemu-coroutine-int.h b/qemu-coroutine-int.h index 64915c2fa5..d495615cf6 100644 --- a/qemu-coroutine-int.h +++ b/qemu-coroutine-int.h @@ -38,6 +38,7 @@ struct Coroutine { void *entry_arg; Coroutine *caller; QLIST_ENTRY(Coroutine) pool_next; + QTAILQ_ENTRY(Coroutine) co_queue_next; }; Coroutine *qemu_coroutine_new(void); diff --git a/qemu-coroutine-lock.c b/qemu-coroutine-lock.c new file mode 100644 index 0000000000..abaa1f7967 --- /dev/null +++ b/qemu-coroutine-lock.c @@ -0,0 +1,124 @@ +/* + * coroutine queues and locks + * + * Copyright (c) 2011 Kevin Wolf + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +#include "qemu-common.h" +#include "qemu-coroutine.h" +#include "qemu-coroutine-int.h" +#include "qemu-queue.h" +#include "trace.h" + +static QTAILQ_HEAD(, Coroutine) unlock_bh_queue = + QTAILQ_HEAD_INITIALIZER(unlock_bh_queue); + +struct unlock_bh { + QEMUBH *bh; +}; + +static void qemu_co_queue_next_bh(void *opaque) +{ + struct unlock_bh *unlock_bh = opaque; + Coroutine *next; + + trace_qemu_co_queue_next_bh(); + while ((next = QTAILQ_FIRST(&unlock_bh_queue))) { + QTAILQ_REMOVE(&unlock_bh_queue, next, co_queue_next); + qemu_coroutine_enter(next, NULL); + } + + qemu_bh_delete(unlock_bh->bh); + qemu_free(unlock_bh); +} + +void qemu_co_queue_init(CoQueue *queue) +{ + QTAILQ_INIT(&queue->entries); +} + +void coroutine_fn qemu_co_queue_wait(CoQueue *queue) +{ + Coroutine *self = qemu_coroutine_self(); + QTAILQ_INSERT_TAIL(&queue->entries, self, co_queue_next); + qemu_coroutine_yield(); + assert(qemu_in_coroutine()); +} + +bool qemu_co_queue_next(CoQueue *queue) +{ + struct unlock_bh *unlock_bh; + Coroutine *next; + + next = QTAILQ_FIRST(&queue->entries); + if (next) { + QTAILQ_REMOVE(&queue->entries, next, co_queue_next); + QTAILQ_INSERT_TAIL(&unlock_bh_queue, next, co_queue_next); + trace_qemu_co_queue_next(next); + + unlock_bh = qemu_malloc(sizeof(*unlock_bh)); + unlock_bh->bh = qemu_bh_new(qemu_co_queue_next_bh, unlock_bh); + qemu_bh_schedule(unlock_bh->bh); + } + + return (next != NULL); +} + +bool qemu_co_queue_empty(CoQueue *queue) +{ + return (QTAILQ_FIRST(&queue->entries) == NULL); +} + +void qemu_co_mutex_init(CoMutex *mutex) +{ + memset(mutex, 0, sizeof(*mutex)); + qemu_co_queue_init(&mutex->queue); +} + +void coroutine_fn qemu_co_mutex_lock(CoMutex *mutex) +{ + Coroutine *self = qemu_coroutine_self(); + + trace_qemu_co_mutex_lock_entry(mutex, self); + + while (mutex->locked) { + qemu_co_queue_wait(&mutex->queue); + } + + mutex->locked = true; + + trace_qemu_co_mutex_lock_return(mutex, self); +} + +void coroutine_fn qemu_co_mutex_unlock(CoMutex *mutex) +{ + Coroutine *self = qemu_coroutine_self(); + + trace_qemu_co_mutex_unlock_entry(mutex, self); + + assert(mutex->locked == true); + assert(qemu_in_coroutine()); + + mutex->locked = false; + qemu_co_queue_next(&mutex->queue); + + trace_qemu_co_mutex_unlock_return(mutex, self); +} diff --git a/qemu-coroutine.h b/qemu-coroutine.h index 08255c7c41..2f2fd95552 100644 --- a/qemu-coroutine.h +++ b/qemu-coroutine.h @@ -5,6 +5,7 @@ * * Authors: * Stefan Hajnoczi + * Kevin Wolf * * This work is licensed under the terms of the GNU LGPL, version 2 or later. * See the COPYING.LIB file in the top-level directory. @@ -15,6 +16,7 @@ #define QEMU_COROUTINE_H #include +#include "qemu-queue.h" /** * Coroutines are a mechanism for stack switching and can be used for @@ -92,4 +94,66 @@ Coroutine *coroutine_fn qemu_coroutine_self(void); */ bool qemu_in_coroutine(void); + + +/** + * CoQueues are a mechanism to queue coroutines in order to continue executing + * them later. They provide the fundamental primitives on which coroutine locks + * are built. + */ +typedef struct CoQueue { + QTAILQ_HEAD(, Coroutine) entries; +} CoQueue; + +/** + * Initialise a CoQueue. This must be called before any other operation is used + * on the CoQueue. + */ +void qemu_co_queue_init(CoQueue *queue); + +/** + * Adds the current coroutine to the CoQueue and transfers control to the + * caller of the coroutine. + */ +void coroutine_fn qemu_co_queue_wait(CoQueue *queue); + +/** + * Restarts the next coroutine in the CoQueue and removes it from the queue. + * + * Returns true if a coroutine was restarted, false if the queue is empty. + */ +bool qemu_co_queue_next(CoQueue *queue); + +/** + * Checks if the CoQueue is empty. + */ +bool qemu_co_queue_empty(CoQueue *queue); + + +/** + * Provides a mutex that can be used to synchronise coroutines + */ +typedef struct CoMutex { + bool locked; + CoQueue queue; +} CoMutex; + +/** + * Initialises a CoMutex. This must be called before any other operation is used + * on the CoMutex. + */ +void qemu_co_mutex_init(CoMutex *mutex); + +/** + * Locks the mutex. If the lock cannot be taken immediately, control is + * transferred to the caller of the current coroutine. + */ +void coroutine_fn qemu_co_mutex_lock(CoMutex *mutex); + +/** + * Unlocks the mutex and schedules the next coroutine that was waiting for this + * lock to be run. + */ +void coroutine_fn qemu_co_mutex_unlock(CoMutex *mutex); + #endif /* QEMU_COROUTINE_H */ diff --git a/trace-events b/trace-events index bc9be3010b..19d31e3541 100644 --- a/trace-events +++ b/trace-events @@ -433,3 +433,11 @@ disable xen_platform_log(char *s) "xen platform: %s" disable qemu_coroutine_enter(void *from, void *to, void *opaque) "from %p to %p opaque %p" disable qemu_coroutine_yield(void *from, void *to) "from %p to %p" disable qemu_coroutine_terminate(void *co) "self %p" + +# qemu-coroutine-lock.c +disable qemu_co_queue_next_bh(void) "" +disable qemu_co_queue_next(void *next) "next %p" +disable qemu_co_mutex_lock_entry(void *mutex, void *self) "mutex %p self %p" +disable qemu_co_mutex_lock_return(void *mutex, void *self) "mutex %p self %p" +disable qemu_co_mutex_unlock_entry(void *mutex, void *self) "mutex %p self %p" +disable qemu_co_mutex_unlock_return(void *mutex, void *self) "mutex %p self %p" From 68d100e905453ebbeea8e915f4f18a2bd4339fe8 Mon Sep 17 00:00:00 2001 From: Kevin Wolf Date: Thu, 30 Jun 2011 17:42:09 +0200 Subject: [PATCH 182/230] qcow2: Use coroutines Signed-off-by: Kevin Wolf --- block/qcow2-cluster.c | 26 +++-- block/qcow2.c | 240 +++++++++++++++--------------------------- block/qcow2.h | 5 +- 3 files changed, 102 insertions(+), 169 deletions(-) diff --git a/block/qcow2-cluster.c b/block/qcow2-cluster.c index 882f50a80b..81cf77d83c 100644 --- a/block/qcow2-cluster.c +++ b/block/qcow2-cluster.c @@ -697,12 +697,12 @@ err: * m->depends_on is set to NULL and the other fields in m are meaningless. * * If the cluster is newly allocated, m->nb_clusters is set to the number of - * contiguous clusters that have been allocated. This may be 0 if the request - * conflict with another write request in flight; in this case, m->depends_on - * is set and the remaining fields of m are meaningless. + * contiguous clusters that have been allocated. In this case, the other + * fields of m are valid and contain information about the first allocated + * cluster. * - * If m->nb_clusters is non-zero, the other fields of m are valid and contain - * information about the first allocated cluster. + * If the request conflicts with another write request in flight, the coroutine + * is queued and will be reentered when the dependency has completed. * * Return 0 on success and -errno in error cases */ @@ -721,6 +721,7 @@ int qcow2_alloc_cluster_offset(BlockDriverState *bs, uint64_t offset, return ret; } +again: nb_clusters = size_to_clusters(s, n_end << 9); nb_clusters = MIN(nb_clusters, s->l2_size - l2_index); @@ -792,12 +793,12 @@ int qcow2_alloc_cluster_offset(BlockDriverState *bs, uint64_t offset, } if (nb_clusters == 0) { - /* Set dependency and wait for a callback */ - m->depends_on = old_alloc; - m->nb_clusters = 0; - *num = 0; - - goto out_wait_dependency; + /* Wait for the dependency to complete. We need to recheck + * the free/allocated clusters when we continue. */ + qemu_co_mutex_unlock(&s->lock); + qemu_co_queue_wait(&old_alloc->dependent_requests); + qemu_co_mutex_lock(&s->lock); + goto again; } } } @@ -834,9 +835,6 @@ out: return 0; -out_wait_dependency: - return qcow2_cache_put(bs, s->l2_table_cache, (void**) &l2_table); - fail: qcow2_cache_put(bs, s->l2_table_cache, (void**) &l2_table); fail_put: diff --git a/block/qcow2.c b/block/qcow2.c index 48e1b95689..f07d550a96 100644 --- a/block/qcow2.c +++ b/block/qcow2.c @@ -276,6 +276,9 @@ static int qcow2_open(BlockDriverState *bs, int flags) goto fail; } + /* Initialise locks */ + qemu_co_mutex_init(&s->lock); + #ifdef DEBUG_ALLOC qcow2_check_refcounts(bs); #endif @@ -379,7 +382,6 @@ typedef struct QCowAIOCB { uint64_t cluster_offset; uint8_t *cluster_data; bool is_write; - BlockDriverAIOCB *hd_aiocb; QEMUIOVector hd_qiov; QEMUBH *bh; QCowL2Meta l2meta; @@ -389,8 +391,6 @@ typedef struct QCowAIOCB { static void qcow2_aio_cancel(BlockDriverAIOCB *blockacb) { QCowAIOCB *acb = container_of(blockacb, QCowAIOCB, common); - if (acb->hd_aiocb) - bdrv_aio_cancel(acb->hd_aiocb); qemu_aio_release(acb); } @@ -399,46 +399,16 @@ static AIOPool qcow2_aio_pool = { .cancel = qcow2_aio_cancel, }; -static void qcow2_aio_read_cb(void *opaque, int ret); -static void qcow2_aio_write_cb(void *opaque, int ret); - -static void qcow2_aio_rw_bh(void *opaque) +/* + * Returns 0 when the request is completed successfully, 1 when there is still + * a part left to do and -errno in error cases. + */ +static int qcow2_aio_read_cb(QCowAIOCB *acb) { - QCowAIOCB *acb = opaque; - qemu_bh_delete(acb->bh); - acb->bh = NULL; - - if (acb->is_write) { - qcow2_aio_write_cb(opaque, 0); - } else { - qcow2_aio_read_cb(opaque, 0); - } -} - -static int qcow2_schedule_bh(QEMUBHFunc *cb, QCowAIOCB *acb) -{ - if (acb->bh) - return -EIO; - - acb->bh = qemu_bh_new(cb, acb); - if (!acb->bh) - return -EIO; - - qemu_bh_schedule(acb->bh); - - return 0; -} - -static void qcow2_aio_read_cb(void *opaque, int ret) -{ - QCowAIOCB *acb = opaque; BlockDriverState *bs = acb->common.bs; BDRVQcowState *s = bs->opaque; int index_in_cluster, n1; - - acb->hd_aiocb = NULL; - if (ret < 0) - goto done; + int ret; /* post process the read buffer */ if (!acb->cluster_offset) { @@ -463,8 +433,7 @@ static void qcow2_aio_read_cb(void *opaque, int ret) if (acb->remaining_sectors == 0) { /* request completed */ - ret = 0; - goto done; + return 0; } /* prepare next AIO request */ @@ -477,7 +446,7 @@ static void qcow2_aio_read_cb(void *opaque, int ret) ret = qcow2_get_cluster_offset(bs, acb->sector_num << 9, &acb->cur_nr_sectors, &acb->cluster_offset); if (ret < 0) { - goto done; + return ret; } index_in_cluster = acb->sector_num & (s->cluster_sectors - 1); @@ -494,42 +463,35 @@ static void qcow2_aio_read_cb(void *opaque, int ret) acb->sector_num, acb->cur_nr_sectors); if (n1 > 0) { BLKDBG_EVENT(bs->file, BLKDBG_READ_BACKING_AIO); - acb->hd_aiocb = bdrv_aio_readv(bs->backing_hd, acb->sector_num, - &acb->hd_qiov, n1, qcow2_aio_read_cb, acb); - if (acb->hd_aiocb == NULL) { - ret = -EIO; - goto done; + qemu_co_mutex_unlock(&s->lock); + ret = bdrv_co_readv(bs->backing_hd, acb->sector_num, + n1, &acb->hd_qiov); + qemu_co_mutex_lock(&s->lock); + if (ret < 0) { + return ret; } - } else { - ret = qcow2_schedule_bh(qcow2_aio_rw_bh, acb); - if (ret < 0) - goto done; } + return 1; } else { /* Note: in this case, no need to wait */ qemu_iovec_memset(&acb->hd_qiov, 0, 512 * acb->cur_nr_sectors); - ret = qcow2_schedule_bh(qcow2_aio_rw_bh, acb); - if (ret < 0) - goto done; + return 1; } } else if (acb->cluster_offset & QCOW_OFLAG_COMPRESSED) { /* add AIO support for compressed blocks ? */ ret = qcow2_decompress_cluster(bs, acb->cluster_offset); if (ret < 0) { - goto done; + return ret; } qemu_iovec_from_buffer(&acb->hd_qiov, s->cluster_cache + index_in_cluster * 512, 512 * acb->cur_nr_sectors); - ret = qcow2_schedule_bh(qcow2_aio_rw_bh, acb); - if (ret < 0) - goto done; + return 1; } else { if ((acb->cluster_offset & 511) != 0) { - ret = -EIO; - goto done; + return -EIO; } if (s->crypt_method) { @@ -550,21 +512,17 @@ static void qcow2_aio_read_cb(void *opaque, int ret) } BLKDBG_EVENT(bs->file, BLKDBG_READ_AIO); - acb->hd_aiocb = bdrv_aio_readv(bs->file, + qemu_co_mutex_unlock(&s->lock); + ret = bdrv_co_readv(bs->file, (acb->cluster_offset >> 9) + index_in_cluster, - &acb->hd_qiov, acb->cur_nr_sectors, - qcow2_aio_read_cb, acb); - if (acb->hd_aiocb == NULL) { - ret = -EIO; - goto done; + acb->cur_nr_sectors, &acb->hd_qiov); + qemu_co_mutex_lock(&s->lock); + if (ret < 0) { + return ret; } } - return; -done: - acb->common.cb(acb->common.opaque, ret); - qemu_iovec_destroy(&acb->hd_qiov); - qemu_aio_release(acb); + return 1; } static QCowAIOCB *qcow2_aio_setup(BlockDriverState *bs, int64_t sector_num, @@ -577,7 +535,6 @@ static QCowAIOCB *qcow2_aio_setup(BlockDriverState *bs, int64_t sector_num, acb = qemu_aio_get(&qcow2_aio_pool, bs, cb, opaque); if (!acb) return NULL; - acb->hd_aiocb = NULL; acb->sector_num = sector_num; acb->qiov = qiov; acb->is_write = is_write; @@ -589,79 +546,73 @@ static QCowAIOCB *qcow2_aio_setup(BlockDriverState *bs, int64_t sector_num, acb->cur_nr_sectors = 0; acb->cluster_offset = 0; acb->l2meta.nb_clusters = 0; - QLIST_INIT(&acb->l2meta.dependent_requests); + qemu_co_queue_init(&acb->l2meta.dependent_requests); return acb; } -static BlockDriverAIOCB *qcow2_aio_readv(BlockDriverState *bs, - int64_t sector_num, - QEMUIOVector *qiov, int nb_sectors, - BlockDriverCompletionFunc *cb, - void *opaque) +static int qcow2_co_readv(BlockDriverState *bs, int64_t sector_num, + int nb_sectors, QEMUIOVector *qiov) { + BDRVQcowState *s = bs->opaque; QCowAIOCB *acb; int ret; - acb = qcow2_aio_setup(bs, sector_num, qiov, nb_sectors, cb, opaque, 0); - if (!acb) - return NULL; + acb = qcow2_aio_setup(bs, sector_num, qiov, nb_sectors, NULL, NULL, 0); - ret = qcow2_schedule_bh(qcow2_aio_rw_bh, acb); - if (ret < 0) { - qemu_iovec_destroy(&acb->hd_qiov); - qemu_aio_release(acb); - return NULL; - } + qemu_co_mutex_lock(&s->lock); + do { + ret = qcow2_aio_read_cb(acb); + } while (ret > 0); + qemu_co_mutex_unlock(&s->lock); - return &acb->common; + qemu_iovec_destroy(&acb->hd_qiov); + qemu_aio_release(acb); + + return ret; } -static void run_dependent_requests(QCowL2Meta *m) +static void run_dependent_requests(BDRVQcowState *s, QCowL2Meta *m) { - QCowAIOCB *req; - QCowAIOCB *next; - /* Take the request off the list of running requests */ if (m->nb_clusters != 0) { QLIST_REMOVE(m, next_in_flight); } /* Restart all dependent requests */ - QLIST_FOREACH_SAFE(req, &m->dependent_requests, next_depend, next) { - qcow2_aio_write_cb(req, 0); + if (!qemu_co_queue_empty(&m->dependent_requests)) { + qemu_co_mutex_unlock(&s->lock); + while(qemu_co_queue_next(&m->dependent_requests)); + qemu_co_mutex_lock(&s->lock); } - - /* Empty the list for the next part of the request */ - QLIST_INIT(&m->dependent_requests); } -static void qcow2_aio_write_cb(void *opaque, int ret) +/* + * Returns 0 when the request is completed successfully, 1 when there is still + * a part left to do and -errno in error cases. + */ +static int qcow2_aio_write_cb(QCowAIOCB *acb) { - QCowAIOCB *acb = opaque; BlockDriverState *bs = acb->common.bs; BDRVQcowState *s = bs->opaque; int index_in_cluster; int n_end; + int ret; - acb->hd_aiocb = NULL; + ret = qcow2_alloc_cluster_link_l2(bs, &acb->l2meta); - if (ret >= 0) { - ret = qcow2_alloc_cluster_link_l2(bs, &acb->l2meta); + run_dependent_requests(s, &acb->l2meta); + + if (ret < 0) { + return ret; } - run_dependent_requests(&acb->l2meta); - - if (ret < 0) - goto done; - acb->remaining_sectors -= acb->cur_nr_sectors; acb->sector_num += acb->cur_nr_sectors; acb->bytes_done += acb->cur_nr_sectors * 512; if (acb->remaining_sectors == 0) { /* request completed */ - ret = 0; - goto done; + return 0; } index_in_cluster = acb->sector_num & (s->cluster_sectors - 1); @@ -673,18 +624,10 @@ static void qcow2_aio_write_cb(void *opaque, int ret) ret = qcow2_alloc_cluster_offset(bs, acb->sector_num << 9, index_in_cluster, n_end, &acb->cur_nr_sectors, &acb->l2meta); if (ret < 0) { - goto done; + return ret; } acb->cluster_offset = acb->l2meta.cluster_offset; - - /* Need to wait for another request? If so, we are done for now. */ - if (acb->l2meta.nb_clusters == 0 && acb->l2meta.depends_on != NULL) { - QLIST_INSERT_HEAD(&acb->l2meta.depends_on->dependent_requests, - acb, next_depend); - return; - } - assert((acb->cluster_offset & 511) == 0); qemu_iovec_reset(&acb->hd_qiov); @@ -709,51 +652,40 @@ static void qcow2_aio_write_cb(void *opaque, int ret) } BLKDBG_EVENT(bs->file, BLKDBG_WRITE_AIO); - acb->hd_aiocb = bdrv_aio_writev(bs->file, - (acb->cluster_offset >> 9) + index_in_cluster, - &acb->hd_qiov, acb->cur_nr_sectors, - qcow2_aio_write_cb, acb); - if (acb->hd_aiocb == NULL) { - ret = -EIO; - goto fail; + qemu_co_mutex_unlock(&s->lock); + ret = bdrv_co_writev(bs->file, + (acb->cluster_offset >> 9) + index_in_cluster, + acb->cur_nr_sectors, &acb->hd_qiov); + qemu_co_mutex_lock(&s->lock); + if (ret < 0) { + return ret; } - return; - -fail: - if (acb->l2meta.nb_clusters != 0) { - QLIST_REMOVE(&acb->l2meta, next_in_flight); - } -done: - acb->common.cb(acb->common.opaque, ret); - qemu_iovec_destroy(&acb->hd_qiov); - qemu_aio_release(acb); + return 1; } -static BlockDriverAIOCB *qcow2_aio_writev(BlockDriverState *bs, - int64_t sector_num, - QEMUIOVector *qiov, int nb_sectors, - BlockDriverCompletionFunc *cb, - void *opaque) +static int qcow2_co_writev(BlockDriverState *bs, + int64_t sector_num, + int nb_sectors, + QEMUIOVector *qiov) { BDRVQcowState *s = bs->opaque; QCowAIOCB *acb; int ret; + acb = qcow2_aio_setup(bs, sector_num, qiov, nb_sectors, NULL, NULL, 1); s->cluster_cache_offset = -1; /* disable compressed cache */ - acb = qcow2_aio_setup(bs, sector_num, qiov, nb_sectors, cb, opaque, 1); - if (!acb) - return NULL; + qemu_co_mutex_lock(&s->lock); + do { + ret = qcow2_aio_write_cb(acb); + } while (ret > 0); + qemu_co_mutex_unlock(&s->lock); - ret = qcow2_schedule_bh(qcow2_aio_rw_bh, acb); - if (ret < 0) { - qemu_iovec_destroy(&acb->hd_qiov); - qemu_aio_release(acb); - return NULL; - } + qemu_iovec_destroy(&acb->hd_qiov); + qemu_aio_release(acb); - return &acb->common; + return ret; } static void qcow2_close(BlockDriverState *bs) @@ -881,7 +813,7 @@ static int preallocate(BlockDriverState *bs) nb_sectors = bdrv_getlength(bs) >> 9; offset = 0; - QLIST_INIT(&meta.dependent_requests); + qemu_co_queue_init(&meta.dependent_requests); meta.cluster_offset = 0; while (nb_sectors) { @@ -899,7 +831,7 @@ static int preallocate(BlockDriverState *bs) /* There are no dependent requests, but we need to remove our request * from the list of in-flight requests */ - run_dependent_requests(&meta); + run_dependent_requests(bs->opaque, &meta); /* TODO Preallocate data if requested */ @@ -1387,8 +1319,8 @@ static BlockDriver bdrv_qcow2 = { .bdrv_set_key = qcow2_set_key, .bdrv_make_empty = qcow2_make_empty, - .bdrv_aio_readv = qcow2_aio_readv, - .bdrv_aio_writev = qcow2_aio_writev, + .bdrv_co_readv = qcow2_co_readv, + .bdrv_co_writev = qcow2_co_writev, .bdrv_aio_flush = qcow2_aio_flush, .bdrv_discard = qcow2_discard, diff --git a/block/qcow2.h b/block/qcow2.h index 6a0a21b694..de23abe1a4 100644 --- a/block/qcow2.h +++ b/block/qcow2.h @@ -26,6 +26,7 @@ #define BLOCK_QCOW2_H #include "aes.h" +#include "qemu-coroutine.h" //#define DEBUG_ALLOC //#define DEBUG_ALLOC2 @@ -114,6 +115,8 @@ typedef struct BDRVQcowState { int64_t free_cluster_index; int64_t free_byte_offset; + CoMutex lock; + uint32_t crypt_method; /* current crypt method, 0 if no key yet */ uint32_t crypt_method_header; AES_KEY aes_encrypt_key; @@ -146,7 +149,7 @@ typedef struct QCowL2Meta int nb_available; int nb_clusters; struct QCowL2Meta *depends_on; - QLIST_HEAD(QCowAioDependencies, QCowAIOCB) dependent_requests; + CoQueue dependent_requests; QLIST_ENTRY(QCowL2Meta) next_in_flight; } QCowL2Meta; From 52b8eb60132b27ad53476490e9d7579003390cfa Mon Sep 17 00:00:00 2001 From: Kevin Wolf Date: Fri, 15 Jul 2011 16:27:42 +0200 Subject: [PATCH 183/230] qcow: Use coroutines The old qcow format is another user of the AsyncContext infrastructure. Converting it to coroutines (and therefore CoMutexes) allows to remove AsyncContexts. Signed-off-by: Kevin Wolf --- block/qcow.c | 186 ++++++++++++++++++--------------------------------- 1 file changed, 65 insertions(+), 121 deletions(-) diff --git a/block/qcow.c b/block/qcow.c index 227b104e36..6447c2a1c0 100644 --- a/block/qcow.c +++ b/block/qcow.c @@ -73,6 +73,7 @@ typedef struct BDRVQcowState { uint32_t crypt_method_header; AES_KEY aes_encrypt_key; AES_KEY aes_decrypt_key; + CoMutex lock; } BDRVQcowState; static int decompress_cluster(BlockDriverState *bs, uint64_t cluster_offset); @@ -517,11 +518,11 @@ static AIOPool qcow_aio_pool = { static QCowAIOCB *qcow_aio_setup(BlockDriverState *bs, int64_t sector_num, QEMUIOVector *qiov, int nb_sectors, - BlockDriverCompletionFunc *cb, void *opaque, int is_write) + int is_write) { QCowAIOCB *acb; - acb = qemu_aio_get(&qcow_aio_pool, bs, cb, opaque); + acb = qemu_aio_get(&qcow_aio_pool, bs, NULL, NULL); if (!acb) return NULL; acb->hd_aiocb = NULL; @@ -542,48 +543,15 @@ static QCowAIOCB *qcow_aio_setup(BlockDriverState *bs, return acb; } -static void qcow_aio_read_cb(void *opaque, int ret); -static void qcow_aio_write_cb(void *opaque, int ret); - -static void qcow_aio_rw_bh(void *opaque) -{ - QCowAIOCB *acb = opaque; - qemu_bh_delete(acb->bh); - acb->bh = NULL; - - if (acb->is_write) { - qcow_aio_write_cb(opaque, 0); - } else { - qcow_aio_read_cb(opaque, 0); - } -} - -static int qcow_schedule_bh(QEMUBHFunc *cb, QCowAIOCB *acb) -{ - if (acb->bh) { - return -EIO; - } - - acb->bh = qemu_bh_new(cb, acb); - if (!acb->bh) { - return -EIO; - } - - qemu_bh_schedule(acb->bh); - - return 0; -} - -static void qcow_aio_read_cb(void *opaque, int ret) +static int qcow_aio_read_cb(void *opaque) { QCowAIOCB *acb = opaque; BlockDriverState *bs = acb->common.bs; BDRVQcowState *s = bs->opaque; int index_in_cluster; + int ret; acb->hd_aiocb = NULL; - if (ret < 0) - goto done; redo: /* post process the read buffer */ @@ -605,8 +573,7 @@ static void qcow_aio_read_cb(void *opaque, int ret) if (acb->nb_sectors == 0) { /* request completed */ - ret = 0; - goto done; + return 0; } /* prepare next AIO request */ @@ -623,11 +590,12 @@ static void qcow_aio_read_cb(void *opaque, int ret) acb->hd_iov.iov_base = (void *)acb->buf; acb->hd_iov.iov_len = acb->n * 512; qemu_iovec_init_external(&acb->hd_qiov, &acb->hd_iov, 1); - acb->hd_aiocb = bdrv_aio_readv(bs->backing_hd, acb->sector_num, - &acb->hd_qiov, acb->n, qcow_aio_read_cb, acb); - if (acb->hd_aiocb == NULL) { - ret = -EIO; - goto done; + qemu_co_mutex_unlock(&s->lock); + ret = bdrv_co_readv(bs->backing_hd, acb->sector_num, + acb->n, &acb->hd_qiov); + qemu_co_mutex_lock(&s->lock); + if (ret < 0) { + return -EIO; } } else { /* Note: in this case, no need to wait */ @@ -637,64 +605,56 @@ static void qcow_aio_read_cb(void *opaque, int ret) } else if (acb->cluster_offset & QCOW_OFLAG_COMPRESSED) { /* add AIO support for compressed blocks ? */ if (decompress_cluster(bs, acb->cluster_offset) < 0) { - ret = -EIO; - goto done; + return -EIO; } memcpy(acb->buf, s->cluster_cache + index_in_cluster * 512, 512 * acb->n); goto redo; } else { if ((acb->cluster_offset & 511) != 0) { - ret = -EIO; - goto done; + return -EIO; } acb->hd_iov.iov_base = (void *)acb->buf; acb->hd_iov.iov_len = acb->n * 512; qemu_iovec_init_external(&acb->hd_qiov, &acb->hd_iov, 1); - acb->hd_aiocb = bdrv_aio_readv(bs->file, + qemu_co_mutex_unlock(&s->lock); + ret = bdrv_co_readv(bs->file, (acb->cluster_offset >> 9) + index_in_cluster, - &acb->hd_qiov, acb->n, qcow_aio_read_cb, acb); - if (acb->hd_aiocb == NULL) { - ret = -EIO; - goto done; + acb->n, &acb->hd_qiov); + qemu_co_mutex_lock(&s->lock); + if (ret < 0) { + return ret; } } - return; + return 1; +} + +static int qcow_co_readv(BlockDriverState *bs, int64_t sector_num, + int nb_sectors, QEMUIOVector *qiov) +{ + BDRVQcowState *s = bs->opaque; + QCowAIOCB *acb; + int ret; + + acb = qcow_aio_setup(bs, sector_num, qiov, nb_sectors, 0); + + qemu_co_mutex_lock(&s->lock); + do { + ret = qcow_aio_read_cb(acb); + } while (ret > 0); + qemu_co_mutex_unlock(&s->lock); -done: if (acb->qiov->niov > 1) { qemu_iovec_from_buffer(acb->qiov, acb->orig_buf, acb->qiov->size); qemu_vfree(acb->orig_buf); } - acb->common.cb(acb->common.opaque, ret); qemu_aio_release(acb); + + return ret; } -static BlockDriverAIOCB *qcow_aio_readv(BlockDriverState *bs, - int64_t sector_num, QEMUIOVector *qiov, int nb_sectors, - BlockDriverCompletionFunc *cb, void *opaque) -{ - QCowAIOCB *acb; - int ret; - - acb = qcow_aio_setup(bs, sector_num, qiov, nb_sectors, cb, opaque, 0); - if (!acb) - return NULL; - - ret = qcow_schedule_bh(qcow_aio_rw_bh, acb); - if (ret < 0) { - if (acb->qiov->niov > 1) { - qemu_vfree(acb->orig_buf); - } - qemu_aio_release(acb); - return NULL; - } - - return &acb->common; -} - -static void qcow_aio_write_cb(void *opaque, int ret) +static int qcow_aio_write_cb(void *opaque) { QCowAIOCB *acb = opaque; BlockDriverState *bs = acb->common.bs; @@ -702,20 +662,17 @@ static void qcow_aio_write_cb(void *opaque, int ret) int index_in_cluster; uint64_t cluster_offset; const uint8_t *src_buf; + int ret; acb->hd_aiocb = NULL; - if (ret < 0) - goto done; - acb->nb_sectors -= acb->n; acb->sector_num += acb->n; acb->buf += acb->n * 512; if (acb->nb_sectors == 0) { /* request completed */ - ret = 0; - goto done; + return 0; } index_in_cluster = acb->sector_num & (s->cluster_sectors - 1); @@ -726,16 +683,11 @@ static void qcow_aio_write_cb(void *opaque, int ret) index_in_cluster, index_in_cluster + acb->n); if (!cluster_offset || (cluster_offset & 511) != 0) { - ret = -EIO; - goto done; + return -EIO; } if (s->crypt_method) { if (!acb->cluster_data) { acb->cluster_data = qemu_mallocz(s->cluster_size); - if (!acb->cluster_data) { - ret = -ENOMEM; - goto done; - } } encrypt_sectors(s, acb->sector_num, acb->cluster_data, acb->buf, acb->n, 1, &s->aes_encrypt_key); @@ -747,26 +699,19 @@ static void qcow_aio_write_cb(void *opaque, int ret) acb->hd_iov.iov_base = (void *)src_buf; acb->hd_iov.iov_len = acb->n * 512; qemu_iovec_init_external(&acb->hd_qiov, &acb->hd_iov, 1); - acb->hd_aiocb = bdrv_aio_writev(bs->file, - (cluster_offset >> 9) + index_in_cluster, - &acb->hd_qiov, acb->n, - qcow_aio_write_cb, acb); - if (acb->hd_aiocb == NULL) { - ret = -EIO; - goto done; + qemu_co_mutex_unlock(&s->lock); + ret = bdrv_co_writev(bs->file, + (cluster_offset >> 9) + index_in_cluster, + acb->n, &acb->hd_qiov); + qemu_co_mutex_lock(&s->lock); + if (ret < 0) { + return ret; } - return; - -done: - if (acb->qiov->niov > 1) - qemu_vfree(acb->orig_buf); - acb->common.cb(acb->common.opaque, ret); - qemu_aio_release(acb); + return 1; } -static BlockDriverAIOCB *qcow_aio_writev(BlockDriverState *bs, - int64_t sector_num, QEMUIOVector *qiov, int nb_sectors, - BlockDriverCompletionFunc *cb, void *opaque) +static int qcow_co_writev(BlockDriverState *bs, int64_t sector_num, + int nb_sectors, QEMUIOVector *qiov) { BDRVQcowState *s = bs->opaque; QCowAIOCB *acb; @@ -774,21 +719,20 @@ static BlockDriverAIOCB *qcow_aio_writev(BlockDriverState *bs, s->cluster_cache_offset = -1; /* disable compressed cache */ - acb = qcow_aio_setup(bs, sector_num, qiov, nb_sectors, cb, opaque, 1); - if (!acb) - return NULL; + acb = qcow_aio_setup(bs, sector_num, qiov, nb_sectors, 1); + qemu_co_mutex_lock(&s->lock); + do { + ret = qcow_aio_write_cb(acb); + } while (ret > 0); + qemu_co_mutex_unlock(&s->lock); - ret = qcow_schedule_bh(qcow_aio_rw_bh, acb); - if (ret < 0) { - if (acb->qiov->niov > 1) { - qemu_vfree(acb->orig_buf); - } - qemu_aio_release(acb); - return NULL; + if (acb->qiov->niov > 1) { + qemu_vfree(acb->orig_buf); } + qemu_aio_release(acb); - return &acb->common; + return ret; } static void qcow_close(BlockDriverState *bs) @@ -1020,8 +964,8 @@ static BlockDriver bdrv_qcow = { .bdrv_is_allocated = qcow_is_allocated, .bdrv_set_key = qcow_set_key, .bdrv_make_empty = qcow_make_empty, - .bdrv_aio_readv = qcow_aio_readv, - .bdrv_aio_writev = qcow_aio_writev, + .bdrv_co_readv = qcow_co_readv, + .bdrv_co_writev = qcow_co_writev, .bdrv_aio_flush = qcow_aio_flush, .bdrv_write_compressed = qcow_write_compressed, .bdrv_get_info = qcow_get_info, From 384acbf46b70edf0d2c1648aa1a92a90bcf7057d Mon Sep 17 00:00:00 2001 From: Kevin Wolf Date: Fri, 15 Jul 2011 16:36:40 +0200 Subject: [PATCH 184/230] async: Remove AsyncContext The purpose of AsyncContexts was to protect qcow and qcow2 against reentrancy during an emulated bdrv_read/write (which includes a qemu_aio_wait() call and can run AIO callbacks of different requests if it weren't for AsyncContexts). Now both qcow and qcow2 are protected by CoMutexes and AsyncContexts can be removed. Signed-off-by: Kevin Wolf --- async.c | 98 ++++------------------------------------------ block.c | 6 --- block/qed-table.c | 14 ------- block/qed.c | 4 -- linux-aio.c | 43 ++------------------ posix-aio-compat.c | 11 ------ qemu-common.h | 4 -- 7 files changed, 11 insertions(+), 169 deletions(-) diff --git a/async.c b/async.c index fd313dffb7..3fe70b9deb 100644 --- a/async.c +++ b/async.c @@ -25,92 +25,8 @@ #include "qemu-common.h" #include "qemu-aio.h" -/* - * An AsyncContext protects the callbacks of AIO requests and Bottom Halves - * against interfering with each other. A typical example is qcow2 that accepts - * asynchronous requests, but relies for manipulation of its metadata on - * synchronous bdrv_read/write that doesn't trigger any callbacks. - * - * However, these functions are often emulated using AIO which means that AIO - * callbacks must be run - but at the same time we must not run callbacks of - * other requests as they might start to modify metadata and corrupt the - * internal state of the caller of bdrv_read/write. - * - * To achieve the desired semantics we switch into a new AsyncContext. - * Callbacks must only be run if they belong to the current AsyncContext. - * Otherwise they need to be queued until their own context is active again. - * This is how you can make qemu_aio_wait() wait only for your own callbacks. - * - * The AsyncContexts form a stack. When you leave a AsyncContexts, you always - * return to the old ("parent") context. - */ -struct AsyncContext { - /* Consecutive number of the AsyncContext (position in the stack) */ - int id; - - /* Anchor of the list of Bottom Halves belonging to the context */ - struct QEMUBH *first_bh; - - /* Link to parent context */ - struct AsyncContext *parent; -}; - -/* The currently active AsyncContext */ -static struct AsyncContext *async_context = &(struct AsyncContext) { 0 }; - -/* - * Enter a new AsyncContext. Already scheduled Bottom Halves and AIO callbacks - * won't be called until this context is left again. - */ -void async_context_push(void) -{ - struct AsyncContext *new = qemu_mallocz(sizeof(*new)); - new->parent = async_context; - new->id = async_context->id + 1; - async_context = new; -} - -/* Run queued AIO completions and destroy Bottom Half */ -static void bh_run_aio_completions(void *opaque) -{ - QEMUBH **bh = opaque; - qemu_bh_delete(*bh); - qemu_free(bh); - qemu_aio_process_queue(); -} -/* - * Leave the currently active AsyncContext. All Bottom Halves belonging to the - * old context are executed before changing the context. - */ -void async_context_pop(void) -{ - struct AsyncContext *old = async_context; - QEMUBH **bh; - - /* Flush the bottom halves, we don't want to lose them */ - while (qemu_bh_poll()); - - /* Switch back to the parent context */ - async_context = async_context->parent; - qemu_free(old); - - if (async_context == NULL) { - abort(); - } - - /* Schedule BH to run any queued AIO completions as soon as possible */ - bh = qemu_malloc(sizeof(*bh)); - *bh = qemu_bh_new(bh_run_aio_completions, bh); - qemu_bh_schedule(*bh); -} - -/* - * Returns the ID of the currently active AsyncContext - */ -int get_async_context_id(void) -{ - return async_context->id; -} +/* Anchor of the list of Bottom Halves belonging to the context */ +static struct QEMUBH *first_bh; /***********************************************************/ /* bottom halves (can be seen as timers which expire ASAP) */ @@ -130,8 +46,8 @@ QEMUBH *qemu_bh_new(QEMUBHFunc *cb, void *opaque) bh = qemu_mallocz(sizeof(QEMUBH)); bh->cb = cb; bh->opaque = opaque; - bh->next = async_context->first_bh; - async_context->first_bh = bh; + bh->next = first_bh; + first_bh = bh; return bh; } @@ -141,7 +57,7 @@ int qemu_bh_poll(void) int ret; ret = 0; - for (bh = async_context->first_bh; bh; bh = next) { + for (bh = first_bh; bh; bh = next) { next = bh->next; if (!bh->deleted && bh->scheduled) { bh->scheduled = 0; @@ -153,7 +69,7 @@ int qemu_bh_poll(void) } /* remove deleted bhs */ - bhp = &async_context->first_bh; + bhp = &first_bh; while (*bhp) { bh = *bhp; if (bh->deleted) { @@ -199,7 +115,7 @@ void qemu_bh_update_timeout(int *timeout) { QEMUBH *bh; - for (bh = async_context->first_bh; bh; bh = bh->next) { + for (bh = first_bh; bh; bh = bh->next) { if (!bh->deleted && bh->scheduled) { if (bh->idle) { /* idle bottom halves will be polled at least diff --git a/block.c b/block.c index e6abea85df..0d05b4b32d 100644 --- a/block.c +++ b/block.c @@ -2777,8 +2777,6 @@ static int bdrv_read_em(BlockDriverState *bs, int64_t sector_num, struct iovec iov; QEMUIOVector qiov; - async_context_push(); - async_ret = NOT_DONE; iov.iov_base = (void *)buf; iov.iov_len = nb_sectors * BDRV_SECTOR_SIZE; @@ -2796,7 +2794,6 @@ static int bdrv_read_em(BlockDriverState *bs, int64_t sector_num, fail: - async_context_pop(); return async_ret; } @@ -2808,8 +2805,6 @@ static int bdrv_write_em(BlockDriverState *bs, int64_t sector_num, struct iovec iov; QEMUIOVector qiov; - async_context_push(); - async_ret = NOT_DONE; iov.iov_base = (void *)buf; iov.iov_len = nb_sectors * BDRV_SECTOR_SIZE; @@ -2825,7 +2820,6 @@ static int bdrv_write_em(BlockDriverState *bs, int64_t sector_num, } fail: - async_context_pop(); return async_ret; } diff --git a/block/qed-table.c b/block/qed-table.c index d38c673547..d96afa81d7 100644 --- a/block/qed-table.c +++ b/block/qed-table.c @@ -179,16 +179,12 @@ int qed_read_l1_table_sync(BDRVQEDState *s) { int ret = -EINPROGRESS; - async_context_push(); - qed_read_table(s, s->header.l1_table_offset, s->l1_table, qed_sync_cb, &ret); while (ret == -EINPROGRESS) { qemu_aio_wait(); } - async_context_pop(); - return ret; } @@ -205,15 +201,11 @@ int qed_write_l1_table_sync(BDRVQEDState *s, unsigned int index, { int ret = -EINPROGRESS; - async_context_push(); - qed_write_l1_table(s, index, n, qed_sync_cb, &ret); while (ret == -EINPROGRESS) { qemu_aio_wait(); } - async_context_pop(); - return ret; } @@ -282,14 +274,11 @@ int qed_read_l2_table_sync(BDRVQEDState *s, QEDRequest *request, uint64_t offset { int ret = -EINPROGRESS; - async_context_push(); - qed_read_l2_table(s, request, offset, qed_sync_cb, &ret); while (ret == -EINPROGRESS) { qemu_aio_wait(); } - async_context_pop(); return ret; } @@ -307,13 +296,10 @@ int qed_write_l2_table_sync(BDRVQEDState *s, QEDRequest *request, { int ret = -EINPROGRESS; - async_context_push(); - qed_write_l2_table(s, request, index, n, flush, qed_sync_cb, &ret); while (ret == -EINPROGRESS) { qemu_aio_wait(); } - async_context_pop(); return ret; } diff --git a/block/qed.c b/block/qed.c index 39703793e9..333f067582 100644 --- a/block/qed.c +++ b/block/qed.c @@ -680,16 +680,12 @@ static int bdrv_qed_is_allocated(BlockDriverState *bs, int64_t sector_num, }; QEDRequest request = { .l2_table = NULL }; - async_context_push(); - qed_find_cluster(s, &request, pos, len, qed_is_allocated_cb, &cb); while (cb.is_allocated == -1) { qemu_aio_wait(); } - async_context_pop(); - qed_unref_l2_cache_entry(request.l2_table); return cb.is_allocated; diff --git a/linux-aio.c b/linux-aio.c index 68f4b3d757..dc3faf2499 100644 --- a/linux-aio.c +++ b/linux-aio.c @@ -31,7 +31,6 @@ struct qemu_laiocb { struct iocb iocb; ssize_t ret; size_t nbytes; - int async_context_id; QLIST_ENTRY(qemu_laiocb) node; }; @@ -39,7 +38,6 @@ struct qemu_laio_state { io_context_t ctx; int efd; int count; - QLIST_HEAD(, qemu_laiocb) completed_reqs; }; static inline ssize_t io_event_ret(struct io_event *ev) @@ -49,7 +47,6 @@ static inline ssize_t io_event_ret(struct io_event *ev) /* * Completes an AIO request (calls the callback and frees the ACB). - * Be sure to be in the right AsyncContext before calling this function. */ static void qemu_laio_process_completion(struct qemu_laio_state *s, struct qemu_laiocb *laiocb) @@ -72,42 +69,12 @@ static void qemu_laio_process_completion(struct qemu_laio_state *s, } /* - * Processes all queued AIO requests, i.e. requests that have return from OS - * but their callback was not called yet. Requests that cannot have their - * callback called in the current AsyncContext, remain in the queue. - * - * Returns 1 if at least one request could be completed, 0 otherwise. + * All requests are directly processed when they complete, so there's nothing + * left to do during qemu_aio_wait(). */ static int qemu_laio_process_requests(void *opaque) { - struct qemu_laio_state *s = opaque; - struct qemu_laiocb *laiocb, *next; - int res = 0; - - QLIST_FOREACH_SAFE (laiocb, &s->completed_reqs, node, next) { - if (laiocb->async_context_id == get_async_context_id()) { - qemu_laio_process_completion(s, laiocb); - QLIST_REMOVE(laiocb, node); - res = 1; - } - } - - return res; -} - -/* - * Puts a request in the completion queue so that its callback is called the - * next time when it's possible. If we already are in the right AsyncContext, - * the request is completed immediately instead. - */ -static void qemu_laio_enqueue_completed(struct qemu_laio_state *s, - struct qemu_laiocb* laiocb) -{ - if (laiocb->async_context_id == get_async_context_id()) { - qemu_laio_process_completion(s, laiocb); - } else { - QLIST_INSERT_HEAD(&s->completed_reqs, laiocb, node); - } + return 0; } static void qemu_laio_completion_cb(void *opaque) @@ -141,7 +108,7 @@ static void qemu_laio_completion_cb(void *opaque) container_of(iocb, struct qemu_laiocb, iocb); laiocb->ret = io_event_ret(&events[i]); - qemu_laio_enqueue_completed(s, laiocb); + qemu_laio_process_completion(s, laiocb); } } } @@ -204,7 +171,6 @@ BlockDriverAIOCB *laio_submit(BlockDriverState *bs, void *aio_ctx, int fd, laiocb->nbytes = nb_sectors * 512; laiocb->ctx = s; laiocb->ret = -EINPROGRESS; - laiocb->async_context_id = get_async_context_id(); iocbs = &laiocb->iocb; @@ -239,7 +205,6 @@ void *laio_init(void) struct qemu_laio_state *s; s = qemu_mallocz(sizeof(*s)); - QLIST_INIT(&s->completed_reqs); s->efd = eventfd(0, 0); if (s->efd == -1) goto out_free_state; diff --git a/posix-aio-compat.c b/posix-aio-compat.c index c4116e30f2..788d113860 100644 --- a/posix-aio-compat.c +++ b/posix-aio-compat.c @@ -49,8 +49,6 @@ struct qemu_paiocb { ssize_t ret; int active; struct qemu_paiocb *next; - - int async_context_id; }; typedef struct PosixAioState { @@ -420,7 +418,6 @@ static int posix_aio_process_queue(void *opaque) struct qemu_paiocb *acb, **pacb; int ret; int result = 0; - int async_context_id = get_async_context_id(); for(;;) { pacb = &s->first_aio; @@ -429,12 +426,6 @@ static int posix_aio_process_queue(void *opaque) if (!acb) return result; - /* we're only interested in requests in the right context */ - if (acb->async_context_id != async_context_id) { - pacb = &acb->next; - continue; - } - ret = qemu_paio_error(acb); if (ret == ECANCELED) { /* remove the request */ @@ -575,7 +566,6 @@ BlockDriverAIOCB *paio_submit(BlockDriverState *bs, int fd, acb->aio_type = type; acb->aio_fildes = fd; acb->ev_signo = SIGUSR2; - acb->async_context_id = get_async_context_id(); if (qiov) { acb->aio_iov = qiov->iov; @@ -604,7 +594,6 @@ BlockDriverAIOCB *paio_ioctl(BlockDriverState *bs, int fd, acb->aio_type = QEMU_AIO_IOCTL; acb->aio_fildes = fd; acb->ev_signo = SIGUSR2; - acb->async_context_id = get_async_context_id(); acb->aio_offset = 0; acb->aio_ioctl_buf = buf; acb->aio_ioctl_cmd = req; diff --git a/qemu-common.h b/qemu-common.h index 1e3c66511e..8f21a8cb29 100644 --- a/qemu-common.h +++ b/qemu-common.h @@ -115,10 +115,6 @@ int qemu_main(int argc, char **argv, char **envp); /* bottom halves */ typedef void QEMUBHFunc(void *opaque); -void async_context_push(void); -void async_context_pop(void); -int get_async_context_id(void); - QEMUBH *qemu_bh_new(QEMUBHFunc *cb, void *opaque); void qemu_bh_schedule(QEMUBH *bh); /* Bottom halfs that are scheduled from a bottom half handler are instantly From e680cfa7e20f5049c475ac94f998a79c9997b48d Mon Sep 17 00:00:00 2001 From: Kevin Wolf Date: Wed, 13 Jul 2011 15:42:52 +0200 Subject: [PATCH 185/230] coroutines: Use one global bottom half for CoQueue Now that AsyncContexts don't exist any more, we can use one global bottom half for restarting coroutines instead of allocating a new one every time (before removing AsyncContexts, the problem with having a global BH was that it had to belong to a single AsyncContexts and wouldn't be executed in a different one - which leads to deadlocks) Signed-off-by: Kevin Wolf --- qemu-coroutine-lock.c | 19 ++++++------------- 1 file changed, 6 insertions(+), 13 deletions(-) diff --git a/qemu-coroutine-lock.c b/qemu-coroutine-lock.c index abaa1f7967..a80f437c59 100644 --- a/qemu-coroutine-lock.c +++ b/qemu-coroutine-lock.c @@ -30,14 +30,10 @@ static QTAILQ_HEAD(, Coroutine) unlock_bh_queue = QTAILQ_HEAD_INITIALIZER(unlock_bh_queue); - -struct unlock_bh { - QEMUBH *bh; -}; +static QEMUBH* unlock_bh; static void qemu_co_queue_next_bh(void *opaque) { - struct unlock_bh *unlock_bh = opaque; Coroutine *next; trace_qemu_co_queue_next_bh(); @@ -45,14 +41,15 @@ static void qemu_co_queue_next_bh(void *opaque) QTAILQ_REMOVE(&unlock_bh_queue, next, co_queue_next); qemu_coroutine_enter(next, NULL); } - - qemu_bh_delete(unlock_bh->bh); - qemu_free(unlock_bh); } void qemu_co_queue_init(CoQueue *queue) { QTAILQ_INIT(&queue->entries); + + if (!unlock_bh) { + unlock_bh = qemu_bh_new(qemu_co_queue_next_bh, NULL); + } } void coroutine_fn qemu_co_queue_wait(CoQueue *queue) @@ -65,7 +62,6 @@ void coroutine_fn qemu_co_queue_wait(CoQueue *queue) bool qemu_co_queue_next(CoQueue *queue) { - struct unlock_bh *unlock_bh; Coroutine *next; next = QTAILQ_FIRST(&queue->entries); @@ -73,10 +69,7 @@ bool qemu_co_queue_next(CoQueue *queue) QTAILQ_REMOVE(&queue->entries, next, co_queue_next); QTAILQ_INSERT_TAIL(&unlock_bh_queue, next, co_queue_next); trace_qemu_co_queue_next(next); - - unlock_bh = qemu_malloc(sizeof(*unlock_bh)); - unlock_bh->bh = qemu_bh_new(qemu_co_queue_next_bh, unlock_bh); - qemu_bh_schedule(unlock_bh->bh); + qemu_bh_schedule(unlock_bh); } return (next != NULL); From ba1d1afdfeeb5ec6cbfd0052ab298d9b7cb7e5ba Mon Sep 17 00:00:00 2001 From: Kevin Wolf Date: Mon, 25 Jul 2011 19:42:37 +0200 Subject: [PATCH 186/230] posix-aio-compat: Allow read after EOF In order to be able to transparently replace bdrv_read calls by bdrv_co_read, reading beyond EOF must produce zeros instead of short reads for AIO, too. Signed-off-by: Kevin Wolf --- posix-aio-compat.c | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) diff --git a/posix-aio-compat.c b/posix-aio-compat.c index 788d113860..8dc00cbb0f 100644 --- a/posix-aio-compat.c +++ b/posix-aio-compat.c @@ -198,6 +198,12 @@ static ssize_t handle_aiocb_rw_vector(struct qemu_paiocb *aiocb) return len; } +/* + * Read/writes the data to/from a given linear buffer. + * + * Returns the number of bytes handles or -errno in case of an error. Short + * reads are only returned if the end of the file is reached. + */ static ssize_t handle_aiocb_rw_linear(struct qemu_paiocb *aiocb, char *buf) { ssize_t offset = 0; @@ -334,6 +340,19 @@ static void *aio_thread(void *unused) switch (aiocb->aio_type & QEMU_AIO_TYPE_MASK) { case QEMU_AIO_READ: + ret = handle_aiocb_rw(aiocb); + if (ret >= 0 && ret < aiocb->aio_nbytes && aiocb->common.bs->growable) { + /* A short read means that we have reached EOF. Pad the buffer + * with zeros for bytes after EOF. */ + QEMUIOVector qiov; + + qemu_iovec_init_external(&qiov, aiocb->aio_iov, + aiocb->aio_niov); + qemu_iovec_memset_skip(&qiov, 0, aiocb->aio_nbytes - ret, ret); + + ret = aiocb->aio_nbytes; + } + break; case QEMU_AIO_WRITE: ret = handle_aiocb_rw(aiocb); break; From bafc72ab01cd5a058f1d07d1bb80ae0b27ff190a Mon Sep 17 00:00:00 2001 From: Jan Kiszka Date: Wed, 27 Jul 2011 14:21:32 +0200 Subject: [PATCH 187/230] slirp: Take maintainer token Anthony asked me to pick up the maintenance of this subsystem, and I agreed. Signed-off-by: Jan Kiszka --- MAINTAINERS | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/MAINTAINERS b/MAINTAINERS index 6115e4ec08..7cbcd7e60d 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -431,9 +431,10 @@ S: Maintained F: net/ SLIRP -M: qemu-devel@nongnu.org -S: Orphan +M: Jan Kiszka +S: Maintained F: slirp/ +T: git://git.kiszka.org/qemu.git queues/slirp Usermode Emulation ------------------ From 5ff4e36c804157bd84af43c139f8cd3a59722db9 Mon Sep 17 00:00:00 2001 From: Alon Levy Date: Wed, 20 Jul 2011 12:20:58 +0300 Subject: [PATCH 188/230] qxl: async io support using new spice api Some of the QXL port i/o commands are waiting for the spice server to complete certain actions. Add async versions for these commands, so we don't block the vcpu while the spice server processses the command. Instead the qxl device will raise an IRQ when done. The async command processing relies on an added QXLInterface::async_complete and added QXLWorker::*_async additions, in spice server qxl >= 3.1 Signed-off-by: Gerd Hoffmann Signed-off-by: Alon Levy --- hw/qxl-render.c | 2 +- hw/qxl.c | 240 ++++++++++++++++++++++++++++++++++++++------- hw/qxl.h | 16 ++- ui/spice-display.c | 47 +++++++-- ui/spice-display.h | 23 ++++- 5 files changed, 274 insertions(+), 54 deletions(-) diff --git a/hw/qxl-render.c b/hw/qxl-render.c index 60b822d8c0..643ff2d841 100644 --- a/hw/qxl-render.c +++ b/hw/qxl-render.c @@ -125,7 +125,7 @@ void qxl_render_update(PCIQXLDevice *qxl) memset(dirty, 0, sizeof(dirty)); qxl_spice_update_area(qxl, 0, &update, - dirty, ARRAY_SIZE(dirty), 1); + dirty, ARRAY_SIZE(dirty), 1, QXL_SYNC); for (i = 0; i < ARRAY_SIZE(dirty); i++) { if (qemu_spice_rect_is_empty(dirty+i)) { diff --git a/hw/qxl.c b/hw/qxl.c index 23e3240379..d3109e432d 100644 --- a/hw/qxl.c +++ b/hw/qxl.c @@ -120,7 +120,7 @@ static QXLMode qxl_modes[] = { static PCIQXLDevice *qxl0; static void qxl_send_events(PCIQXLDevice *d, uint32_t events); -static void qxl_destroy_primary(PCIQXLDevice *d); +static int qxl_destroy_primary(PCIQXLDevice *d, qxl_async_io async); static void qxl_reset_memslots(PCIQXLDevice *d); static void qxl_reset_surfaces(PCIQXLDevice *d); static void qxl_ring_set_dirty(PCIQXLDevice *qxl); @@ -144,22 +144,47 @@ void qxl_guest_bug(PCIQXLDevice *qxl, const char *msg, ...) void qxl_spice_update_area(PCIQXLDevice *qxl, uint32_t surface_id, struct QXLRect *area, struct QXLRect *dirty_rects, uint32_t num_dirty_rects, - uint32_t clear_dirty_region) + uint32_t clear_dirty_region, + qxl_async_io async) { - qxl->ssd.worker->update_area(qxl->ssd.worker, surface_id, area, dirty_rects, - num_dirty_rects, clear_dirty_region); + if (async == QXL_SYNC) { + qxl->ssd.worker->update_area(qxl->ssd.worker, surface_id, area, + dirty_rects, num_dirty_rects, clear_dirty_region); + } else { +#if SPICE_INTERFACE_QXL_MINOR >= 1 + spice_qxl_update_area_async(&qxl->ssd.qxl, surface_id, area, + clear_dirty_region, 0); +#else + abort(); +#endif + } } -void qxl_spice_destroy_surface_wait(PCIQXLDevice *qxl, uint32_t id) +static void qxl_spice_destroy_surface_wait_complete(PCIQXLDevice *qxl, + uint32_t id) { qemu_mutex_lock(&qxl->track_lock); - PANIC_ON(id >= NUM_SURFACES); - qxl->ssd.worker->destroy_surface_wait(qxl->ssd.worker, id); qxl->guest_surfaces.cmds[id] = 0; qxl->guest_surfaces.count--; qemu_mutex_unlock(&qxl->track_lock); } +static void qxl_spice_destroy_surface_wait(PCIQXLDevice *qxl, uint32_t id, + qxl_async_io async) +{ + if (async) { +#if SPICE_INTERFACE_QXL_MINOR < 1 + abort(); +#else + spice_qxl_destroy_surface_async(&qxl->ssd.qxl, id, + (uint64_t)id); +#endif + } else { + qxl->ssd.worker->destroy_surface_wait(qxl->ssd.worker, id); + qxl_spice_destroy_surface_wait_complete(qxl, id); + } +} + void qxl_spice_loadvm_commands(PCIQXLDevice *qxl, struct QXLCommandExt *ext, uint32_t count) { @@ -176,15 +201,28 @@ void qxl_spice_reset_memslots(PCIQXLDevice *qxl) qxl->ssd.worker->reset_memslots(qxl->ssd.worker); } -void qxl_spice_destroy_surfaces(PCIQXLDevice *qxl) +static void qxl_spice_destroy_surfaces_complete(PCIQXLDevice *qxl) { qemu_mutex_lock(&qxl->track_lock); - qxl->ssd.worker->destroy_surfaces(qxl->ssd.worker); memset(&qxl->guest_surfaces.cmds, 0, sizeof(qxl->guest_surfaces.cmds)); qxl->guest_surfaces.count = 0; qemu_mutex_unlock(&qxl->track_lock); } +static void qxl_spice_destroy_surfaces(PCIQXLDevice *qxl, qxl_async_io async) +{ + if (async) { +#if SPICE_INTERFACE_QXL_MINOR < 1 + abort(); +#else + spice_qxl_destroy_surfaces_async(&qxl->ssd.qxl, 0); +#endif + } else { + qxl->ssd.worker->destroy_surfaces(qxl->ssd.worker); + qxl_spice_destroy_surfaces_complete(qxl); + } +} + void qxl_spice_reset_image_cache(PCIQXLDevice *qxl) { qxl->ssd.worker->reset_image_cache(qxl->ssd.worker); @@ -689,6 +727,38 @@ static int interface_flush_resources(QXLInstance *sin) return ret; } +static void qxl_create_guest_primary_complete(PCIQXLDevice *d); + +#if SPICE_INTERFACE_QXL_MINOR >= 1 + +/* called from spice server thread context only */ +static void interface_async_complete(QXLInstance *sin, uint64_t cookie) +{ + PCIQXLDevice *qxl = container_of(sin, PCIQXLDevice, ssd.qxl); + uint32_t current_async; + + qemu_mutex_lock(&qxl->async_lock); + current_async = qxl->current_async; + qxl->current_async = QXL_UNDEFINED_IO; + qemu_mutex_unlock(&qxl->async_lock); + + dprint(qxl, 2, "async_complete: %d (%ld) done\n", current_async, cookie); + switch (current_async) { + case QXL_IO_CREATE_PRIMARY_ASYNC: + qxl_create_guest_primary_complete(qxl); + break; + case QXL_IO_DESTROY_ALL_SURFACES_ASYNC: + qxl_spice_destroy_surfaces_complete(qxl); + break; + case QXL_IO_DESTROY_SURFACE_ASYNC: + qxl_spice_destroy_surface_wait_complete(qxl, (uint32_t)cookie); + break; + } + qxl_send_events(qxl, QXL_INTERRUPT_IO_CMD); +} + +#endif + static const QXLInterface qxl_interface = { .base.type = SPICE_INTERFACE_QXL, .base.description = "qxl gpu", @@ -708,6 +778,9 @@ static const QXLInterface qxl_interface = { .req_cursor_notification = interface_req_cursor_notification, .notify_update = interface_notify_update, .flush_resources = interface_flush_resources, +#if SPICE_INTERFACE_QXL_MINOR >= 1 + .async_complete = interface_async_complete, +#endif }; static void qxl_enter_vga_mode(PCIQXLDevice *d) @@ -727,7 +800,7 @@ static void qxl_exit_vga_mode(PCIQXLDevice *d) return; } dprint(d, 1, "%s\n", __FUNCTION__); - qxl_destroy_primary(d); + qxl_destroy_primary(d, QXL_SYNC); } static void qxl_set_irq(PCIQXLDevice *d) @@ -824,13 +897,14 @@ static void qxl_vga_ioport_write(void *opaque, uint32_t addr, uint32_t val) if (qxl->mode != QXL_MODE_VGA) { dprint(qxl, 1, "%s\n", __FUNCTION__); - qxl_destroy_primary(qxl); + qxl_destroy_primary(qxl, QXL_SYNC); qxl_soft_reset(qxl); } vga_ioport_write(opaque, addr, val); } -static void qxl_add_memslot(PCIQXLDevice *d, uint32_t slot_id, uint64_t delta) +static void qxl_add_memslot(PCIQXLDevice *d, uint32_t slot_id, uint64_t delta, + qxl_async_io async) { static const int regions[] = { QXL_RAM_RANGE_INDEX, @@ -900,7 +974,7 @@ static void qxl_add_memslot(PCIQXLDevice *d, uint32_t slot_id, uint64_t delta) __FUNCTION__, memslot.slot_id, memslot.virt_start, memslot.virt_end); - qemu_spice_add_memslot(&d->ssd, &memslot); + qemu_spice_add_memslot(&d->ssd, &memslot, async); d->guest_slots[slot_id].ptr = (void*)memslot.virt_start; d->guest_slots[slot_id].size = memslot.virt_end - memslot.virt_start; d->guest_slots[slot_id].delta = delta; @@ -925,7 +999,7 @@ static void qxl_reset_surfaces(PCIQXLDevice *d) { dprint(d, 1, "%s:\n", __FUNCTION__); d->mode = QXL_MODE_UNDEFINED; - qxl_spice_destroy_surfaces(d); + qxl_spice_destroy_surfaces(d, QXL_SYNC); } /* called from spice server thread context only */ @@ -950,7 +1024,14 @@ void *qxl_phys2virt(PCIQXLDevice *qxl, QXLPHYSICAL pqxl, int group_id) } } -static void qxl_create_guest_primary(PCIQXLDevice *qxl, int loadvm) +static void qxl_create_guest_primary_complete(PCIQXLDevice *qxl) +{ + /* for local rendering */ + qxl_render_resize(qxl); +} + +static void qxl_create_guest_primary(PCIQXLDevice *qxl, int loadvm, + qxl_async_io async) { QXLDevSurfaceCreate surface; QXLSurfaceCreate *sc = &qxl->guest_primary.surface; @@ -978,22 +1059,26 @@ static void qxl_create_guest_primary(PCIQXLDevice *qxl, int loadvm) qxl->mode = QXL_MODE_NATIVE; qxl->cmdflags = 0; - qemu_spice_create_primary_surface(&qxl->ssd, 0, &surface); + qemu_spice_create_primary_surface(&qxl->ssd, 0, &surface, async); - /* for local rendering */ - qxl_render_resize(qxl); + if (async == QXL_SYNC) { + qxl_create_guest_primary_complete(qxl); + } } -static void qxl_destroy_primary(PCIQXLDevice *d) +/* return 1 if surface destoy was initiated (in QXL_ASYNC case) or + * done (in QXL_SYNC case), 0 otherwise. */ +static int qxl_destroy_primary(PCIQXLDevice *d, qxl_async_io async) { if (d->mode == QXL_MODE_UNDEFINED) { - return; + return 0; } dprint(d, 1, "%s\n", __FUNCTION__); d->mode = QXL_MODE_UNDEFINED; - qemu_spice_destroy_primary_surface(&d->ssd, 0); + qemu_spice_destroy_primary_surface(&d->ssd, 0, async); + return 1; } static void qxl_set_mode(PCIQXLDevice *d, int modenr, int loadvm) @@ -1023,10 +1108,10 @@ static void qxl_set_mode(PCIQXLDevice *d, int modenr, int loadvm) } d->guest_slots[0].slot = slot; - qxl_add_memslot(d, 0, devmem); + qxl_add_memslot(d, 0, devmem, QXL_SYNC); d->guest_primary.surface = surface; - qxl_create_guest_primary(d, 0); + qxl_create_guest_primary(d, 0, QXL_SYNC); d->mode = QXL_MODE_COMPAT; d->cmdflags = QXL_COMMAND_FLAG_COMPAT; @@ -1044,6 +1129,10 @@ static void ioport_write(void *opaque, uint32_t addr, uint32_t val) { PCIQXLDevice *d = opaque; uint32_t io_port = addr - d->io_base; + qxl_async_io async = QXL_SYNC; +#if SPICE_INTERFACE_QXL_MINOR >= 1 + uint32_t orig_io_port = io_port; +#endif switch (io_port) { case QXL_IO_RESET: @@ -1053,6 +1142,10 @@ static void ioport_write(void *opaque, uint32_t addr, uint32_t val) case QXL_IO_CREATE_PRIMARY: case QXL_IO_UPDATE_IRQ: case QXL_IO_LOG: +#if SPICE_INTERFACE_QXL_MINOR >= 1 + case QXL_IO_MEMSLOT_ADD_ASYNC: + case QXL_IO_CREATE_PRIMARY_ASYNC: +#endif break; default: if (d->mode != QXL_MODE_VGA) { @@ -1060,15 +1153,61 @@ static void ioport_write(void *opaque, uint32_t addr, uint32_t val) } dprint(d, 1, "%s: unexpected port 0x%x (%s) in vga mode\n", __func__, io_port, io_port_to_string(io_port)); +#if SPICE_INTERFACE_QXL_MINOR >= 1 + /* be nice to buggy guest drivers */ + if (io_port >= QXL_IO_UPDATE_AREA_ASYNC && + io_port <= QXL_IO_DESTROY_ALL_SURFACES_ASYNC) { + qxl_send_events(d, QXL_INTERRUPT_IO_CMD); + } +#endif return; } +#if SPICE_INTERFACE_QXL_MINOR >= 1 + /* we change the io_port to avoid ifdeffery in the main switch */ + orig_io_port = io_port; + switch (io_port) { + case QXL_IO_UPDATE_AREA_ASYNC: + io_port = QXL_IO_UPDATE_AREA; + goto async_common; + case QXL_IO_MEMSLOT_ADD_ASYNC: + io_port = QXL_IO_MEMSLOT_ADD; + goto async_common; + case QXL_IO_CREATE_PRIMARY_ASYNC: + io_port = QXL_IO_CREATE_PRIMARY; + goto async_common; + case QXL_IO_DESTROY_PRIMARY_ASYNC: + io_port = QXL_IO_DESTROY_PRIMARY; + goto async_common; + case QXL_IO_DESTROY_SURFACE_ASYNC: + io_port = QXL_IO_DESTROY_SURFACE_WAIT; + goto async_common; + case QXL_IO_DESTROY_ALL_SURFACES_ASYNC: + io_port = QXL_IO_DESTROY_ALL_SURFACES; +async_common: + async = QXL_ASYNC; + qemu_mutex_lock(&d->async_lock); + if (d->current_async != QXL_UNDEFINED_IO) { + qxl_guest_bug(d, "%d async started before last (%d) complete", + io_port, d->current_async); + qemu_mutex_unlock(&d->async_lock); + return; + } + d->current_async = orig_io_port; + qemu_mutex_unlock(&d->async_lock); + dprint(d, 2, "start async %d (%d)\n", io_port, val); + break; + default: + break; + } +#endif + switch (io_port) { case QXL_IO_UPDATE_AREA: { QXLRect update = d->ram->update_area; qxl_spice_update_area(d, d->ram->update_surface, - &update, NULL, 0, 0); + &update, NULL, 0, 0, async); break; } case QXL_IO_NOTIFY_CMD: @@ -1116,7 +1255,7 @@ static void ioport_write(void *opaque, uint32_t addr, uint32_t val) break; } d->guest_slots[val].slot = d->ram->mem_slot; - qxl_add_memslot(d, val, 0); + qxl_add_memslot(d, val, 0, async); break; case QXL_IO_MEMSLOT_DEL: if (val >= NUM_MEMSLOTS) { @@ -1127,31 +1266,56 @@ static void ioport_write(void *opaque, uint32_t addr, uint32_t val) break; case QXL_IO_CREATE_PRIMARY: if (val != 0) { - qxl_guest_bug(d, "QXL_IO_CREATE_PRIMARY: val != 0"); - break; + qxl_guest_bug(d, "QXL_IO_CREATE_PRIMARY (async=%d): val != 0", + async); + goto cancel_async; } - dprint(d, 1, "QXL_IO_CREATE_PRIMARY\n"); + dprint(d, 1, "QXL_IO_CREATE_PRIMARY async=%d\n", async); d->guest_primary.surface = d->ram->create_surface; - qxl_create_guest_primary(d, 0); + qxl_create_guest_primary(d, 0, async); break; case QXL_IO_DESTROY_PRIMARY: if (val != 0) { - qxl_guest_bug(d, "QXL_IO_DESTROY_PRIMARY: val != 0"); - break; + qxl_guest_bug(d, "QXL_IO_DESTROY_PRIMARY (async=%d): val != 0", + async); + goto cancel_async; + } + dprint(d, 1, "QXL_IO_DESTROY_PRIMARY (async=%d) (%s)\n", async, + qxl_mode_to_string(d->mode)); + if (!qxl_destroy_primary(d, async)) { + dprint(d, 1, "QXL_IO_DESTROY_PRIMARY_ASYNC in %s, ignored\n", + qxl_mode_to_string(d->mode)); + goto cancel_async; } - dprint(d, 1, "QXL_IO_DESTROY_PRIMARY (%s)\n", qxl_mode_to_string(d->mode)); - qxl_destroy_primary(d); break; case QXL_IO_DESTROY_SURFACE_WAIT: - qxl_spice_destroy_surface_wait(d, val); + if (val >= NUM_SURFACES) { + qxl_guest_bug(d, "QXL_IO_DESTROY_SURFACE (async=%d):" + "%d >= NUM_SURFACES", async, val); + goto cancel_async; + } + qxl_spice_destroy_surface_wait(d, val, async); break; case QXL_IO_DESTROY_ALL_SURFACES: - qxl_spice_destroy_surfaces(d); + d->mode = QXL_MODE_UNDEFINED; + qxl_spice_destroy_surfaces(d, async); break; default: fprintf(stderr, "%s: ioport=0x%x, abort()\n", __FUNCTION__, io_port); abort(); } + return; +cancel_async: +#if SPICE_INTERFACE_QXL_MINOR >= 1 + if (async) { + qxl_send_events(d, QXL_INTERRUPT_IO_CMD); + qemu_mutex_lock(&d->async_lock); + d->current_async = QXL_UNDEFINED_IO; + qemu_mutex_unlock(&d->async_lock); + } +#else + return; +#endif } static uint32_t ioport_read(void *opaque, uint32_t addr) @@ -1364,6 +1528,8 @@ static int qxl_init_common(PCIQXLDevice *qxl) qxl->num_memslots = NUM_MEMSLOTS; qxl->num_surfaces = NUM_SURFACES; qemu_mutex_init(&qxl->track_lock); + qemu_mutex_init(&qxl->async_lock); + qxl->current_async = QXL_UNDEFINED_IO; switch (qxl->revision) { case 1: /* spice 0.4 -- qxl-1 */ @@ -1528,9 +1694,9 @@ static int qxl_post_load(void *opaque, int version) if (!d->guest_slots[i].active) { continue; } - qxl_add_memslot(d, i, 0); + qxl_add_memslot(d, i, 0, QXL_SYNC); } - qxl_create_guest_primary(d, 1); + qxl_create_guest_primary(d, 1, QXL_SYNC); /* replay surface-create and cursor-set commands */ cmds = qemu_mallocz(sizeof(QXLCommandExt) * (NUM_SURFACES + 1)); diff --git a/hw/qxl.h b/hw/qxl.h index 32ca5a0895..1046205ef8 100644 --- a/hw/qxl.h +++ b/hw/qxl.h @@ -15,6 +15,8 @@ enum qxl_mode { QXL_MODE_NATIVE, }; +#define QXL_UNDEFINED_IO UINT32_MAX + typedef struct PCIQXLDevice { PCIDevice pci; SimpleSpiceDisplay ssd; @@ -30,6 +32,9 @@ typedef struct PCIQXLDevice { int32_t num_memslots; int32_t num_surfaces; + uint32_t current_async; + QemuMutex async_lock; + struct guest_slots { QXLMemSlot slot; void *ptr; @@ -104,13 +109,12 @@ void qxl_guest_bug(PCIQXLDevice *qxl, const char *msg, ...); void qxl_spice_update_area(PCIQXLDevice *qxl, uint32_t surface_id, struct QXLRect *area, struct QXLRect *dirty_rects, uint32_t num_dirty_rects, - uint32_t clear_dirty_region); -void qxl_spice_destroy_surface_wait(PCIQXLDevice *qxl, uint32_t id); + uint32_t clear_dirty_region, + qxl_async_io async); void qxl_spice_loadvm_commands(PCIQXLDevice *qxl, struct QXLCommandExt *ext, uint32_t count); void qxl_spice_oom(PCIQXLDevice *qxl); void qxl_spice_reset_memslots(PCIQXLDevice *qxl); -void qxl_spice_destroy_surfaces(PCIQXLDevice *qxl); void qxl_spice_reset_image_cache(PCIQXLDevice *qxl); void qxl_spice_reset_cursor(PCIQXLDevice *qxl); @@ -122,3 +126,9 @@ void qxl_log_command(PCIQXLDevice *qxl, const char *ring, QXLCommandExt *ext); void qxl_render_resize(PCIQXLDevice *qxl); void qxl_render_update(PCIQXLDevice *qxl); void qxl_render_cursor(PCIQXLDevice *qxl, QXLCommandExt *ext); +#if SPICE_INTERFACE_QXL_MINOR >= 1 +void qxl_spice_update_area_async(PCIQXLDevice *qxl, uint32_t surface_id, + struct QXLRect *area, + uint32_t clear_dirty_region, + int is_vga); +#endif diff --git a/ui/spice-display.c b/ui/spice-display.c index af10ae8a6f..683d45429f 100644 --- a/ui/spice-display.c +++ b/ui/spice-display.c @@ -62,10 +62,18 @@ void qemu_spice_rect_union(QXLRect *dest, const QXLRect *r) dest->right = MAX(dest->right, r->right); } - -void qemu_spice_add_memslot(SimpleSpiceDisplay *ssd, QXLDevMemSlot *memslot) +void qemu_spice_add_memslot(SimpleSpiceDisplay *ssd, QXLDevMemSlot *memslot, + qxl_async_io async) { - ssd->worker->add_memslot(ssd->worker, memslot); + if (async != QXL_SYNC) { +#if SPICE_INTERFACE_QXL_MINOR >= 1 + spice_qxl_add_memslot_async(&ssd->qxl, memslot, 0); +#else + abort(); +#endif + } else { + ssd->worker->add_memslot(ssd->worker, memslot); + } } void qemu_spice_del_memslot(SimpleSpiceDisplay *ssd, uint32_t gid, uint32_t sid) @@ -74,14 +82,33 @@ void qemu_spice_del_memslot(SimpleSpiceDisplay *ssd, uint32_t gid, uint32_t sid) } void qemu_spice_create_primary_surface(SimpleSpiceDisplay *ssd, uint32_t id, - QXLDevSurfaceCreate *surface) + QXLDevSurfaceCreate *surface, + qxl_async_io async) { - ssd->worker->create_primary_surface(ssd->worker, id, surface); + if (async != QXL_SYNC) { +#if SPICE_INTERFACE_QXL_MINOR >= 1 + spice_qxl_create_primary_surface_async(&ssd->qxl, id, surface, 0); +#else + abort(); +#endif + } else { + ssd->worker->create_primary_surface(ssd->worker, id, surface); + } } -void qemu_spice_destroy_primary_surface(SimpleSpiceDisplay *ssd, uint32_t id) + +void qemu_spice_destroy_primary_surface(SimpleSpiceDisplay *ssd, + uint32_t id, qxl_async_io async) { - ssd->worker->destroy_primary_surface(ssd->worker, id); + if (async != QXL_SYNC) { +#if SPICE_INTERFACE_QXL_MINOR >= 1 + spice_qxl_destroy_primary_surface_async(&ssd->qxl, id, 0); +#else + abort(); +#endif + } else { + ssd->worker->destroy_primary_surface(ssd->worker, id); + } } void qemu_spice_wakeup(SimpleSpiceDisplay *ssd) @@ -198,7 +225,7 @@ void qemu_spice_create_host_memslot(SimpleSpiceDisplay *ssd) memset(&memslot, 0, sizeof(memslot)); memslot.slot_group_id = MEMSLOT_GROUP_HOST; memslot.virt_end = ~0; - qemu_spice_add_memslot(ssd, &memslot); + qemu_spice_add_memslot(ssd, &memslot, QXL_SYNC); } void qemu_spice_create_host_primary(SimpleSpiceDisplay *ssd) @@ -218,14 +245,14 @@ void qemu_spice_create_host_primary(SimpleSpiceDisplay *ssd) surface.mem = (intptr_t)ssd->buf; surface.group_id = MEMSLOT_GROUP_HOST; - qemu_spice_create_primary_surface(ssd, 0, &surface); + qemu_spice_create_primary_surface(ssd, 0, &surface, QXL_SYNC); } void qemu_spice_destroy_host_primary(SimpleSpiceDisplay *ssd) { dprint(1, "%s:\n", __FUNCTION__); - qemu_spice_destroy_primary_surface(ssd, 0); + qemu_spice_destroy_primary_surface(ssd, 0, QXL_SYNC); } void qemu_spice_vm_change_state_handler(void *opaque, int running, int reason) diff --git a/ui/spice-display.h b/ui/spice-display.h index abe99c7d33..1388641370 100644 --- a/ui/spice-display.h +++ b/ui/spice-display.h @@ -33,6 +33,20 @@ #define NUM_SURFACES 1024 +/* + * Internal enum to differenciate between options for + * io calls that have a sync (old) version and an _async (new) + * version: + * QXL_SYNC: use the old version + * QXL_ASYNC: use the new version and make sure there are no two + * happening at the same time. This is used for guest initiated + * calls + */ +typedef enum qxl_async_io { + QXL_SYNC, + QXL_ASYNC, +} qxl_async_io; + typedef struct SimpleSpiceDisplay SimpleSpiceDisplay; typedef struct SimpleSpiceUpdate SimpleSpiceUpdate; @@ -82,12 +96,15 @@ void qemu_spice_display_update(SimpleSpiceDisplay *ssd, void qemu_spice_display_resize(SimpleSpiceDisplay *ssd); void qemu_spice_display_refresh(SimpleSpiceDisplay *ssd); -void qemu_spice_add_memslot(SimpleSpiceDisplay *ssd, QXLDevMemSlot *memslot); +void qemu_spice_add_memslot(SimpleSpiceDisplay *ssd, QXLDevMemSlot *memslot, + qxl_async_io async); void qemu_spice_del_memslot(SimpleSpiceDisplay *ssd, uint32_t gid, uint32_t sid); void qemu_spice_create_primary_surface(SimpleSpiceDisplay *ssd, uint32_t id, - QXLDevSurfaceCreate *surface); -void qemu_spice_destroy_primary_surface(SimpleSpiceDisplay *ssd, uint32_t id); + QXLDevSurfaceCreate *surface, + qxl_async_io async); +void qemu_spice_destroy_primary_surface(SimpleSpiceDisplay *ssd, + uint32_t id, qxl_async_io async); void qemu_spice_wakeup(SimpleSpiceDisplay *ssd); void qemu_spice_start(SimpleSpiceDisplay *ssd); void qemu_spice_stop(SimpleSpiceDisplay *ssd); From 3e16b9c53493d6107c5a1ec810ace2447fcb11eb Mon Sep 17 00:00:00 2001 From: Alon Levy Date: Wed, 20 Jul 2011 12:20:59 +0300 Subject: [PATCH 189/230] qxl: add QXL_IO_FLUSH_{SURFACES,RELEASE} for guest S3&S4 support Add two new IOs. QXL_IO_FLUSH_SURFACES - equivalent to update area for all surfaces, used to reduce vmexits from NumSurfaces to 1 on guest S3, S4 and resolution change (windows driver implementation is such that this is done on each of those occasions). QXL_IO_FLUSH_RELEASE - used to ensure anything on last_release is put on the release ring for the client to free. Signed-off-by: Yonit Halperin Signed-off-by: Alon Levy Signed-off-by: Gerd Hoffmann --- hw/qxl.c | 30 ++++++++++++++++++++++++++++++ 1 file changed, 30 insertions(+) diff --git a/hw/qxl.c b/hw/qxl.c index d3109e432d..847a9b8426 100644 --- a/hw/qxl.c +++ b/hw/qxl.c @@ -185,6 +185,13 @@ static void qxl_spice_destroy_surface_wait(PCIQXLDevice *qxl, uint32_t id, } } +#if SPICE_INTERFACE_QXL_MINOR >= 1 +static void qxl_spice_flush_surfaces_async(PCIQXLDevice *qxl) +{ + spice_qxl_flush_surfaces_async(&qxl->ssd.qxl, 0); +} +#endif + void qxl_spice_loadvm_commands(PCIQXLDevice *qxl, struct QXLCommandExt *ext, uint32_t count) { @@ -1184,6 +1191,8 @@ static void ioport_write(void *opaque, uint32_t addr, uint32_t val) goto async_common; case QXL_IO_DESTROY_ALL_SURFACES_ASYNC: io_port = QXL_IO_DESTROY_ALL_SURFACES; + goto async_common; + case QXL_IO_FLUSH_SURFACES_ASYNC: async_common: async = QXL_ASYNC; qemu_mutex_lock(&d->async_lock); @@ -1296,6 +1305,27 @@ async_common: } qxl_spice_destroy_surface_wait(d, val, async); break; +#if SPICE_INTERFACE_QXL_MINOR >= 1 + case QXL_IO_FLUSH_RELEASE: { + QXLReleaseRing *ring = &d->ram->release_ring; + if (ring->prod - ring->cons + 1 == ring->num_items) { + fprintf(stderr, + "ERROR: no flush, full release ring [p%d,%dc]\n", + ring->prod, ring->cons); + } + qxl_push_free_res(d, 1 /* flush */); + dprint(d, 1, "QXL_IO_FLUSH_RELEASE exit (%s, s#=%d, res#=%d,%p)\n", + qxl_mode_to_string(d->mode), d->guest_surfaces.count, + d->num_free_res, d->last_release); + break; + } + case QXL_IO_FLUSH_SURFACES_ASYNC: + dprint(d, 1, "QXL_IO_FLUSH_SURFACES_ASYNC (%d) (%s, s#=%d, res#=%d)\n", + val, qxl_mode_to_string(d->mode), d->guest_surfaces.count, + d->num_free_res); + qxl_spice_flush_surfaces_async(d); + break; +#endif case QXL_IO_DESTROY_ALL_SURFACES: d->mode = QXL_MODE_UNDEFINED; qxl_spice_destroy_surfaces(d, async); From 9197a7c8af41c2a410d5debcf20deabbc20243c0 Mon Sep 17 00:00:00 2001 From: Gerd Hoffmann Date: Wed, 20 Jul 2011 12:21:00 +0300 Subject: [PATCH 190/230] qxl: bump pci rev Inform guest drivers about the new features I/O commands we have now (async commands, S3 support) if building with newer spice, i.e. if SPICE_INTERFACE_QXL_MINOR >= 1. sneaked in some 81+ column line spliting. Signed-off-by: Gerd Hoffmann Signed-off-by: Alon Levy --- hw/qxl.c | 25 ++++++++++++++++++------- hw/qxl.h | 6 ++++++ 2 files changed, 24 insertions(+), 7 deletions(-) diff --git a/hw/qxl.c b/hw/qxl.c index 847a9b8426..b684608f87 100644 --- a/hw/qxl.c +++ b/hw/qxl.c @@ -1566,9 +1566,14 @@ static int qxl_init_common(PCIQXLDevice *qxl) pci_device_rev = QXL_REVISION_STABLE_V04; break; case 2: /* spice 0.6 -- qxl-2 */ - default: pci_device_rev = QXL_REVISION_STABLE_V06; break; +#if SPICE_INTERFACE_QXL_MINOR >= 1 + case 3: /* qxl-3 */ +#endif + default: + pci_device_rev = QXL_DEFAULT_REVISION; + break; } pci_set_byte(&config[PCI_REVISION_ID], pci_device_rev); @@ -1830,9 +1835,12 @@ static PCIDeviceInfo qxl_info_primary = { .device_id = QXL_DEVICE_ID_STABLE, .class_id = PCI_CLASS_DISPLAY_VGA, .qdev.props = (Property[]) { - DEFINE_PROP_UINT32("ram_size", PCIQXLDevice, vga.vram_size, 64 * 1024 * 1024), - DEFINE_PROP_UINT32("vram_size", PCIQXLDevice, vram_size, 64 * 1024 * 1024), - DEFINE_PROP_UINT32("revision", PCIQXLDevice, revision, 2), + DEFINE_PROP_UINT32("ram_size", PCIQXLDevice, vga.vram_size, + 64 * 1024 * 1024), + DEFINE_PROP_UINT32("vram_size", PCIQXLDevice, vram_size, + 64 * 1024 * 1024), + DEFINE_PROP_UINT32("revision", PCIQXLDevice, revision, + QXL_DEFAULT_REVISION), DEFINE_PROP_UINT32("debug", PCIQXLDevice, debug, 0), DEFINE_PROP_UINT32("guestdebug", PCIQXLDevice, guestdebug, 0), DEFINE_PROP_UINT32("cmdlog", PCIQXLDevice, cmdlog, 0), @@ -1851,9 +1859,12 @@ static PCIDeviceInfo qxl_info_secondary = { .device_id = QXL_DEVICE_ID_STABLE, .class_id = PCI_CLASS_DISPLAY_OTHER, .qdev.props = (Property[]) { - DEFINE_PROP_UINT32("ram_size", PCIQXLDevice, vga.vram_size, 64 * 1024 * 1024), - DEFINE_PROP_UINT32("vram_size", PCIQXLDevice, vram_size, 64 * 1024 * 1024), - DEFINE_PROP_UINT32("revision", PCIQXLDevice, revision, 2), + DEFINE_PROP_UINT32("ram_size", PCIQXLDevice, vga.vram_size, + 64 * 1024 * 1024), + DEFINE_PROP_UINT32("vram_size", PCIQXLDevice, vram_size, + 64 * 1024 * 1024), + DEFINE_PROP_UINT32("revision", PCIQXLDevice, revision, + QXL_DEFAULT_REVISION), DEFINE_PROP_UINT32("debug", PCIQXLDevice, debug, 0), DEFINE_PROP_UINT32("guestdebug", PCIQXLDevice, guestdebug, 0), DEFINE_PROP_UINT32("cmdlog", PCIQXLDevice, cmdlog, 0), diff --git a/hw/qxl.h b/hw/qxl.h index 1046205ef8..4bcf7e1429 100644 --- a/hw/qxl.h +++ b/hw/qxl.h @@ -102,6 +102,12 @@ typedef struct PCIQXLDevice { } \ } while (0) +#if SPICE_INTERFACE_QXL_MINOR >= 1 +#define QXL_DEFAULT_REVISION QXL_REVISION_STABLE_V10 +#else +#define QXL_DEFAULT_REVISION QXL_REVISION_STABLE_V06 +#endif + /* qxl.c */ void *qxl_phys2virt(PCIQXLDevice *qxl, QXLPHYSICAL phys, int group_id); void qxl_guest_bug(PCIQXLDevice *qxl, const char *msg, ...); From d41a75a20f2e8f9ccc64c27b61f730e4cb4ee2c5 Mon Sep 17 00:00:00 2001 From: Brad Date: Tue, 26 Jul 2011 23:11:26 -0400 Subject: [PATCH 191/230] configure: display "no" for disabled kvm/vhost-net Fix configure display for non-Linux OS's and the KVM / vhost-net features to show "no" output instead of nothing at the end of the line. Signed-off-by: Brad Smith Acked-by: Jan Kiszka Signed-off-by: Stefan Hajnoczi --- configure | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/configure b/configure index 77194cf9a7..9d467802dd 100755 --- a/configure +++ b/configure @@ -113,7 +113,6 @@ curl="" curses="" docs="" fdt="" -kvm="" nptl="" sdl="" vnc="yes" @@ -129,9 +128,10 @@ xen="" xen_ctrl_version="" linux_aio="" attr="" -vhost_net="" xfs="" +vhost_net="no" +kvm="no" gprof="no" debug_tcg="no" debug_mon="no" From 0f1b583ee7db23cb31f68a9d9b55755ca95f9f3f Mon Sep 17 00:00:00 2001 From: Zhi Yong Wu Date: Wed, 27 Jul 2011 17:48:16 +0800 Subject: [PATCH 192/230] HMP: Remove the duplicated info "info kvm" in hmp-commands.hx. Signed-off-by: Zhi Yong Wu Signed-off-by: Stefan Hajnoczi --- hmp-commands.hx | 2 -- 1 file changed, 2 deletions(-) diff --git a/hmp-commands.hx b/hmp-commands.hx index c857827618..0ccfb2867f 100644 --- a/hmp-commands.hx +++ b/hmp-commands.hx @@ -1311,8 +1311,6 @@ show virtual to physical memory mappings (i386, SH4 and SPARC only) show the active virtual memory mappings (i386 only) @item info jit show dynamic compiler info -@item info kvm -show KVM information @item info numa show NUMA information @item info kvm From 793553acb3d70ae64e459fa067486c6c741133e7 Mon Sep 17 00:00:00 2001 From: Alexandre Raymond Date: Mon, 25 Jul 2011 23:56:02 -0400 Subject: [PATCH 193/230] Makefile: delete config.log in distclean Distclean should remove anything created by the configure script. Signed-off-by: Alexandre Raymond Signed-off-by: Stefan Hajnoczi --- Makefile | 1 + 1 file changed, 1 insertion(+) diff --git a/Makefile b/Makefile index 48552512d6..a0218a309b 100644 --- a/Makefile +++ b/Makefile @@ -226,6 +226,7 @@ distclean: clean rm -f qemu-doc.fn qemu-doc.fns qemu-doc.info qemu-doc.ky qemu-doc.kys rm -f qemu-doc.log qemu-doc.pdf qemu-doc.pg qemu-doc.toc qemu-doc.tp rm -f qemu-doc.vr + rm -f config.log rm -f qemu-tech.info qemu-tech.aux qemu-tech.cp qemu-tech.dvi qemu-tech.fn qemu-tech.info qemu-tech.ky qemu-tech.log qemu-tech.pdf qemu-tech.pg qemu-tech.toc qemu-tech.tp qemu-tech.vr for d in $(TARGET_DIRS) $(QEMULIBS); do \ rm -rf $$d || exit 1 ; \ From 1a0ca1e1f6011a8623ec0653a1b35bbfc3f576c9 Mon Sep 17 00:00:00 2001 From: Fabien Chouteau Date: Wed, 3 Aug 2011 12:52:54 +0200 Subject: [PATCH 194/230] Simple ARP table This patch adds a simple ARP table in Slirp and also adds handling of gratuitous ARP requests. Signed-off-by: Fabien Chouteau Signed-off-by: Jan Kiszka --- Makefile.objs | 2 +- slirp/arp_table.c | 95 +++++++++++++++++++++++++++++++++++++++++++++++ slirp/bootp.c | 21 +++++++---- slirp/slirp.c | 63 ++++++++----------------------- slirp/slirp.h | 47 +++++++++++++++++++++-- 5 files changed, 169 insertions(+), 59 deletions(-) create mode 100644 slirp/arp_table.c diff --git a/Makefile.objs b/Makefile.objs index 6991a9f52a..0c10557097 100644 --- a/Makefile.objs +++ b/Makefile.objs @@ -151,7 +151,7 @@ common-obj-y += qemu-timer.o qemu-timer-common.o slirp-obj-y = cksum.o if.o ip_icmp.o ip_input.o ip_output.o slirp-obj-y += slirp.o mbuf.o misc.o sbuf.o socket.o tcp_input.o tcp_output.o -slirp-obj-y += tcp_subr.o tcp_timer.o udp.o bootp.o tftp.o +slirp-obj-y += tcp_subr.o tcp_timer.o udp.o bootp.o tftp.o arp_table.o common-obj-$(CONFIG_SLIRP) += $(addprefix slirp/, $(slirp-obj-y)) # xen backend driver support diff --git a/slirp/arp_table.c b/slirp/arp_table.c new file mode 100644 index 0000000000..820dee22b0 --- /dev/null +++ b/slirp/arp_table.c @@ -0,0 +1,95 @@ +/* + * ARP table + * + * Copyright (c) 2011 AdaCore + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +#include "slirp.h" + +void arp_table_add(Slirp *slirp, int ip_addr, uint8_t ethaddr[ETH_ALEN]) +{ + const in_addr_t broadcast_addr = + ~slirp->vnetwork_mask.s_addr | slirp->vnetwork_addr.s_addr; + ArpTable *arptbl = &slirp->arp_table; + int i; + + DEBUG_CALL("arp_table_add"); + DEBUG_ARG("ip = 0x%x", ip_addr); + DEBUG_ARGS((dfd, " hw addr = %02x:%02x:%02x:%02x:%02x:%02x\n", + ethaddr[0], ethaddr[1], ethaddr[2], + ethaddr[3], ethaddr[4], ethaddr[5])); + + /* Check 0.0.0.0/8 invalid source-only addresses */ + assert((ip_addr & htonl(~(0xf << 28))) != 0); + + if (ip_addr == 0xffffffff || ip_addr == broadcast_addr) { + /* Do not register broadcast addresses */ + return; + } + + /* Search for an entry */ + for (i = 0; i < ARP_TABLE_SIZE; i++) { + if (arptbl->table[i].ar_sip == ip_addr) { + /* Update the entry */ + memcpy(arptbl->table[i].ar_sha, ethaddr, ETH_ALEN); + return; + } + } + + /* No entry found, create a new one */ + arptbl->table[arptbl->next_victim].ar_sip = ip_addr; + memcpy(arptbl->table[arptbl->next_victim].ar_sha, ethaddr, ETH_ALEN); + arptbl->next_victim = (arptbl->next_victim + 1) % ARP_TABLE_SIZE; +} + +bool arp_table_search(Slirp *slirp, int in_ip_addr, + uint8_t out_ethaddr[ETH_ALEN]) +{ + const in_addr_t broadcast_addr = + ~slirp->vnetwork_mask.s_addr | slirp->vnetwork_addr.s_addr; + ArpTable *arptbl = &slirp->arp_table; + int i; + + DEBUG_CALL("arp_table_search"); + DEBUG_ARG("ip = 0x%x", in_ip_addr); + + /* Check 0.0.0.0/8 invalid source-only addresses */ + assert((in_ip_addr & htonl(~(0xf << 28))) != 0); + + /* If broadcast address */ + if (in_ip_addr == 0xffffffff || in_ip_addr == broadcast_addr) { + /* return Ethernet broadcast address */ + memset(out_ethaddr, 0xff, ETH_ALEN); + return 1; + } + + for (i = 0; i < ARP_TABLE_SIZE; i++) { + if (arptbl->table[i].ar_sip == in_ip_addr) { + memcpy(out_ethaddr, arptbl->table[i].ar_sha, ETH_ALEN); + DEBUG_ARGS((dfd, " found hw addr = %02x:%02x:%02x:%02x:%02x:%02x\n", + out_ethaddr[0], out_ethaddr[1], out_ethaddr[2], + out_ethaddr[3], out_ethaddr[4], out_ethaddr[5])); + return 1; + } + } + + return 0; +} diff --git a/slirp/bootp.c b/slirp/bootp.c index 1eb2ed1143..efd1fe777a 100644 --- a/slirp/bootp.c +++ b/slirp/bootp.c @@ -149,6 +149,7 @@ static void bootp_reply(Slirp *slirp, const struct bootp_t *bp) struct in_addr preq_addr; int dhcp_msg_type, val; uint8_t *q; + uint8_t client_ethaddr[ETH_ALEN]; /* extract exact DHCP msg type */ dhcp_decode(bp, &dhcp_msg_type, &preq_addr); @@ -164,8 +165,9 @@ static void bootp_reply(Slirp *slirp, const struct bootp_t *bp) if (dhcp_msg_type != DHCPDISCOVER && dhcp_msg_type != DHCPREQUEST) return; - /* XXX: this is a hack to get the client mac address */ - memcpy(slirp->client_ethaddr, bp->bp_hwaddr, 6); + + /* Get client's hardware address from bootp request */ + memcpy(client_ethaddr, bp->bp_hwaddr, ETH_ALEN); m = m_get(slirp); if (!m) { @@ -178,25 +180,25 @@ static void bootp_reply(Slirp *slirp, const struct bootp_t *bp) if (dhcp_msg_type == DHCPDISCOVER) { if (preq_addr.s_addr != htonl(0L)) { - bc = request_addr(slirp, &preq_addr, slirp->client_ethaddr); + bc = request_addr(slirp, &preq_addr, client_ethaddr); if (bc) { daddr.sin_addr = preq_addr; } } if (!bc) { new_addr: - bc = get_new_addr(slirp, &daddr.sin_addr, slirp->client_ethaddr); + bc = get_new_addr(slirp, &daddr.sin_addr, client_ethaddr); if (!bc) { DPRINTF("no address left\n"); return; } } - memcpy(bc->macaddr, slirp->client_ethaddr, 6); + memcpy(bc->macaddr, client_ethaddr, ETH_ALEN); } else if (preq_addr.s_addr != htonl(0L)) { - bc = request_addr(slirp, &preq_addr, slirp->client_ethaddr); + bc = request_addr(slirp, &preq_addr, client_ethaddr); if (bc) { daddr.sin_addr = preq_addr; - memcpy(bc->macaddr, slirp->client_ethaddr, 6); + memcpy(bc->macaddr, client_ethaddr, ETH_ALEN); } else { daddr.sin_addr.s_addr = 0; } @@ -209,6 +211,9 @@ static void bootp_reply(Slirp *slirp, const struct bootp_t *bp) } } + /* Update ARP table for this IP address */ + arp_table_add(slirp, daddr.sin_addr.s_addr, client_ethaddr); + saddr.sin_addr = slirp->vhost_addr; saddr.sin_port = htons(BOOTP_SERVER); @@ -218,7 +223,7 @@ static void bootp_reply(Slirp *slirp, const struct bootp_t *bp) rbp->bp_xid = bp->bp_xid; rbp->bp_htype = 1; rbp->bp_hlen = 6; - memcpy(rbp->bp_hwaddr, bp->bp_hwaddr, 6); + memcpy(rbp->bp_hwaddr, bp->bp_hwaddr, ETH_ALEN); rbp->bp_yiaddr = daddr.sin_addr; /* Client IP address */ rbp->bp_siaddr = saddr.sin_addr; /* Server IP address */ diff --git a/slirp/slirp.c b/slirp/slirp.c index df787ea1d9..4a9a4d5121 100644 --- a/slirp/slirp.c +++ b/slirp/slirp.c @@ -31,11 +31,11 @@ struct in_addr loopback_addr; /* emulated hosts use the MAC addr 52:55:IP:IP:IP:IP */ -static const uint8_t special_ethaddr[6] = { +static const uint8_t special_ethaddr[ETH_ALEN] = { 0x52, 0x55, 0x00, 0x00, 0x00, 0x00 }; -static const uint8_t zero_ethaddr[6] = { 0, 0, 0, 0, 0, 0 }; +static const uint8_t zero_ethaddr[ETH_ALEN] = { 0, 0, 0, 0, 0, 0 }; /* XXX: suppress those select globals */ fd_set *global_readfds, *global_writefds, *global_xfds; @@ -599,42 +599,8 @@ void slirp_select_poll(fd_set *readfds, fd_set *writefds, fd_set *xfds, global_xfds = NULL; } -#define ETH_ALEN 6 -#define ETH_HLEN 14 - -#define ETH_P_IP 0x0800 /* Internet Protocol packet */ -#define ETH_P_ARP 0x0806 /* Address Resolution packet */ - -#define ARPOP_REQUEST 1 /* ARP request */ -#define ARPOP_REPLY 2 /* ARP reply */ - -struct ethhdr -{ - unsigned char h_dest[ETH_ALEN]; /* destination eth addr */ - unsigned char h_source[ETH_ALEN]; /* source ether addr */ - unsigned short h_proto; /* packet type ID field */ -}; - -struct arphdr -{ - unsigned short ar_hrd; /* format of hardware address */ - unsigned short ar_pro; /* format of protocol address */ - unsigned char ar_hln; /* length of hardware address */ - unsigned char ar_pln; /* length of protocol address */ - unsigned short ar_op; /* ARP opcode (command) */ - - /* - * Ethernet looks like this : This bit is variable sized however... - */ - unsigned char ar_sha[ETH_ALEN]; /* sender hardware address */ - uint32_t ar_sip; /* sender IP address */ - unsigned char ar_tha[ETH_ALEN]; /* target hardware address */ - uint32_t ar_tip ; /* target IP address */ -} __attribute__((packed)); - static void arp_input(Slirp *slirp, const uint8_t *pkt, int pkt_len) { - struct ethhdr *eh = (struct ethhdr *)pkt; struct arphdr *ah = (struct arphdr *)(pkt + ETH_HLEN); uint8_t arp_reply[max(ETH_HLEN + sizeof(struct arphdr), 64)]; struct ethhdr *reh = (struct ethhdr *)arp_reply; @@ -645,6 +611,12 @@ static void arp_input(Slirp *slirp, const uint8_t *pkt, int pkt_len) ar_op = ntohs(ah->ar_op); switch(ar_op) { case ARPOP_REQUEST: + if (ah->ar_tip == ah->ar_sip) { + /* Gratuitous ARP */ + arp_table_add(slirp, ah->ar_sip, ah->ar_sha); + return; + } + if ((ah->ar_tip & slirp->vnetwork_mask.s_addr) == slirp->vnetwork_addr.s_addr) { if (ah->ar_tip == slirp->vnameserver_addr.s_addr || @@ -657,8 +629,8 @@ static void arp_input(Slirp *slirp, const uint8_t *pkt, int pkt_len) return; arp_ok: memset(arp_reply, 0, sizeof(arp_reply)); - /* XXX: make an ARP request to have the client address */ - memcpy(slirp->client_ethaddr, eh->h_source, ETH_ALEN); + + arp_table_add(slirp, ah->ar_sip, ah->ar_sha); /* ARP request for alias/dns mac address */ memcpy(reh->h_dest, pkt + ETH_ALEN, ETH_ALEN); @@ -679,11 +651,7 @@ static void arp_input(Slirp *slirp, const uint8_t *pkt, int pkt_len) } break; case ARPOP_REPLY: - /* reply to request of client mac address ? */ - if (!memcmp(slirp->client_ethaddr, zero_ethaddr, ETH_ALEN) && - ah->ar_sip == slirp->client_ipaddr.s_addr) { - memcpy(slirp->client_ethaddr, ah->ar_sha, ETH_ALEN); - } + arp_table_add(slirp, ah->ar_sip, ah->ar_sha); break; default: break; @@ -729,15 +697,16 @@ void if_encap(Slirp *slirp, const uint8_t *ip_data, int ip_data_len) { uint8_t buf[1600]; struct ethhdr *eh = (struct ethhdr *)buf; + uint8_t ethaddr[ETH_ALEN]; + const struct ip *iph = (const struct ip *)ip_data; if (ip_data_len + ETH_HLEN > sizeof(buf)) return; - - if (!memcmp(slirp->client_ethaddr, zero_ethaddr, ETH_ALEN)) { + + if (!arp_table_search(slirp, iph->ip_dst.s_addr, ethaddr)) { uint8_t arp_req[ETH_HLEN + sizeof(struct arphdr)]; struct ethhdr *reh = (struct ethhdr *)arp_req; struct arphdr *rah = (struct arphdr *)(arp_req + ETH_HLEN); - const struct ip *iph = (const struct ip *)ip_data; /* If the client addr is not known, there is no point in sending the packet to it. Normally the sender should have @@ -765,7 +734,7 @@ void if_encap(Slirp *slirp, const uint8_t *ip_data, int ip_data_len) slirp->client_ipaddr = iph->ip_dst; slirp_output(slirp->opaque, arp_req, sizeof(arp_req)); } else { - memcpy(eh->h_dest, slirp->client_ethaddr, ETH_ALEN); + memcpy(eh->h_dest, ethaddr, ETH_ALEN); memcpy(eh->h_source, special_ethaddr, ETH_ALEN - 4); /* XXX: not correct */ memcpy(&eh->h_source[2], &slirp->vhost_addr, 4); diff --git a/slirp/slirp.h b/slirp/slirp.h index 16bb6bae45..2a070e6126 100644 --- a/slirp/slirp.h +++ b/slirp/slirp.h @@ -170,6 +170,48 @@ int inet_aton(const char *cp, struct in_addr *ia); /* osdep.c */ int qemu_socket(int domain, int type, int protocol); +#define ETH_ALEN 6 +#define ETH_HLEN 14 + +#define ETH_P_IP 0x0800 /* Internet Protocol packet */ +#define ETH_P_ARP 0x0806 /* Address Resolution packet */ + +#define ARPOP_REQUEST 1 /* ARP request */ +#define ARPOP_REPLY 2 /* ARP reply */ + +struct ethhdr { + unsigned char h_dest[ETH_ALEN]; /* destination eth addr */ + unsigned char h_source[ETH_ALEN]; /* source ether addr */ + unsigned short h_proto; /* packet type ID field */ +}; + +struct arphdr { + unsigned short ar_hrd; /* format of hardware address */ + unsigned short ar_pro; /* format of protocol address */ + unsigned char ar_hln; /* length of hardware address */ + unsigned char ar_pln; /* length of protocol address */ + unsigned short ar_op; /* ARP opcode (command) */ + + /* + * Ethernet looks like this : This bit is variable sized however... + */ + unsigned char ar_sha[ETH_ALEN]; /* sender hardware address */ + uint32_t ar_sip; /* sender IP address */ + unsigned char ar_tha[ETH_ALEN]; /* target hardware address */ + uint32_t ar_tip; /* target IP address */ +} __attribute__((packed)); + +#define ARP_TABLE_SIZE 16 + +typedef struct ArpTable { + struct arphdr table[ARP_TABLE_SIZE]; + int next_victim; +} ArpTable; + +void arp_table_add(Slirp *slirp, int ip_addr, uint8_t ethaddr[ETH_ALEN]); + +bool arp_table_search(Slirp *slirp, int in_ip_addr, + uint8_t out_ethaddr[ETH_ALEN]); struct Slirp { QTAILQ_ENTRY(Slirp) entry; @@ -181,9 +223,6 @@ struct Slirp { struct in_addr vdhcp_startaddr; struct in_addr vnameserver_addr; - /* ARP cache for the guest IP addresses (XXX: allow many entries) */ - uint8_t client_ethaddr[6]; - struct in_addr client_ipaddr; char client_hostname[33]; @@ -227,6 +266,8 @@ struct Slirp { char *tftp_prefix; struct tftp_session tftp_sessions[TFTP_SESSIONS_MAX]; + ArpTable arp_table; + void *opaque; }; From 1ab74cea060d776b19857c3babc64d729bbdba5c Mon Sep 17 00:00:00 2001 From: Fabien Chouteau Date: Mon, 1 Aug 2011 18:18:37 +0200 Subject: [PATCH 195/230] Delayed IP packets In the current implementation, if Slirp tries to send an IP packet to a client with an unknown hardware address, the packet is simply dropped and an ARP request is sent (if_encap in slirp/slirp.c). With this patch, Slirp will send the ARP request, re-queue the packet and try to send it later. The packet is dropped after one second if the ARP reply is not received. Signed-off-by: Fabien Chouteau Signed-off-by: Jan Kiszka --- slirp/if.c | 28 +++++++++++++++++--- slirp/main.h | 2 +- slirp/mbuf.c | 2 ++ slirp/mbuf.h | 2 ++ slirp/slirp.c | 72 ++++++++++++++++++++++++++++----------------------- 5 files changed, 69 insertions(+), 37 deletions(-) diff --git a/slirp/if.c b/slirp/if.c index 0f04e13989..2d79e45bcd 100644 --- a/slirp/if.c +++ b/slirp/if.c @@ -6,6 +6,7 @@ */ #include +#include "qemu-timer.h" #define ifs_init(ifm) ((ifm)->ifs_next = (ifm)->ifs_prev = (ifm)) @@ -105,6 +106,9 @@ if_output(struct socket *so, struct mbuf *ifm) ifs_init(ifm); insque(ifm, ifq); + /* Expiration date = Now + 1 second */ + ifm->expiration_date = qemu_get_clock_ns(rt_clock) + 1000000000ULL; + diddit: slirp->if_queued++; @@ -153,6 +157,9 @@ diddit: void if_start(Slirp *slirp) { + int requeued = 0; + uint64_t now; + struct mbuf *ifm, *ifqt; DEBUG_CALL("if_start"); @@ -165,6 +172,8 @@ if_start(Slirp *slirp) if (!slirp_can_output(slirp->opaque)) return; + now = qemu_get_clock_ns(rt_clock); + /* * See which queue to get next packet from * If there's something in the fastq, select it immediately @@ -199,11 +208,22 @@ if_start(Slirp *slirp) ifm->ifq_so->so_nqueued = 0; } - /* Encapsulate the packet for sending */ - if_encap(slirp, (uint8_t *)ifm->m_data, ifm->m_len); - - m_free(ifm); + if (ifm->expiration_date < now) { + /* Expired */ + m_free(ifm); + } else { + /* Encapsulate the packet for sending */ + if (if_encap(slirp, ifm)) { + m_free(ifm); + } else { + /* re-queue */ + insque(ifm, ifqt); + requeued++; + } + } if (slirp->if_queued) goto again; + + slirp->if_queued = requeued; } diff --git a/slirp/main.h b/slirp/main.h index 0dd8d81ce4..028df4b361 100644 --- a/slirp/main.h +++ b/slirp/main.h @@ -42,5 +42,5 @@ extern int tcp_keepintvl; #define PROTO_PPP 0x2 #endif -void if_encap(Slirp *slirp, const uint8_t *ip_data, int ip_data_len); +int if_encap(Slirp *slirp, struct mbuf *ifm); ssize_t slirp_send(struct socket *so, const void *buf, size_t len, int flags); diff --git a/slirp/mbuf.c b/slirp/mbuf.c index ce2eb843f5..c699c75096 100644 --- a/slirp/mbuf.c +++ b/slirp/mbuf.c @@ -70,6 +70,8 @@ m_get(Slirp *slirp) m->m_len = 0; m->m_nextpkt = NULL; m->m_prevpkt = NULL; + m->arp_requested = false; + m->expiration_date = (uint64_t)-1; end_error: DEBUG_ARG("m = %lx", (long )m); return m; diff --git a/slirp/mbuf.h b/slirp/mbuf.h index b74544b42b..55170e517b 100644 --- a/slirp/mbuf.h +++ b/slirp/mbuf.h @@ -86,6 +86,8 @@ struct mbuf { char m_dat_[1]; /* ANSI don't like 0 sized arrays */ char *m_ext_; } M_dat; + bool arp_requested; + uint64_t expiration_date; }; #define m_next m_hdr.mh_next diff --git a/slirp/slirp.c b/slirp/slirp.c index 4a9a4d5121..a86cc6eb2d 100644 --- a/slirp/slirp.c +++ b/slirp/slirp.c @@ -692,55 +692,63 @@ void slirp_input(Slirp *slirp, const uint8_t *pkt, int pkt_len) } } -/* output the IP packet to the ethernet device */ -void if_encap(Slirp *slirp, const uint8_t *ip_data, int ip_data_len) +/* Output the IP packet to the ethernet device. Returns 0 if the packet must be + * re-queued. + */ +int if_encap(Slirp *slirp, struct mbuf *ifm) { uint8_t buf[1600]; struct ethhdr *eh = (struct ethhdr *)buf; uint8_t ethaddr[ETH_ALEN]; - const struct ip *iph = (const struct ip *)ip_data; + const struct ip *iph = (const struct ip *)ifm->m_data; - if (ip_data_len + ETH_HLEN > sizeof(buf)) - return; + if (ifm->m_len + ETH_HLEN > sizeof(buf)) { + return 1; + } if (!arp_table_search(slirp, iph->ip_dst.s_addr, ethaddr)) { uint8_t arp_req[ETH_HLEN + sizeof(struct arphdr)]; struct ethhdr *reh = (struct ethhdr *)arp_req; struct arphdr *rah = (struct arphdr *)(arp_req + ETH_HLEN); - /* If the client addr is not known, there is no point in - sending the packet to it. Normally the sender should have - done an ARP request to get its MAC address. Here we do it - in place of sending the packet and we hope that the sender - will retry sending its packet. */ - memset(reh->h_dest, 0xff, ETH_ALEN); - memcpy(reh->h_source, special_ethaddr, ETH_ALEN - 4); - memcpy(&reh->h_source[2], &slirp->vhost_addr, 4); - reh->h_proto = htons(ETH_P_ARP); - rah->ar_hrd = htons(1); - rah->ar_pro = htons(ETH_P_IP); - rah->ar_hln = ETH_ALEN; - rah->ar_pln = 4; - rah->ar_op = htons(ARPOP_REQUEST); - /* source hw addr */ - memcpy(rah->ar_sha, special_ethaddr, ETH_ALEN - 4); - memcpy(&rah->ar_sha[2], &slirp->vhost_addr, 4); - /* source IP */ - rah->ar_sip = slirp->vhost_addr.s_addr; - /* target hw addr (none) */ - memset(rah->ar_tha, 0, ETH_ALEN); - /* target IP */ - rah->ar_tip = iph->ip_dst.s_addr; - slirp->client_ipaddr = iph->ip_dst; - slirp_output(slirp->opaque, arp_req, sizeof(arp_req)); + if (!ifm->arp_requested) { + /* If the client addr is not known, send an ARP request */ + memset(reh->h_dest, 0xff, ETH_ALEN); + memcpy(reh->h_source, special_ethaddr, ETH_ALEN - 4); + memcpy(&reh->h_source[2], &slirp->vhost_addr, 4); + reh->h_proto = htons(ETH_P_ARP); + rah->ar_hrd = htons(1); + rah->ar_pro = htons(ETH_P_IP); + rah->ar_hln = ETH_ALEN; + rah->ar_pln = 4; + rah->ar_op = htons(ARPOP_REQUEST); + + /* source hw addr */ + memcpy(rah->ar_sha, special_ethaddr, ETH_ALEN - 4); + memcpy(&rah->ar_sha[2], &slirp->vhost_addr, 4); + + /* source IP */ + rah->ar_sip = slirp->vhost_addr.s_addr; + + /* target hw addr (none) */ + memset(rah->ar_tha, 0, ETH_ALEN); + + /* target IP */ + rah->ar_tip = iph->ip_dst.s_addr; + slirp->client_ipaddr = iph->ip_dst; + slirp_output(slirp->opaque, arp_req, sizeof(arp_req)); + ifm->arp_requested = true; + } + return 0; } else { memcpy(eh->h_dest, ethaddr, ETH_ALEN); memcpy(eh->h_source, special_ethaddr, ETH_ALEN - 4); /* XXX: not correct */ memcpy(&eh->h_source[2], &slirp->vhost_addr, 4); eh->h_proto = htons(ETH_P_IP); - memcpy(buf + sizeof(struct ethhdr), ip_data, ip_data_len); - slirp_output(slirp->opaque, buf, ip_data_len + ETH_HLEN); + memcpy(buf + sizeof(struct ethhdr), ifm->m_data, ifm->m_len); + slirp_output(slirp->opaque, buf, ifm->m_len + ETH_HLEN); + return 1; } } From cb4b4fde82b064472c13fb9d983ca36a70e560aa Mon Sep 17 00:00:00 2001 From: "Michael S. Tsirkin" Date: Wed, 3 Aug 2011 15:24:41 +0300 Subject: [PATCH 196/230] vhost: remove an incorrect assert The 'to' can go negative when the first region gets removed (it gets incremented by to 0 immediately afterward), which makes the assertion fail. Nothing breaks if to < 0 here so just remove the assert. Tested-by: David Ahern Signed-off-by: Michael S. Tsirkin --- hw/vhost.c | 1 - 1 file changed, 1 deletion(-) diff --git a/hw/vhost.c b/hw/vhost.c index c3d88214fe..19e72555c4 100644 --- a/hw/vhost.c +++ b/hw/vhost.c @@ -120,7 +120,6 @@ static void vhost_dev_unassign_memory(struct vhost_dev *dev, if (start_addr <= reg->guest_phys_addr && memlast >= reglast) { --dev->mem->nregions; --to; - assert(to >= 0); ++overlap_middle; continue; } From a6f4e09d90cef88be07cd597c2f2a9f0b3ed0763 Mon Sep 17 00:00:00 2001 From: Michael Walle Date: Thu, 21 Jul 2011 20:52:24 +0200 Subject: [PATCH 197/230] lm32: softusb: claim to support full speed The QEMU keyboard and mouse reports themselves as full speed devices, though they are actually low speed devices. Until this is fixed, claim that we are supporting full speed devices. Acked-by: Gerd Hoffmann Signed-off-by: Michael Walle Signed-off-by: Edgar E. Iglesias --- hw/milkymist-softusb.c | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/hw/milkymist-softusb.c b/hw/milkymist-softusb.c index ce2bfc60f2..abf7b59acd 100644 --- a/hw/milkymist-softusb.c +++ b/hw/milkymist-softusb.c @@ -310,10 +310,12 @@ static int milkymist_softusb_init(SysBusDevice *dev) usb_bus_new(&s->usbbus, &softusb_bus_ops, NULL); /* our two ports */ + /* FIXME: claim to support full speed devices. qemu mouse and keyboard + * report themselves as full speed devices. */ usb_register_port(&s->usbbus, &s->usbport[0], NULL, 0, &softusb_ops, - USB_SPEED_MASK_LOW); + USB_SPEED_MASK_LOW | USB_SPEED_MASK_FULL); usb_register_port(&s->usbbus, &s->usbport[1], NULL, 1, &softusb_ops, - USB_SPEED_MASK_LOW); + USB_SPEED_MASK_LOW | USB_SPEED_MASK_FULL); /* and finally create an usb keyboard */ s->usbdev = usb_create_simple(&s->usbbus, "usb-kbd"); From e7a8a7837a964e0fe327e6ef8dde02c6a53dd14a Mon Sep 17 00:00:00 2001 From: Kevin Wolf Date: Fri, 15 Jul 2011 16:05:00 +0200 Subject: [PATCH 198/230] block: Use bdrv_co_* instead of synchronous versions in coroutines If we're already in a coroutine, there is no reason to use the synchronous version of block layer functions when a coroutine one exists. This makes bdrv_read/write/flush use bdrv_co_* when used inside a coroutine. Signed-off-by: Kevin Wolf --- block.c | 56 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 56 insertions(+) diff --git a/block.c b/block.c index 0d05b4b32d..26910ca143 100644 --- a/block.c +++ b/block.c @@ -70,6 +70,7 @@ static int coroutine_fn bdrv_co_readv_em(BlockDriverState *bs, static int coroutine_fn bdrv_co_writev_em(BlockDriverState *bs, int64_t sector_num, int nb_sectors, QEMUIOVector *iov); +static int coroutine_fn bdrv_co_flush_em(BlockDriverState *bs); static QTAILQ_HEAD(, BlockDriverState) bdrv_states = QTAILQ_HEAD_INITIALIZER(bdrv_states); @@ -946,6 +947,17 @@ static int bdrv_check_request(BlockDriverState *bs, int64_t sector_num, nb_sectors * BDRV_SECTOR_SIZE); } +static inline bool bdrv_has_async_rw(BlockDriver *drv) +{ + return drv->bdrv_co_readv != bdrv_co_readv_em + || drv->bdrv_aio_readv != bdrv_aio_readv_em; +} + +static inline bool bdrv_has_async_flush(BlockDriver *drv) +{ + return drv->bdrv_aio_flush != bdrv_aio_flush_em; +} + /* return < 0 if error. See bdrv_write() for the return codes */ int bdrv_read(BlockDriverState *bs, int64_t sector_num, uint8_t *buf, int nb_sectors) @@ -954,6 +966,18 @@ int bdrv_read(BlockDriverState *bs, int64_t sector_num, if (!drv) return -ENOMEDIUM; + + if (bdrv_has_async_rw(drv) && qemu_in_coroutine()) { + QEMUIOVector qiov; + struct iovec iov = { + .iov_base = (void *)buf, + .iov_len = nb_sectors * BDRV_SECTOR_SIZE, + }; + + qemu_iovec_init_external(&qiov, &iov, 1); + return bdrv_co_readv(bs, sector_num, nb_sectors, &qiov); + } + if (bdrv_check_request(bs, sector_num, nb_sectors)) return -EIO; @@ -998,8 +1022,21 @@ int bdrv_write(BlockDriverState *bs, int64_t sector_num, const uint8_t *buf, int nb_sectors) { BlockDriver *drv = bs->drv; + if (!bs->drv) return -ENOMEDIUM; + + if (bdrv_has_async_rw(drv) && qemu_in_coroutine()) { + QEMUIOVector qiov; + struct iovec iov = { + .iov_base = (void *)buf, + .iov_len = nb_sectors * BDRV_SECTOR_SIZE, + }; + + qemu_iovec_init_external(&qiov, &iov, 1); + return bdrv_co_writev(bs, sector_num, nb_sectors, &qiov); + } + if (bs->read_only) return -EACCES; if (bdrv_check_request(bs, sector_num, nb_sectors)) @@ -1649,6 +1686,10 @@ int bdrv_flush(BlockDriverState *bs) return 0; } + if (bs->drv && bdrv_has_async_flush(bs->drv) && qemu_in_coroutine()) { + return bdrv_co_flush_em(bs); + } + if (bs->drv && bs->drv->bdrv_flush) { return bs->drv->bdrv_flush(bs); } @@ -2916,6 +2957,21 @@ static int coroutine_fn bdrv_co_writev_em(BlockDriverState *bs, return bdrv_co_io_em(bs, sector_num, nb_sectors, iov, true); } +static int coroutine_fn bdrv_co_flush_em(BlockDriverState *bs) +{ + CoroutineIOCompletion co = { + .coroutine = qemu_coroutine_self(), + }; + BlockDriverAIOCB *acb; + + acb = bdrv_aio_flush(bs, bdrv_co_io_em_complete, &co); + if (!acb) { + return -EIO; + } + qemu_coroutine_yield(); + return co.ret; +} + /**************************************************************/ /* removable device support */ From fa57ee8ed246cfea53acd09663203deda64b4f33 Mon Sep 17 00:00:00 2001 From: Gerd Hoffmann Date: Thu, 4 Aug 2011 14:54:19 +0200 Subject: [PATCH 199/230] re-activate usb-host for bsd A bunch of code was disabled via #if 0, for a quite long time (since Sept 2009). Surprisingly the code builds just fine when they are removed (tested on OpenBSD). /me wonders nevertheless whenever there are any users of those bits when this went unnoticed for almost two years ... Signed-off-by: Gerd Hoffmann --- usb-bsd.c | 10 ++-------- 1 file changed, 2 insertions(+), 8 deletions(-) diff --git a/usb-bsd.c b/usb-bsd.c index 3b97eb491b..ab8e3b7294 100644 --- a/usb-bsd.c +++ b/usb-bsd.c @@ -62,7 +62,6 @@ typedef struct USBHostDevice { } USBHostDevice; -#if 0 static int ensure_ep_open(USBHostDevice *dev, int ep, int mode) { char buf[32]; @@ -110,7 +109,6 @@ static void ensure_eps_closed(USBHostDevice *dev) epnum++; } } -#endif static void usb_host_handle_reset(USBDevice *dev) { @@ -119,7 +117,6 @@ static void usb_host_handle_reset(USBDevice *dev) #endif } -#if 0 /* XXX: * -check device states against transfer requests * and return appropriate response @@ -278,7 +275,6 @@ static int usb_host_handle_data(USBDevice *dev, USBPacket *p) return ret; } } -#endif static void usb_host_handle_destroy(USBDevice *opaque) { @@ -305,8 +301,8 @@ static int usb_host_initfn(USBDevice *dev) USBDevice *usb_host_device_open(const char *devname) { struct usb_device_info bus_info, dev_info; - USBDevice *d = NULL; - USBHostDevice *dev, *ret = NULL; + USBDevice *d = NULL, *ret = NULL; + USBHostDevice *dev; char ctlpath[PATH_MAX + 1]; char buspath[PATH_MAX + 1]; int bfd, dfd, bus, address, i; @@ -408,10 +404,8 @@ static struct USBDeviceInfo usb_host_dev_info = { .init = usb_host_initfn, .handle_packet = usb_generic_handle_packet, .handle_reset = usb_host_handle_reset, -#if 0 .handle_control = usb_host_handle_control, .handle_data = usb_host_handle_data, -#endif .handle_destroy = usb_host_handle_destroy, }; From 3a1dca94d6dba00fe0fd4c4a28449f57e01b9b6c Mon Sep 17 00:00:00 2001 From: Gerd Hoffmann Date: Tue, 12 Jul 2011 13:35:10 +0200 Subject: [PATCH 200/230] Add iov_hexdump() Useful for debugging purposes. Signed-off-by: Gerd Hoffmann --- iov.c | 31 +++++++++++++++++++++++++++++++ iov.h | 2 ++ 2 files changed, 33 insertions(+) diff --git a/iov.c b/iov.c index 1e027914d4..60553c7542 100644 --- a/iov.c +++ b/iov.c @@ -73,3 +73,34 @@ size_t iov_size(const struct iovec *iov, const unsigned int iov_cnt) } return len; } + +void iov_hexdump(const struct iovec *iov, const unsigned int iov_cnt, + FILE *fp, const char *prefix, size_t limit) +{ + unsigned int i, v, b; + uint8_t *c; + + c = iov[0].iov_base; + for (i = 0, v = 0, b = 0; b < limit; i++, b++) { + if (i == iov[v].iov_len) { + i = 0; v++; + if (v == iov_cnt) { + break; + } + c = iov[v].iov_base; + } + if ((b % 16) == 0) { + fprintf(fp, "%s: %04x:", prefix, b); + } + if ((b % 4) == 0) { + fprintf(fp, " "); + } + fprintf(fp, " %02x", c[i]); + if ((b % 16) == 15) { + fprintf(fp, "\n"); + } + } + if ((b % 16) != 0) { + fprintf(fp, "\n"); + } +} diff --git a/iov.h b/iov.h index 110f67ab53..c2c5b39b8d 100644 --- a/iov.h +++ b/iov.h @@ -17,3 +17,5 @@ size_t iov_from_buf(struct iovec *iov, unsigned int iov_cnt, size_t iov_to_buf(const struct iovec *iov, const unsigned int iov_cnt, void *buf, size_t iov_off, size_t size); size_t iov_size(const struct iovec *iov, const unsigned int iov_cnt); +void iov_hexdump(const struct iovec *iov, const unsigned int iov_cnt, + FILE *fp, const char *prefix, size_t limit); From 8d15028ec03999fa6eca8dba7ef7cd4eb575486b Mon Sep 17 00:00:00 2001 From: Gerd Hoffmann Date: Wed, 13 Jul 2011 15:16:08 +0200 Subject: [PATCH 201/230] Add iov_clear() Fill the spefified area with zeros. Signed-off-by: Gerd Hoffmann --- iov.c | 23 +++++++++++++++++++++++ iov.h | 2 ++ 2 files changed, 25 insertions(+) diff --git a/iov.c b/iov.c index 60553c7542..e7385c41f4 100644 --- a/iov.c +++ b/iov.c @@ -62,6 +62,29 @@ size_t iov_to_buf(const struct iovec *iov, const unsigned int iov_cnt, return buf_off; } +size_t iov_clear(const struct iovec *iov, const unsigned int iov_cnt, + size_t iov_off, size_t size) +{ + size_t iovec_off, buf_off; + unsigned int i; + + iovec_off = 0; + buf_off = 0; + for (i = 0; i < iov_cnt && size; i++) { + if (iov_off < (iovec_off + iov[i].iov_len)) { + size_t len = MIN((iovec_off + iov[i].iov_len) - iov_off , size); + + memset(iov[i].iov_base + (iov_off - iovec_off), 0, len); + + buf_off += len; + iov_off += len; + size -= len; + } + iovec_off += iov[i].iov_len; + } + return buf_off; +} + size_t iov_size(const struct iovec *iov, const unsigned int iov_cnt) { size_t len; diff --git a/iov.h b/iov.h index c2c5b39b8d..94d2f78284 100644 --- a/iov.h +++ b/iov.h @@ -17,5 +17,7 @@ size_t iov_from_buf(struct iovec *iov, unsigned int iov_cnt, size_t iov_to_buf(const struct iovec *iov, const unsigned int iov_cnt, void *buf, size_t iov_off, size_t size); size_t iov_size(const struct iovec *iov, const unsigned int iov_cnt); +size_t iov_clear(const struct iovec *iov, const unsigned int iov_cnt, + size_t iov_off, size_t size); void iov_hexdump(const struct iovec *iov, const unsigned int iov_cnt, FILE *fp, const char *prefix, size_t limit); From d35bf9ade5293171f13bc5fd1460920a258e3e39 Mon Sep 17 00:00:00 2001 From: Gerd Hoffmann Date: Tue, 12 Jul 2011 13:36:23 +0200 Subject: [PATCH 202/230] move QEMUSGList typedef Move the QEMUSGList typedef to qemu-common so it can easily be used. The actual struct definition stays in dma.h. Signed-off-by: Gerd Hoffmann --- dma.h | 4 ++-- qemu-common.h | 1 + 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/dma.h b/dma.h index 3d8324bb54..a6db5bacbb 100644 --- a/dma.h +++ b/dma.h @@ -20,12 +20,12 @@ typedef struct { target_phys_addr_t len; } ScatterGatherEntry; -typedef struct { +struct QEMUSGList { ScatterGatherEntry *sg; int nsg; int nalloc; target_phys_addr_t size; -} QEMUSGList; +}; void qemu_sglist_init(QEMUSGList *qsg, int alloc_hint); void qemu_sglist_add(QEMUSGList *qsg, target_phys_addr_t base, diff --git a/qemu-common.h b/qemu-common.h index 1e3c66511e..c7064d3620 100644 --- a/qemu-common.h +++ b/qemu-common.h @@ -270,6 +270,7 @@ typedef struct I2SCodec I2SCodec; typedef struct SSIBus SSIBus; typedef struct EventNotifier EventNotifier; typedef struct VirtIODevice VirtIODevice; +typedef struct QEMUSGList QEMUSGList; typedef uint64_t pcibus_t; From 4f4321c11ff6e98583846bfd6f0e81954924b003 Mon Sep 17 00:00:00 2001 From: Gerd Hoffmann Date: Tue, 12 Jul 2011 15:22:25 +0200 Subject: [PATCH 203/230] usb: use iovecs in USBPacket Zap data pointer from USBPacket, add a QEMUIOVector instead. Add a bunch of helper functions to manage USBPacket data. Switch over users to the new interface. Note that USBPacket->len was used for two purposes: First to pass in the buffer size and second to return the number of transfered bytes or the status code on async transfers. There is a new result variable for the latter. A new status code was added to catch uninitialized result. Nobody creates iovecs with more than one element (yet). Some users are (temporarely) limited to iovecs with a single element to keep the patch size as small as possible. Signed-off-by: Gerd Hoffmann --- Makefile.objs | 1 + hw/bt-hid.c | 16 ++++---- hw/milkymist-softusb.c | 8 ++-- hw/usb-bt.c | 31 ++++++--------- hw/usb-ccid.c | 46 +++++++++++----------- hw/usb-ehci.c | 21 ++++------- hw/usb-hid.c | 6 ++- hw/usb-hub.c | 8 ++-- hw/usb-libhw.c | 63 +++++++++++++++++++++++++++++++ hw/usb-msd.c | 12 ++++-- hw/usb-musb.c | 22 +++++------ hw/usb-net.c | 65 +++++++++++-------------------- hw/usb-ohci.c | 23 ++++++----- hw/usb-serial.c | 5 ++- hw/usb-uhci.c | 38 +++++++++---------- hw/usb-wacom.c | 6 ++- hw/usb.c | 86 +++++++++++++++++++++++++++++++++++------- hw/usb.h | 13 ++++++- usb-bsd.c | 4 +- usb-linux.c | 27 ++++++------- usb-redir.c | 59 ++++++++++++++++------------- 21 files changed, 338 insertions(+), 222 deletions(-) create mode 100644 hw/usb-libhw.c diff --git a/Makefile.objs b/Makefile.objs index 6991a9f52a..3d1a4de33c 100644 --- a/Makefile.objs +++ b/Makefile.objs @@ -172,6 +172,7 @@ user-obj-y += cutils.o cache-utils.o hw-obj-y = hw-obj-y += vl.o loader.o hw-obj-$(CONFIG_VIRTIO) += virtio-console.o +hw-obj-y += usb-libhw.o hw-obj-$(CONFIG_VIRTIO_PCI) += virtio-pci.o hw-obj-y += fw_cfg.o hw-obj-$(CONFIG_PCI) += pci.o pci_bridge.o diff --git a/hw/bt-hid.c b/hw/bt-hid.c index 09120af074..a4204f98e7 100644 --- a/hw/bt-hid.c +++ b/hw/bt-hid.c @@ -127,11 +127,11 @@ static int bt_hid_out(struct bt_hid_device_s *s) USBPacket p; if (s->data_type == BT_DATA_OUTPUT) { - p.pid = USB_TOKEN_OUT; - p.devep = 1; - p.data = s->dataout.buffer; - p.len = s->dataout.len; + usb_packet_init(&p); + usb_packet_setup(&p, USB_TOKEN_OUT, 0, 1); + usb_packet_addbuf(&p, s->dataout.buffer, s->dataout.len); s->dataout.len = s->usbdev->info->handle_data(s->usbdev, &p); + usb_packet_cleanup(&p); return s->dataout.len; } @@ -150,11 +150,11 @@ static int bt_hid_in(struct bt_hid_device_s *s) { USBPacket p; - p.pid = USB_TOKEN_IN; - p.devep = 1; - p.data = s->datain.buffer; - p.len = sizeof(s->datain.buffer); + usb_packet_init(&p); + usb_packet_setup(&p, USB_TOKEN_IN, 0, 1); + usb_packet_addbuf(&p, s->dataout.buffer, sizeof(s->datain.buffer)); s->datain.len = s->usbdev->info->handle_data(s->usbdev, &p); + usb_packet_cleanup(&p); return s->datain.len; } diff --git a/hw/milkymist-softusb.c b/hw/milkymist-softusb.c index abf7b59acd..75c85aeb6f 100644 --- a/hw/milkymist-softusb.c +++ b/hw/milkymist-softusb.c @@ -234,11 +234,11 @@ static void softusb_usbdev_datain(void *opaque) USBPacket p; - p.pid = USB_TOKEN_IN; - p.devep = 1; - p.data = s->kbd_usb_buffer; - p.len = sizeof(s->kbd_usb_buffer); + usb_packet_init(&p); + usb_packet_setup(&p, USB_TOKEN_IN, 0, 1); + usb_packet_addbuf(&p, s->kbd_usb_buffer, sizeof(s->kbd_usb_buffer)); s->usbdev->info->handle_data(s->usbdev, &p); + usb_packet_cleanup(&p); softusb_kbd_changed(s); } diff --git a/hw/usb-bt.c b/hw/usb-bt.c index 4557802bbc..529fa3355d 100644 --- a/hw/usb-bt.c +++ b/hw/usb-bt.c @@ -294,9 +294,9 @@ static inline int usb_bt_fifo_dequeue(struct usb_hci_in_fifo_s *fifo, if (likely(!fifo->len)) return USB_RET_STALL; - len = MIN(p->len, fifo->fifo[fifo->start].len); - memcpy(p->data, fifo->fifo[fifo->start].data, len); - if (len == p->len) { + len = MIN(p->iov.size, fifo->fifo[fifo->start].len); + usb_packet_copy(p, fifo->fifo[fifo->start].data, len); + if (len == p->iov.size) { fifo->fifo[fifo->start].len -= len; fifo->fifo[fifo->start].data += len; } else { @@ -319,20 +319,13 @@ static inline void usb_bt_fifo_out_enqueue(struct USBBtState *s, struct usb_hci_out_fifo_s *fifo, void (*send)(struct HCIInfo *, const uint8_t *, int), int (*complete)(const uint8_t *, int), - const uint8_t *data, int len) + USBPacket *p) { - if (fifo->len) { - memcpy(fifo->data + fifo->len, data, len); - fifo->len += len; - if (complete(fifo->data, fifo->len)) { - send(s->hci, fifo->data, fifo->len); - fifo->len = 0; - } - } else if (complete(data, len)) - send(s->hci, data, len); - else { - memcpy(fifo->data, data, len); - fifo->len = len; + usb_packet_copy(p, fifo->data + fifo->len, p->iov.size); + fifo->len += p->iov.size; + if (complete(fifo->data, fifo->len)) { + send(s->hci, fifo->data, fifo->len); + fifo->len = 0; } /* TODO: do we need to loop? */ @@ -432,7 +425,7 @@ static int usb_bt_handle_control(USBDevice *dev, USBPacket *p, case ((USB_DIR_OUT | USB_TYPE_CLASS | USB_RECIP_DEVICE) << 8): if (s->config) usb_bt_fifo_out_enqueue(s, &s->outcmd, s->hci->cmd_send, - usb_bt_hci_cmd_complete, data, length); + usb_bt_hci_cmd_complete, p); break; default: fail: @@ -474,12 +467,12 @@ static int usb_bt_handle_data(USBDevice *dev, USBPacket *p) switch (p->devep & 0xf) { case USB_ACL_EP: usb_bt_fifo_out_enqueue(s, &s->outacl, s->hci->acl_send, - usb_bt_hci_acl_complete, p->data, p->len); + usb_bt_hci_acl_complete, p); break; case USB_SCO_EP: usb_bt_fifo_out_enqueue(s, &s->outsco, s->hci->sco_send, - usb_bt_hci_sco_complete, p->data, p->len); + usb_bt_hci_sco_complete, p); break; default: diff --git a/hw/usb-ccid.c b/hw/usb-ccid.c index 4dda2c4833..66aeb211af 100644 --- a/hw/usb-ccid.c +++ b/hw/usb-ccid.c @@ -934,16 +934,16 @@ static int ccid_handle_bulk_out(USBCCIDState *s, USBPacket *p) { CCID_Header *ccid_header; - if (p->len + s->bulk_out_pos > BULK_OUT_DATA_SIZE) { + if (p->iov.size + s->bulk_out_pos > BULK_OUT_DATA_SIZE) { return USB_RET_STALL; } ccid_header = (CCID_Header *)s->bulk_out_data; - memcpy(s->bulk_out_data + s->bulk_out_pos, p->data, p->len); - s->bulk_out_pos += p->len; - if (p->len == CCID_MAX_PACKET_SIZE) { + usb_packet_copy(p, s->bulk_out_data + s->bulk_out_pos, p->iov.size); + s->bulk_out_pos += p->iov.size; + if (p->iov.size == CCID_MAX_PACKET_SIZE) { DPRINTF(s, D_VERBOSE, - "usb-ccid: bulk_in: expecting more packets (%d/%d)\n", - p->len, ccid_header->dwLength); + "usb-ccid: bulk_in: expecting more packets (%zd/%d)\n", + p->iov.size, ccid_header->dwLength); return 0; } if (s->bulk_out_pos < 10) { @@ -1006,15 +1006,17 @@ static int ccid_handle_bulk_out(USBCCIDState *s, USBPacket *p) return 0; } -static int ccid_bulk_in_copy_to_guest(USBCCIDState *s, uint8_t *data, int len) +static int ccid_bulk_in_copy_to_guest(USBCCIDState *s, USBPacket *p) { int ret = 0; - assert(len > 0); + assert(p->iov.size > 0); ccid_bulk_in_get(s); if (s->current_bulk_in != NULL) { - ret = MIN(s->current_bulk_in->len - s->current_bulk_in->pos, len); - memcpy(data, s->current_bulk_in->data + s->current_bulk_in->pos, ret); + ret = MIN(s->current_bulk_in->len - s->current_bulk_in->pos, + p->iov.size); + usb_packet_copy(p, s->current_bulk_in->data + + s->current_bulk_in->pos, ret); s->current_bulk_in->pos += ret; if (s->current_bulk_in->pos == s->current_bulk_in->len) { ccid_bulk_in_release(s); @@ -1025,11 +1027,13 @@ static int ccid_bulk_in_copy_to_guest(USBCCIDState *s, uint8_t *data, int len) } if (ret > 0) { DPRINTF(s, D_MORE_INFO, - "%s: %d/%d req/act to guest (BULK_IN)\n", __func__, len, ret); + "%s: %zd/%d req/act to guest (BULK_IN)\n", + __func__, p->iov.size, ret); } - if (ret != USB_RET_NAK && ret < len) { + if (ret != USB_RET_NAK && ret < p->iov.size) { DPRINTF(s, 1, - "%s: returning short (EREMOTEIO) %d < %d\n", __func__, ret, len); + "%s: returning short (EREMOTEIO) %d < %zd\n", + __func__, ret, p->iov.size); } return ret; } @@ -1038,8 +1042,7 @@ static int ccid_handle_data(USBDevice *dev, USBPacket *p) { USBCCIDState *s = DO_UPCAST(USBCCIDState, dev, dev); int ret = 0; - uint8_t *data = p->data; - int len = p->len; + uint8_t buf[2]; switch (p->pid) { case USB_TOKEN_OUT: @@ -1049,24 +1052,25 @@ static int ccid_handle_data(USBDevice *dev, USBPacket *p) case USB_TOKEN_IN: switch (p->devep & 0xf) { case CCID_BULK_IN_EP: - if (!len) { + if (!p->iov.size) { ret = USB_RET_NAK; } else { - ret = ccid_bulk_in_copy_to_guest(s, data, len); + ret = ccid_bulk_in_copy_to_guest(s, p); } break; case CCID_INT_IN_EP: if (s->notify_slot_change) { /* page 56, RDR_to_PC_NotifySlotChange */ - data[0] = CCID_MESSAGE_TYPE_RDR_to_PC_NotifySlotChange; - data[1] = s->bmSlotICCState; + buf[0] = CCID_MESSAGE_TYPE_RDR_to_PC_NotifySlotChange; + buf[1] = s->bmSlotICCState; + usb_packet_copy(p, buf, 2); ret = 2; s->notify_slot_change = false; s->bmSlotICCState &= ~SLOT_0_CHANGED_MASK; DPRINTF(s, D_INFO, "handle_data: int_in: notify_slot_change %X, " - "requested len %d\n", - s->bmSlotICCState, len); + "requested len %zd\n", + s->bmSlotICCState, p->iov.size); } break; default: diff --git a/hw/usb-ehci.c b/hw/usb-ehci.c index 8b0dcc335d..799e31a319 100644 --- a/hw/usb-ehci.c +++ b/hw/usb-ehci.c @@ -1235,7 +1235,7 @@ static void ehci_async_complete_packet(USBPort *port, USBPacket *packet) trace_usb_ehci_queue_action(q, "wakeup"); assert(q->async == EHCI_ASYNC_INFLIGHT); q->async = EHCI_ASYNC_FINISHED; - q->usb_status = packet->len; + q->usb_status = packet->result; } static void ehci_execute_complete(EHCIQueue *q) @@ -1367,17 +1367,15 @@ static int ehci_execute(EHCIQueue *q) continue; } - q->packet.pid = q->pid; - q->packet.devaddr = devadr; - q->packet.devep = endp; - q->packet.data = q->buffer; - q->packet.len = q->tbytes; + usb_packet_setup(&q->packet, q->pid, devadr, endp); + usb_packet_addbuf(&q->packet, q->buffer, q->tbytes); ret = usb_handle_packet(dev, &q->packet); - DPRINTF("submit: qh %x next %x qtd %x pid %x len %d (total %d) endp %x ret %d\n", + DPRINTF("submit: qh %x next %x qtd %x pid %x len %zd " + "(total %d) endp %x ret %d\n", q->qhaddr, q->qh.next, q->qtdaddr, q->pid, - q->packet.len, q->tbytes, endp, ret); + q->packet.iov.size, q->tbytes, endp, ret); if (ret != USB_RET_NODEV) { break; @@ -1457,11 +1455,8 @@ static int ehci_process_itd(EHCIState *ehci, continue; } - ehci->ipacket.pid = pid; - ehci->ipacket.devaddr = devaddr; - ehci->ipacket.devep = endp; - ehci->ipacket.data = ehci->ibuffer; - ehci->ipacket.len = len; + usb_packet_setup(&ehci->ipacket, pid, devaddr, endp); + usb_packet_addbuf(&ehci->ipacket, ehci->ibuffer, len); ret = usb_handle_packet(dev, &ehci->ipacket); diff --git a/hw/usb-hid.c b/hw/usb-hid.c index 9008320c86..541644a06d 100644 --- a/hw/usb-hid.c +++ b/hw/usb-hid.c @@ -816,6 +816,7 @@ static int usb_hid_handle_control(USBDevice *dev, USBPacket *p, static int usb_hid_handle_data(USBDevice *dev, USBPacket *p) { USBHIDState *s = (USBHIDState *)dev; + uint8_t buf[p->iov.size]; int ret = 0; switch(p->pid) { @@ -826,11 +827,12 @@ static int usb_hid_handle_data(USBDevice *dev, USBPacket *p) return USB_RET_NAK; usb_hid_set_next_idle(s, curtime); if (s->kind == USB_MOUSE || s->kind == USB_TABLET) { - ret = usb_pointer_poll(s, p->data, p->len); + ret = usb_pointer_poll(s, buf, p->iov.size); } else if (s->kind == USB_KEYBOARD) { - ret = usb_keyboard_poll(s, p->data, p->len); + ret = usb_keyboard_poll(s, buf, p->iov.size); } + usb_packet_copy(p, buf, ret); s->changed = s->n > 0; } else { goto fail; diff --git a/hw/usb-hub.c b/hw/usb-hub.c index b49a2fe882..c49c547d0c 100644 --- a/hw/usb-hub.c +++ b/hw/usb-hub.c @@ -394,11 +394,12 @@ static int usb_hub_handle_data(USBDevice *dev, USBPacket *p) if (p->devep == 1) { USBHubPort *port; unsigned int status; + uint8_t buf[4]; int i, n; n = (NUM_PORTS + 1 + 7) / 8; - if (p->len == 1) { /* FreeBSD workaround */ + if (p->iov.size == 1) { /* FreeBSD workaround */ n = 1; - } else if (n > p->len) { + } else if (n > p->iov.size) { return USB_RET_BABBLE; } status = 0; @@ -409,8 +410,9 @@ static int usb_hub_handle_data(USBDevice *dev, USBPacket *p) } if (status != 0) { for(i = 0; i < n; i++) { - p->data[i] = status >> (8 * i); + buf[i] = status >> (8 * i); } + usb_packet_copy(p, buf, n); ret = n; } else { ret = USB_RET_NAK; /* usb11 11.13.1 */ diff --git a/hw/usb-libhw.c b/hw/usb-libhw.c new file mode 100644 index 0000000000..162b42bd5b --- /dev/null +++ b/hw/usb-libhw.c @@ -0,0 +1,63 @@ +/* + * QEMU USB emulation, libhw bits. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +#include "qemu-common.h" +#include "cpu-common.h" +#include "usb.h" +#include "dma.h" + +int usb_packet_map(USBPacket *p, QEMUSGList *sgl) +{ + int is_write = (p->pid == USB_TOKEN_IN); + target_phys_addr_t len; + void *mem; + int i; + + for (i = 0; i < sgl->nsg; i++) { + len = sgl->sg[i].len; + mem = cpu_physical_memory_map(sgl->sg[i].base, &len, + is_write); + if (!mem) { + goto err; + } + qemu_iovec_add(&p->iov, mem, len); + if (len != sgl->sg[i].len) { + goto err; + } + } + return 0; + +err: + usb_packet_unmap(p); + return -1; +} + +void usb_packet_unmap(USBPacket *p) +{ + int is_write = (p->pid == USB_TOKEN_IN); + int i; + + for (i = 0; i < p->iov.niov; i++) { + cpu_physical_memory_unmap(p->iov.iov[i].iov_base, + p->iov.iov[i].iov_len, is_write, + p->iov.iov[i].iov_len); + } +} diff --git a/hw/usb-msd.c b/hw/usb-msd.c index cdeac581e3..48e0b343ca 100644 --- a/hw/usb-msd.c +++ b/hw/usb-msd.c @@ -207,8 +207,9 @@ static void usb_msd_send_status(MSDState *s, USBPacket *p) csw.residue = s->residue; csw.status = s->result; - len = MIN(sizeof(csw), p->len); - memcpy(p->data, &csw, len); + len = MIN(sizeof(csw), p->iov.size); + usb_packet_copy(p, &csw, len); + p->result = len; } static void usb_msd_transfer_data(SCSIRequest *req, uint32_t len) @@ -222,6 +223,7 @@ static void usb_msd_transfer_data(SCSIRequest *req, uint32_t len) if (p) { usb_msd_copy_data(s); if (s->packet && s->usb_len == 0) { + p->result = p->iov.size; /* Set s->packet to NULL before calling usb_packet_complete because another request may be issued before usb_packet_complete returns. */ @@ -257,6 +259,7 @@ static void usb_msd_command_complete(SCSIRequest *req, uint32_t status) if (s->data_len == 0) { s->mode = USB_MSDM_CSW; } + p->result = p->iov.size; } s->packet = NULL; usb_packet_complete(&s->dev, p); @@ -342,9 +345,10 @@ static int usb_msd_handle_data(USBDevice *dev, USBPacket *p) int ret = 0; struct usb_msd_cbw cbw; uint8_t devep = p->devep; - uint8_t *data = p->data; - int len = p->len; + uint8_t *data = p->iov.iov[0].iov_base; + int len = p->iov.iov[0].iov_len; + assert(p->iov.niov == 1); /* temporary */ switch (p->pid) { case USB_TOKEN_OUT: if (devep != 2) diff --git a/hw/usb-musb.c b/hw/usb-musb.c index 035dda8372..d3ccde9199 100644 --- a/hw/usb-musb.c +++ b/hw/usb-musb.c @@ -365,6 +365,8 @@ struct MUSBState *musb_init(qemu_irq *irqs) s->ep[i].maxp[1] = 0x40; s->ep[i].musb = s; s->ep[i].epnum = i; + usb_packet_init(&s->ep[i].packey[0].p); + usb_packet_init(&s->ep[i].packey[1].p); } usb_bus_new(&s->bus, &musb_bus_ops, NULL /* FIXME */); @@ -605,12 +607,10 @@ static void musb_packet(MUSBState *s, MUSBEndPoint *ep, ep->interrupt[dir] = ttype == USB_ENDPOINT_XFER_INT; ep->delayed_cb[dir] = cb; - ep->packey[dir].p.pid = pid; /* A wild guess on the FADDR semantics... */ - ep->packey[dir].p.devaddr = ep->faddr[idx]; - ep->packey[dir].p.devep = ep->type[idx] & 0xf; - ep->packey[dir].p.data = (void *) ep->buf[idx]; - ep->packey[dir].p.len = len; + usb_packet_setup(&ep->packey[dir].p, pid, ep->faddr[idx], + ep->type[idx] & 0xf); + usb_packet_addbuf(&ep->packey[dir].p, ep->buf[idx], len); ep->packey[dir].ep = ep; ep->packey[dir].dir = dir; @@ -738,7 +738,7 @@ static void musb_rx_packet_complete(USBPacket *packey, void *opaque) if (ep->status[1] == USB_RET_STALL) { ep->status[1] = 0; - packey->len = 0; + packey->result = 0; ep->csr[1] |= MGC_M_RXCSR_H_RXSTALL; if (!epnum) @@ -752,7 +752,7 @@ static void musb_rx_packet_complete(USBPacket *packey, void *opaque) * Data-errors in Isochronous. */ if (ep->interrupt[1]) return musb_packet(s, ep, epnum, USB_TOKEN_IN, - packey->len, musb_rx_packet_complete, 1); + packey->iov.size, musb_rx_packet_complete, 1); ep->csr[1] |= MGC_M_RXCSR_DATAERROR; if (!epnum) @@ -777,14 +777,14 @@ static void musb_rx_packet_complete(USBPacket *packey, void *opaque) /* TODO: check len for over/underruns of an OUT packet? */ /* TODO: perhaps make use of e->ext_size[1] here. */ - packey->len = ep->status[1]; + packey->result = ep->status[1]; if (!(ep->csr[1] & (MGC_M_RXCSR_H_RXSTALL | MGC_M_RXCSR_DATAERROR))) { ep->csr[1] |= MGC_M_RXCSR_FIFOFULL | MGC_M_RXCSR_RXPKTRDY; if (!epnum) ep->csr[0] |= MGC_M_CSR0_RXPKTRDY; - ep->rxcount = packey->len; /* XXX: MIN(packey->len, ep->maxp[1]); */ + ep->rxcount = packey->result; /* XXX: MIN(packey->len, ep->maxp[1]); */ /* In DMA mode: assert DMA request for this EP */ } @@ -856,12 +856,12 @@ static void musb_rx_req(MUSBState *s, int epnum) * 64 bytes of the FIFO, only move the FIFO start and return. (Obsolete) */ if (ep->packey[1].p.pid == USB_TOKEN_IN && ep->status[1] >= 0 && (ep->fifostart[1]) + ep->rxcount < - ep->packey[1].p.len) { + ep->packey[1].p.iov.size) { TRACE("0x%08x, %d", ep->fifostart[1], ep->rxcount ); ep->fifostart[1] += ep->rxcount; ep->fifolen[1] = 0; - ep->rxcount = MIN(ep->packey[0].p.len - (ep->fifostart[1]), + ep->rxcount = MIN(ep->packey[0].p.iov.size - (ep->fifostart[1]), ep->maxp[1]); ep->csr[1] &= ~MGC_M_RXCSR_H_REQPKT; diff --git a/hw/usb-net.c b/hw/usb-net.c index 4212e5b3c5..0cb47d63b3 100644 --- a/hw/usb-net.c +++ b/hw/usb-net.c @@ -29,6 +29,7 @@ #include "net.h" #include "qemu-queue.h" #include "sysemu.h" +#include "iov.h" /*#define TRAFFIC_DEBUG*/ /* Thanks to NetChip Technologies for donating this product ID. @@ -1121,28 +1122,23 @@ static int usb_net_handle_control(USBDevice *dev, USBPacket *p, static int usb_net_handle_statusin(USBNetState *s, USBPacket *p) { + le32 buf[2]; int ret = 8; - if (p->len < 8) + if (p->iov.size < 8) { return USB_RET_STALL; + } - ((le32 *) p->data)[0] = cpu_to_le32(1); - ((le32 *) p->data)[1] = cpu_to_le32(0); + buf[0] = cpu_to_le32(1); + buf[1] = cpu_to_le32(0); + usb_packet_copy(p, buf, 8); if (!s->rndis_resp.tqh_first) ret = USB_RET_NAK; #ifdef TRAFFIC_DEBUG - fprintf(stderr, "usbnet: interrupt poll len %u return %d", p->len, ret); - { - int i; - fprintf(stderr, ":"); - for (i = 0; i < ret; i++) { - if (!(i & 15)) - fprintf(stderr, "\n%04x:", i); - fprintf(stderr, " %02x", p->data[i]); - } - fprintf(stderr, "\n\n"); - } + fprintf(stderr, "usbnet: interrupt poll len %zu return %d", + p->iov.size, ret); + iov_hexdump(p->iov.iov, p->iov.niov, stderr, "usbnet", ret); #endif return ret; @@ -1162,9 +1158,10 @@ static int usb_net_handle_datain(USBNetState *s, USBPacket *p) return ret; } ret = s->in_len - s->in_ptr; - if (ret > p->len) - ret = p->len; - memcpy(p->data, &s->in_buf[s->in_ptr], ret); + if (ret > p->iov.size) { + ret = p->iov.size; + } + usb_packet_copy(p, &s->in_buf[s->in_ptr], ret); s->in_ptr += ret; if (s->in_ptr >= s->in_len && (is_rndis(s) || (s->in_len & (64 - 1)) || !ret)) { @@ -1173,17 +1170,8 @@ static int usb_net_handle_datain(USBNetState *s, USBPacket *p) } #ifdef TRAFFIC_DEBUG - fprintf(stderr, "usbnet: data in len %u return %d", p->len, ret); - { - int i; - fprintf(stderr, ":"); - for (i = 0; i < ret; i++) { - if (!(i & 15)) - fprintf(stderr, "\n%04x:", i); - fprintf(stderr, " %02x", p->data[i]); - } - fprintf(stderr, "\n\n"); - } + fprintf(stderr, "usbnet: data in len %zu return %d", p->iov.size, ret); + iov_hexdump(p->iov.iov, p->iov.niov, stderr, "usbnet", ret); #endif return ret; @@ -1191,29 +1179,20 @@ static int usb_net_handle_datain(USBNetState *s, USBPacket *p) static int usb_net_handle_dataout(USBNetState *s, USBPacket *p) { - int ret = p->len; + int ret = p->iov.size; int sz = sizeof(s->out_buf) - s->out_ptr; struct rndis_packet_msg_type *msg = (struct rndis_packet_msg_type *) s->out_buf; uint32_t len; #ifdef TRAFFIC_DEBUG - fprintf(stderr, "usbnet: data out len %u\n", p->len); - { - int i; - fprintf(stderr, ":"); - for (i = 0; i < p->len; i++) { - if (!(i & 15)) - fprintf(stderr, "\n%04x:", i); - fprintf(stderr, " %02x", p->data[i]); - } - fprintf(stderr, "\n\n"); - } + fprintf(stderr, "usbnet: data out len %zu\n", p->iov.size); + iov_hexdump(p->iov.iov, p->iov.niov, stderr, "usbnet", p->iov.size); #endif if (sz > ret) sz = ret; - memcpy(&s->out_buf[s->out_ptr], p->data, sz); + usb_packet_copy(p, &s->out_buf[s->out_ptr], sz); s->out_ptr += sz; if (!is_rndis(s)) { @@ -1277,8 +1256,8 @@ static int usb_net_handle_data(USBDevice *dev, USBPacket *p) } if (ret == USB_RET_STALL) fprintf(stderr, "usbnet: failed data transaction: " - "pid 0x%x ep 0x%x len 0x%x\n", - p->pid, p->devep, p->len); + "pid 0x%x ep 0x%x len 0x%zx\n", + p->pid, p->devep, p->iov.size); return ret; } diff --git a/hw/usb-ohci.c b/hw/usb-ohci.c index 337b250261..d39bcb0c0d 100644 --- a/hw/usb-ohci.c +++ b/hw/usb-ohci.c @@ -777,18 +777,17 @@ static int ohci_service_iso_td(OHCIState *ohci, struct ohci_ed *ed, } if (completion) { - ret = ohci->usb_packet.len; + ret = ohci->usb_packet.result; } else { ret = USB_RET_NODEV; for (i = 0; i < ohci->num_ports; i++) { dev = ohci->rhport[i].port.dev; if ((ohci->rhport[i].ctrl & OHCI_PORT_PES) == 0) continue; - ohci->usb_packet.pid = pid; - ohci->usb_packet.devaddr = OHCI_BM(ed->flags, ED_FA); - ohci->usb_packet.devep = OHCI_BM(ed->flags, ED_EN); - ohci->usb_packet.data = ohci->usb_buf; - ohci->usb_packet.len = len; + usb_packet_setup(&ohci->usb_packet, pid, + OHCI_BM(ed->flags, ED_FA), + OHCI_BM(ed->flags, ED_EN)); + usb_packet_addbuf(&ohci->usb_packet, ohci->usb_buf, len); ret = usb_handle_packet(dev, &ohci->usb_packet); if (ret != USB_RET_NODEV) break; @@ -959,7 +958,7 @@ static int ohci_service_td(OHCIState *ohci, struct ohci_ed *ed) } #endif if (completion) { - ret = ohci->usb_packet.len; + ret = ohci->usb_packet.result; ohci->async_td = 0; ohci->async_complete = 0; } else { @@ -980,11 +979,10 @@ static int ohci_service_td(OHCIState *ohci, struct ohci_ed *ed) #endif return 1; } - ohci->usb_packet.pid = pid; - ohci->usb_packet.devaddr = OHCI_BM(ed->flags, ED_FA); - ohci->usb_packet.devep = OHCI_BM(ed->flags, ED_EN); - ohci->usb_packet.data = ohci->usb_buf; - ohci->usb_packet.len = len; + usb_packet_setup(&ohci->usb_packet, pid, + OHCI_BM(ed->flags, ED_FA), + OHCI_BM(ed->flags, ED_EN)); + usb_packet_addbuf(&ohci->usb_packet, ohci->usb_buf, len); ret = usb_handle_packet(dev, &ohci->usb_packet); if (ret != USB_RET_NODEV) break; @@ -1761,6 +1759,7 @@ static int usb_ohci_init(OHCIState *ohci, DeviceState *dev, ohci->localmem_base = localmem_base; ohci->name = dev->info->name; + usb_packet_init(&ohci->usb_packet); ohci->async_td = 0; qemu_register_reset(ohci_reset, ohci); diff --git a/hw/usb-serial.c b/hw/usb-serial.c index 298c1e9d95..09731da485 100644 --- a/hw/usb-serial.c +++ b/hw/usb-serial.c @@ -361,10 +361,11 @@ static int usb_serial_handle_data(USBDevice *dev, USBPacket *p) USBSerialState *s = (USBSerialState *)dev; int ret = 0; uint8_t devep = p->devep; - uint8_t *data = p->data; - int len = p->len; + uint8_t *data = p->iov.iov[0].iov_base; + int len = p->iov.iov[0].iov_len; int first_len; + assert(p->iov.niov == 1); /* temporary */ switch (p->pid) { case USB_TOKEN_OUT: if (devep != 2) diff --git a/hw/usb-uhci.c b/hw/usb-uhci.c index da74c57c62..20b829ba55 100644 --- a/hw/usb-uhci.c +++ b/hw/usb-uhci.c @@ -30,6 +30,7 @@ #include "pci.h" #include "qemu-timer.h" #include "usb-uhci.h" +#include "iov.h" //#define DEBUG //#define DEBUG_DUMP_DATA @@ -93,17 +94,12 @@ static const char *pid2str(int pid) #endif #ifdef DEBUG_DUMP_DATA -static void dump_data(const uint8_t *data, int len) +static void dump_data(USBPacket *p, int ret) { - int i; - - printf("uhci: data: "); - for(i = 0; i < len; i++) - printf(" %02x", data[i]); - printf("\n"); + iov_hexdump(p->iov.iov, p->iov.niov, stderr, "uhci", ret); } #else -static void dump_data(const uint8_t *data, int len) {} +static void dump_data(USBPacket *p, int ret) {} #endif typedef struct UHCIState UHCIState; @@ -179,12 +175,14 @@ static UHCIAsync *uhci_async_alloc(UHCIState *s) async->token = 0; async->done = 0; async->isoc = 0; + usb_packet_init(&async->packet); return async; } static void uhci_async_free(UHCIState *s, UHCIAsync *async) { + usb_packet_cleanup(&async->packet); qemu_free(async); } @@ -648,10 +646,10 @@ static int uhci_broadcast_packet(UHCIState *s, USBPacket *p) { int i, ret; - DPRINTF("uhci: packet enter. pid %s addr 0x%02x ep %d len %d\n", - pid2str(p->pid), p->devaddr, p->devep, p->len); + DPRINTF("uhci: packet enter. pid %s addr 0x%02x ep %d len %zd\n", + pid2str(p->pid), p->devaddr, p->devep, p->iov.size); if (p->pid == USB_TOKEN_OUT || p->pid == USB_TOKEN_SETUP) - dump_data(p->data, p->len); + dump_data(p, 0); ret = USB_RET_NODEV; for (i = 0; i < NB_PORTS && ret == USB_RET_NODEV; i++) { @@ -662,9 +660,9 @@ static int uhci_broadcast_packet(UHCIState *s, USBPacket *p) ret = usb_handle_packet(dev, p); } - DPRINTF("uhci: packet exit. ret %d len %d\n", ret, p->len); + DPRINTF("uhci: packet exit. ret %d len %zd\n", ret, p->iov.size); if (p->pid == USB_TOKEN_IN && ret > 0) - dump_data(p->data, ret); + dump_data(p, ret); return ret; } @@ -684,7 +682,7 @@ static int uhci_complete_td(UHCIState *s, UHCI_TD *td, UHCIAsync *async, uint32_ max_len = ((td->token >> 21) + 1) & 0x7ff; pid = td->token & 0xff; - ret = async->packet.len; + ret = async->packet.result; if (td->ctrl & TD_CTRL_IOS) td->ctrl &= ~TD_CTRL_ACTIVE; @@ -692,7 +690,7 @@ static int uhci_complete_td(UHCIState *s, UHCI_TD *td, UHCIAsync *async, uint32_ if (ret < 0) goto out; - len = async->packet.len; + len = async->packet.result; td->ctrl = (td->ctrl & ~0x7ff) | ((len - 1) & 0x7ff); /* The NAK bit may have been set by a previous frame, so clear it @@ -827,11 +825,9 @@ static int uhci_handle_td(UHCIState *s, uint32_t addr, UHCI_TD *td, uint32_t *in max_len = ((td->token >> 21) + 1) & 0x7ff; pid = td->token & 0xff; - async->packet.pid = pid; - async->packet.devaddr = (td->token >> 8) & 0x7f; - async->packet.devep = (td->token >> 15) & 0xf; - async->packet.data = async->buffer; - async->packet.len = max_len; + usb_packet_setup(&async->packet, pid, (td->token >> 8) & 0x7f, + (td->token >> 15) & 0xf); + usb_packet_addbuf(&async->packet, async->buffer, max_len); switch(pid) { case USB_TOKEN_OUT: @@ -859,7 +855,7 @@ static int uhci_handle_td(UHCIState *s, uint32_t addr, UHCI_TD *td, uint32_t *in return 2; } - async->packet.len = len; + async->packet.result = len; done: len = uhci_complete_td(s, td, async, int_mask); diff --git a/hw/usb-wacom.c b/hw/usb-wacom.c index d76ee97e49..25580067f2 100644 --- a/hw/usb-wacom.c +++ b/hw/usb-wacom.c @@ -308,6 +308,7 @@ static int usb_wacom_handle_control(USBDevice *dev, USBPacket *p, static int usb_wacom_handle_data(USBDevice *dev, USBPacket *p) { USBWacomState *s = (USBWacomState *) dev; + uint8_t buf[p->iov.size]; int ret = 0; switch (p->pid) { @@ -317,9 +318,10 @@ static int usb_wacom_handle_data(USBDevice *dev, USBPacket *p) return USB_RET_NAK; s->changed = 0; if (s->mode == WACOM_MODE_HID) - ret = usb_mouse_poll(s, p->data, p->len); + ret = usb_mouse_poll(s, buf, p->iov.size); else if (s->mode == WACOM_MODE_WACOM) - ret = usb_wacom_poll(s, p->data, p->len); + ret = usb_wacom_poll(s, buf, p->iov.size); + usb_packet_copy(p, buf, ret); break; } /* Fall through. */ diff --git a/hw/usb.c b/hw/usb.c index 27a983ca5c..685e775a00 100644 --- a/hw/usb.c +++ b/hw/usb.c @@ -25,6 +25,7 @@ */ #include "qemu-common.h" #include "usb.h" +#include "iov.h" void usb_attach(USBPort *port, USBDevice *dev) { @@ -72,10 +73,11 @@ static int do_token_setup(USBDevice *s, USBPacket *p) int request, value, index; int ret = 0; - if (p->len != 8) + if (p->iov.size != 8) { return USB_RET_STALL; - - memcpy(s->setup_buf, p->data, 8); + } + + usb_packet_copy(p, s->setup_buf, p->iov.size); s->setup_len = (s->setup_buf[7] << 8) | s->setup_buf[6]; s->setup_index = 0; @@ -144,9 +146,10 @@ static int do_token_in(USBDevice *s, USBPacket *p) case SETUP_STATE_DATA: if (s->setup_buf[0] & USB_DIR_IN) { int len = s->setup_len - s->setup_index; - if (len > p->len) - len = p->len; - memcpy(p->data, s->data_buf + s->setup_index, len); + if (len > p->iov.size) { + len = p->iov.size; + } + usb_packet_copy(p, s->data_buf + s->setup_index, len); s->setup_index += len; if (s->setup_index >= s->setup_len) s->setup_state = SETUP_STATE_ACK; @@ -179,9 +182,10 @@ static int do_token_out(USBDevice *s, USBPacket *p) case SETUP_STATE_DATA: if (!(s->setup_buf[0] & USB_DIR_IN)) { int len = s->setup_len - s->setup_index; - if (len > p->len) - len = p->len; - memcpy(s->data_buf + s->setup_index, p->data, len); + if (len > p->iov.size) { + len = p->iov.size; + } + usb_packet_copy(p, s->data_buf + s->setup_index, len); s->setup_index += len; if (s->setup_index >= s->setup_len) s->setup_state = SETUP_STATE_ACK; @@ -251,22 +255,22 @@ int usb_generic_handle_packet(USBDevice *s, USBPacket *p) usb_packet_complete to complete their async control packets. */ void usb_generic_async_ctrl_complete(USBDevice *s, USBPacket *p) { - if (p->len < 0) { + if (p->result < 0) { s->setup_state = SETUP_STATE_IDLE; } switch (s->setup_state) { case SETUP_STATE_SETUP: - if (p->len < s->setup_len) { - s->setup_len = p->len; + if (p->result < s->setup_len) { + s->setup_len = p->result; } s->setup_state = SETUP_STATE_DATA; - p->len = 8; + p->result = 8; break; case SETUP_STATE_ACK: s->setup_state = SETUP_STATE_IDLE; - p->len = 0; + p->result = 0; break; default: @@ -347,3 +351,57 @@ void usb_cancel_packet(USBPacket * p) p->owner->info->cancel_packet(p->owner, p); p->owner = NULL; } + + +void usb_packet_init(USBPacket *p) +{ + qemu_iovec_init(&p->iov, 1); +} + +void usb_packet_setup(USBPacket *p, int pid, uint8_t addr, uint8_t ep) +{ + p->pid = pid; + p->devaddr = addr; + p->devep = ep; + p->result = 0; + qemu_iovec_reset(&p->iov); +} + +void usb_packet_addbuf(USBPacket *p, void *ptr, size_t len) +{ + qemu_iovec_add(&p->iov, ptr, len); +} + +void usb_packet_copy(USBPacket *p, void *ptr, size_t bytes) +{ + assert(p->result >= 0); + assert(p->result + bytes <= p->iov.size); + switch (p->pid) { + case USB_TOKEN_SETUP: + case USB_TOKEN_OUT: + iov_to_buf(p->iov.iov, p->iov.niov, ptr, p->result, bytes); + break; + case USB_TOKEN_IN: + iov_from_buf(p->iov.iov, p->iov.niov, ptr, p->result, bytes); + break; + default: + fprintf(stderr, "%s: invalid pid: %x\n", __func__, p->pid); + abort(); + } + p->result += bytes; +} + +void usb_packet_skip(USBPacket *p, size_t bytes) +{ + assert(p->result >= 0); + assert(p->result + bytes <= p->iov.size); + if (p->pid == USB_TOKEN_IN) { + iov_clear(p->iov.iov, p->iov.niov, p->result, bytes); + } + p->result += bytes; +} + +void usb_packet_cleanup(USBPacket *p) +{ + qemu_iovec_destroy(&p->iov); +} diff --git a/hw/usb.h b/hw/usb.h index ded2de29b9..84d04df2e1 100644 --- a/hw/usb.h +++ b/hw/usb.h @@ -285,12 +285,21 @@ struct USBPacket { int pid; uint8_t devaddr; uint8_t devep; - uint8_t *data; - int len; + QEMUIOVector iov; + int result; /* transfer length or USB_RET_* status code */ /* Internal use by the USB layer. */ USBDevice *owner; }; +void usb_packet_init(USBPacket *p); +void usb_packet_setup(USBPacket *p, int pid, uint8_t addr, uint8_t ep); +void usb_packet_addbuf(USBPacket *p, void *ptr, size_t len); +int usb_packet_map(USBPacket *p, QEMUSGList *sgl); +void usb_packet_unmap(USBPacket *p); +void usb_packet_copy(USBPacket *p, void *ptr, size_t bytes); +void usb_packet_skip(USBPacket *p, size_t bytes); +void usb_packet_cleanup(USBPacket *p); + int usb_handle_packet(USBDevice *dev, USBPacket *p); void usb_packet_complete(USBDevice *dev, USBPacket *p); void usb_cancel_packet(USBPacket * p); diff --git a/usb-bsd.c b/usb-bsd.c index ab8e3b7294..ab84d93857 100644 --- a/usb-bsd.c +++ b/usb-bsd.c @@ -253,9 +253,9 @@ static int usb_host_handle_data(USBDevice *dev, USBPacket *p) } if (p->pid == USB_TOKEN_IN) - ret = read(fd, p->data, p->len); + ret = readv(fd, p->iov.iov, p->iov.niov); else - ret = write(fd, p->data, p->len); + ret = writev(fd, p->iov.iov, p->iov.niov); sigprocmask(SIG_SETMASK, &old_mask, NULL); diff --git a/usb-linux.c b/usb-linux.c index 53cc5fc00e..184f56f499 100644 --- a/usb-linux.c +++ b/usb-linux.c @@ -341,16 +341,16 @@ static void async_complete(void *opaque) if (p) { switch (aurb->urb.status) { case 0: - p->len += aurb->urb.actual_length; + p->result += aurb->urb.actual_length; break; case -EPIPE: set_halt(s, p->devep); - p->len = USB_RET_STALL; + p->result = USB_RET_STALL; break; default: - p->len = USB_RET_NAK; + p->result = USB_RET_NAK; break; } @@ -604,6 +604,7 @@ static int usb_host_handle_iso_data(USBHostDevice *s, USBPacket *p, int in) { AsyncURB *aurb; int i, j, ret, max_packet_size, offset, len = 0; + uint8_t *buf; max_packet_size = get_max_packet_size(s, p->devep); if (max_packet_size == 0) @@ -628,19 +629,19 @@ static int usb_host_handle_iso_data(USBHostDevice *s, USBPacket *p, int in) len = urb_status_to_usb_ret( aurb[i].urb.iso_frame_desc[j].status); /* Check the frame fits */ - } else if (aurb[i].urb.iso_frame_desc[j].actual_length > p->len) { + } else if (aurb[i].urb.iso_frame_desc[j].actual_length + > p->iov.size) { printf("husb: received iso data is larger then packet\n"); len = USB_RET_NAK; /* All good copy data over */ } else { len = aurb[i].urb.iso_frame_desc[j].actual_length; - memcpy(p->data, - aurb[i].urb.buffer + - j * aurb[i].urb.iso_frame_desc[0].length, - len); + buf = aurb[i].urb.buffer + + j * aurb[i].urb.iso_frame_desc[0].length; + usb_packet_copy(p, buf, len); } } else { - len = p->len; + len = p->iov.size; offset = (j == 0) ? 0 : get_iso_buffer_used(s, p->devep); /* Check the frame fits */ @@ -650,7 +651,7 @@ static int usb_host_handle_iso_data(USBHostDevice *s, USBPacket *p, int in) } /* All good copy data over */ - memcpy(aurb[i].urb.buffer + offset, p->data, len); + usb_packet_copy(p, aurb[i].urb.buffer + offset, len); aurb[i].urb.iso_frame_desc[j].length = len; offset += len; set_iso_buffer_used(s, p->devep, offset); @@ -734,9 +735,9 @@ static int usb_host_handle_data(USBDevice *dev, USBPacket *p) return usb_host_handle_iso_data(s, p, p->pid == USB_TOKEN_IN); } - rem = p->len; - pbuf = p->data; - p->len = 0; + assert(p->iov.niov == 1); /* temporary */ + rem = p->iov.iov[0].iov_len; + pbuf = p->iov.iov[0].iov_base; while (rem) { aurb = async_alloc(s); aurb->packet = p; diff --git a/usb-redir.c b/usb-redir.c index e2129931a0..9e5fce21ea 100644 --- a/usb-redir.c +++ b/usb-redir.c @@ -365,12 +365,12 @@ static int usbredir_handle_iso_data(USBRedirDevice *dev, USBPacket *p, } len = isop->len; - if (len > p->len) { + if (len > p->iov.size) { ERROR("received iso data is larger then packet ep %02X\n", ep); bufp_free(dev, isop, ep); return USB_RET_NAK; } - memcpy(p->data, isop->data, len); + usb_packet_copy(p, isop->data, len); bufp_free(dev, isop, ep); return len; } else { @@ -379,18 +379,20 @@ static int usbredir_handle_iso_data(USBRedirDevice *dev, USBPacket *p, if (dev->endpoint[EP2I(ep)].iso_started) { struct usb_redir_iso_packet_header iso_packet = { .endpoint = ep, - .length = p->len + .length = p->iov.size }; + uint8_t buf[p->iov.size]; /* No id, we look at the ep when receiving a status back */ + usb_packet_copy(p, buf, p->iov.size); usbredirparser_send_iso_packet(dev->parser, 0, &iso_packet, - p->data, p->len); + buf, p->iov.size); usbredirparser_do_write(dev->parser); } status = dev->endpoint[EP2I(ep)].iso_error; dev->endpoint[EP2I(ep)].iso_error = 0; - DPRINTF2("iso-token-out ep %02X status %d len %d\n", ep, status, - p->len); - return usbredir_handle_status(dev, status, p->len); + DPRINTF2("iso-token-out ep %02X status %d len %zd\n", ep, status, + p->iov.size); + return usbredir_handle_status(dev, status, p->iov.size); } } @@ -413,10 +415,11 @@ static int usbredir_handle_bulk_data(USBRedirDevice *dev, USBPacket *p, AsyncURB *aurb = async_alloc(dev, p); struct usb_redir_bulk_packet_header bulk_packet; - DPRINTF("bulk-out ep %02X len %d id %u\n", ep, p->len, aurb->packet_id); + DPRINTF("bulk-out ep %02X len %zd id %u\n", ep, + p->iov.size, aurb->packet_id); bulk_packet.endpoint = ep; - bulk_packet.length = p->len; + bulk_packet.length = p->iov.size; bulk_packet.stream_id = 0; aurb->bulk_packet = bulk_packet; @@ -424,9 +427,11 @@ static int usbredir_handle_bulk_data(USBRedirDevice *dev, USBPacket *p, usbredirparser_send_bulk_packet(dev->parser, aurb->packet_id, &bulk_packet, NULL, 0); } else { - usbredir_log_data(dev, "bulk data out:", p->data, p->len); + uint8_t buf[p->iov.size]; + usb_packet_copy(p, buf, p->iov.size); + usbredir_log_data(dev, "bulk data out:", buf, p->iov.size); usbredirparser_send_bulk_packet(dev->parser, aurb->packet_id, - &bulk_packet, p->data, p->len); + &bulk_packet, buf, p->iov.size); } usbredirparser_do_write(dev->parser); return USB_RET_ASYNC; @@ -471,29 +476,31 @@ static int usbredir_handle_interrupt_data(USBRedirDevice *dev, } len = intp->len; - if (len > p->len) { + if (len > p->iov.size) { ERROR("received int data is larger then packet ep %02X\n", ep); bufp_free(dev, intp, ep); return USB_RET_NAK; } - memcpy(p->data, intp->data, len); + usb_packet_copy(p, intp->data, len); bufp_free(dev, intp, ep); return len; } else { /* Output interrupt endpoint, normal async operation */ AsyncURB *aurb = async_alloc(dev, p); struct usb_redir_interrupt_packet_header interrupt_packet; + uint8_t buf[p->iov.size]; - DPRINTF("interrupt-out ep %02X len %d id %u\n", ep, p->len, + DPRINTF("interrupt-out ep %02X len %zd id %u\n", ep, p->iov.size, aurb->packet_id); interrupt_packet.endpoint = ep; - interrupt_packet.length = p->len; + interrupt_packet.length = p->iov.size; aurb->interrupt_packet = interrupt_packet; - usbredir_log_data(dev, "interrupt data out:", p->data, p->len); + usb_packet_copy(p, buf, p->iov.size); + usbredir_log_data(dev, "interrupt data out:", buf, p->iov.size); usbredirparser_send_interrupt_packet(dev->parser, aurb->packet_id, - &interrupt_packet, p->data, p->len); + &interrupt_packet, buf, p->iov.size); usbredirparser_do_write(dev->parser); return USB_RET_ASYNC; } @@ -959,7 +966,7 @@ static void usbredir_configuration_status(void *priv, uint32_t id, dev->dev.data_buf[0] = config_status->configuration; len = 1; } - aurb->packet->len = + aurb->packet->result = usbredir_handle_status(dev, config_status->status, len); usb_generic_async_ctrl_complete(&dev->dev, aurb->packet); } @@ -987,7 +994,7 @@ static void usbredir_alt_setting_status(void *priv, uint32_t id, dev->dev.data_buf[0] = alt_setting_status->alt; len = 1; } - aurb->packet->len = + aurb->packet->result = usbredir_handle_status(dev, alt_setting_status->status, len); usb_generic_async_ctrl_complete(&dev->dev, aurb->packet); } @@ -1070,7 +1077,7 @@ static void usbredir_control_packet(void *priv, uint32_t id, len = USB_RET_STALL; } } - aurb->packet->len = len; + aurb->packet->result = len; usb_generic_async_ctrl_complete(&dev->dev, aurb->packet); } async_free(dev, aurb); @@ -1105,15 +1112,15 @@ static void usbredir_bulk_packet(void *priv, uint32_t id, len = usbredir_handle_status(dev, bulk_packet->status, len); if (len > 0) { usbredir_log_data(dev, "bulk data in:", data, data_len); - if (data_len <= aurb->packet->len) { - memcpy(aurb->packet->data, data, data_len); + if (data_len <= aurb->packet->iov.size) { + usb_packet_copy(aurb->packet, data, data_len); } else { - ERROR("bulk buffer too small (%d > %d)\n", data_len, - aurb->packet->len); + ERROR("bulk buffer too small (%d > %zd)\n", data_len, + aurb->packet->iov.size); len = USB_RET_STALL; } } - aurb->packet->len = len; + aurb->packet->result = len; usb_packet_complete(&dev->dev, aurb->packet); } async_free(dev, aurb); @@ -1185,7 +1192,7 @@ static void usbredir_interrupt_packet(void *priv, uint32_t id, } if (aurb->packet) { - aurb->packet->len = usbredir_handle_status(dev, + aurb->packet->result = usbredir_handle_status(dev, interrupt_packet->status, len); usb_packet_complete(&dev->dev, aurb->packet); } From 9440b7e5553f1fbceb9afe81a08f4e56975dea6d Mon Sep 17 00:00:00 2001 From: Gerd Hoffmann Date: Wed, 13 Jul 2011 10:53:23 +0200 Subject: [PATCH 204/230] usb-serial: iovec support Add full support for iovecs to usb-serial. Signed-off-by: Gerd Hoffmann --- hw/usb-serial.c | 27 ++++++++++++++++----------- 1 file changed, 16 insertions(+), 11 deletions(-) diff --git a/hw/usb-serial.c b/hw/usb-serial.c index 09731da485..bf2b775e83 100644 --- a/hw/usb-serial.c +++ b/hw/usb-serial.c @@ -359,38 +359,42 @@ static int usb_serial_handle_control(USBDevice *dev, USBPacket *p, static int usb_serial_handle_data(USBDevice *dev, USBPacket *p) { USBSerialState *s = (USBSerialState *)dev; - int ret = 0; + int i, ret = 0; uint8_t devep = p->devep; - uint8_t *data = p->iov.iov[0].iov_base; - int len = p->iov.iov[0].iov_len; - int first_len; + struct iovec *iov; + uint8_t header[2]; + int first_len, len; - assert(p->iov.niov == 1); /* temporary */ switch (p->pid) { case USB_TOKEN_OUT: if (devep != 2) goto fail; - qemu_chr_write(s->cs, data, len); + for (i = 0; i < p->iov.niov; i++) { + iov = p->iov.iov + i; + qemu_chr_write(s->cs, iov->iov_base, iov->iov_len); + } break; case USB_TOKEN_IN: if (devep != 1) goto fail; first_len = RECV_BUF - s->recv_ptr; + len = p->iov.size; if (len <= 2) { ret = USB_RET_NAK; break; } - *data++ = usb_get_modem_lines(s) | 1; + header[0] = usb_get_modem_lines(s) | 1; /* We do not have the uart details */ /* handle serial break */ if (s->event_trigger && s->event_trigger & FTDI_BI) { s->event_trigger &= ~FTDI_BI; - *data = FTDI_BI; + header[1] = FTDI_BI; + usb_packet_copy(p, header, 2); ret = 2; break; } else { - *data++ = 0; + header[1] = 0; } len -= 2; if (len > s->recv_used) @@ -401,9 +405,10 @@ static int usb_serial_handle_data(USBDevice *dev, USBPacket *p) } if (first_len > len) first_len = len; - memcpy(data, s->recv_buf + s->recv_ptr, first_len); + usb_packet_copy(p, header, 2); + usb_packet_copy(p, s->recv_buf + s->recv_ptr, first_len); if (len > first_len) - memcpy(data + first_len, s->recv_buf, len - first_len); + usb_packet_copy(p, s->recv_buf, len - first_len); s->recv_used -= len; s->recv_ptr = (s->recv_ptr + len) % RECV_BUF; ret = len + 2; From b621bab436b1260b1c6f3841f3bb7d76236289dc Mon Sep 17 00:00:00 2001 From: Gerd Hoffmann Date: Wed, 13 Jul 2011 11:28:17 +0200 Subject: [PATCH 205/230] usb-host: iovec support Add full support for iovecs to usb-host. The code can split large transfers into smaller ones already, we are using this to also split requests at iovec borders. Signed-off-by: Gerd Hoffmann --- usb-linux.c | 27 ++++++++++++++++++--------- 1 file changed, 18 insertions(+), 9 deletions(-) diff --git a/usb-linux.c b/usb-linux.c index 184f56f499..5562187bd5 100644 --- a/usb-linux.c +++ b/usb-linux.c @@ -707,7 +707,7 @@ static int usb_host_handle_data(USBDevice *dev, USBPacket *p) USBHostDevice *s = DO_UPCAST(USBHostDevice, dev, dev); struct usbdevfs_urb *urb; AsyncURB *aurb; - int ret, rem; + int ret, rem, prem, v; uint8_t *pbuf; uint8_t ep; @@ -735,10 +735,18 @@ static int usb_host_handle_data(USBDevice *dev, USBPacket *p) return usb_host_handle_iso_data(s, p, p->pid == USB_TOKEN_IN); } - assert(p->iov.niov == 1); /* temporary */ - rem = p->iov.iov[0].iov_len; - pbuf = p->iov.iov[0].iov_base; + v = 0; + prem = p->iov.iov[v].iov_len; + pbuf = p->iov.iov[v].iov_base; + rem = p->iov.size; while (rem) { + if (prem == 0) { + v++; + assert(v < p->iov.niov); + prem = p->iov.iov[v].iov_len; + pbuf = p->iov.iov[v].iov_base; + assert(prem <= rem); + } aurb = async_alloc(s); aurb->packet = p; @@ -747,16 +755,17 @@ static int usb_host_handle_data(USBDevice *dev, USBPacket *p) urb->type = USBDEVFS_URB_TYPE_BULK; urb->usercontext = s; urb->buffer = pbuf; + urb->buffer_length = prem; - if (rem > MAX_USBFS_BUFFER_SIZE) { + if (urb->buffer_length > MAX_USBFS_BUFFER_SIZE) { urb->buffer_length = MAX_USBFS_BUFFER_SIZE; - aurb->more = 1; - } else { - urb->buffer_length = rem; - aurb->more = 0; } pbuf += urb->buffer_length; + prem -= urb->buffer_length; rem -= urb->buffer_length; + if (rem) { + aurb->more = 1; + } ret = ioctl(s->fd, USBDEVFS_SUBMITURB, urb); From 29c74f762bc65d1f38da8624ee4182822db369bf Mon Sep 17 00:00:00 2001 From: Gerd Hoffmann Date: Wed, 13 Jul 2011 12:32:06 +0200 Subject: [PATCH 206/230] usb-storage: iovec support Add full iovec support to usb-storage. Signed-off-by: Gerd Hoffmann --- hw/usb-msd.c | 107 +++++++++++++++++++++++---------------------------- 1 file changed, 49 insertions(+), 58 deletions(-) diff --git a/hw/usb-msd.c b/hw/usb-msd.c index 48e0b343ca..90e57fbf6b 100644 --- a/hw/usb-msd.c +++ b/hw/usb-msd.c @@ -43,8 +43,6 @@ typedef struct { enum USBMSDMode mode; uint32_t scsi_len; uint8_t *scsi_buf; - uint32_t usb_len; - uint8_t *usb_buf; uint32_t data_len; uint32_t residue; uint32_t tag; @@ -176,20 +174,14 @@ static const USBDesc desc = { .str = desc_strings, }; -static void usb_msd_copy_data(MSDState *s) +static void usb_msd_copy_data(MSDState *s, USBPacket *p) { uint32_t len; - len = s->usb_len; + len = p->iov.size - p->result; if (len > s->scsi_len) len = s->scsi_len; - if (s->mode == USB_MSDM_DATAIN) { - memcpy(s->usb_buf, s->scsi_buf, len); - } else { - memcpy(s->scsi_buf, s->usb_buf, len); - } - s->usb_len -= len; + usb_packet_copy(p, s->scsi_buf, len); s->scsi_len -= len; - s->usb_buf += len; s->scsi_buf += len; s->data_len -= len; if (s->scsi_len == 0 || s->data_len == 0) { @@ -221,9 +213,9 @@ static void usb_msd_transfer_data(SCSIRequest *req, uint32_t len) s->scsi_len = len; s->scsi_buf = scsi_req_get_buf(req); if (p) { - usb_msd_copy_data(s); - if (s->packet && s->usb_len == 0) { - p->result = p->iov.size; + usb_msd_copy_data(s, p); + p = s->packet; + if (p && p->result == p->iov.size) { /* Set s->packet to NULL before calling usb_packet_complete because another request may be issued before usb_packet_complete returns. */ @@ -250,16 +242,13 @@ static void usb_msd_command_complete(SCSIRequest *req, uint32_t status) s->mode = USB_MSDM_CBW; } else { if (s->data_len) { - s->data_len -= s->usb_len; - if (s->mode == USB_MSDM_DATAIN) { - memset(s->usb_buf, 0, s->usb_len); - } - s->usb_len = 0; + int len = (p->iov.size - p->result); + usb_packet_skip(p, len); + s->data_len -= len; } if (s->data_len == 0) { s->mode = USB_MSDM_CSW; } - p->result = p->iov.size; } s->packet = NULL; usb_packet_complete(&s->dev, p); @@ -345,10 +334,7 @@ static int usb_msd_handle_data(USBDevice *dev, USBPacket *p) int ret = 0; struct usb_msd_cbw cbw; uint8_t devep = p->devep; - uint8_t *data = p->iov.iov[0].iov_base; - int len = p->iov.iov[0].iov_len; - assert(p->iov.niov == 1); /* temporary */ switch (p->pid) { case USB_TOKEN_OUT: if (devep != 2) @@ -356,11 +342,11 @@ static int usb_msd_handle_data(USBDevice *dev, USBPacket *p) switch (s->mode) { case USB_MSDM_CBW: - if (len != 31) { + if (p->iov.size != 31) { fprintf(stderr, "usb-msd: Bad CBW size"); goto fail; } - memcpy(&cbw, data, 31); + usb_packet_copy(p, &cbw, 31); if (le32_to_cpu(cbw.sig) != 0x43425355) { fprintf(stderr, "usb-msd: Bad signature %08x\n", le32_to_cpu(cbw.sig)); @@ -391,36 +377,39 @@ static int usb_msd_handle_data(USBDevice *dev, USBPacket *p) if (s->mode != USB_MSDM_CSW && s->residue == 0) { scsi_req_continue(s->req); } - ret = len; + ret = p->result; break; case USB_MSDM_DATAOUT: - DPRINTF("Data out %d/%d\n", len, s->data_len); - if (len > s->data_len) + DPRINTF("Data out %zd/%d\n", p->iov.size, s->data_len); + if (p->iov.size > s->data_len) { goto fail; + } - s->usb_buf = data; - s->usb_len = len; if (s->scsi_len) { - usb_msd_copy_data(s); + usb_msd_copy_data(s, p); } - if (s->residue && s->usb_len) { - s->data_len -= s->usb_len; - if (s->data_len == 0) - s->mode = USB_MSDM_CSW; - s->usb_len = 0; + if (s->residue) { + int len = p->iov.size - p->result; + if (len) { + usb_packet_skip(p, len); + s->data_len -= len; + if (s->data_len == 0) { + s->mode = USB_MSDM_CSW; + } + } } - if (s->usb_len) { + if (p->result < p->iov.size) { DPRINTF("Deferring packet %p\n", p); s->packet = p; ret = USB_RET_ASYNC; } else { - ret = len; + ret = p->result; } break; default: - DPRINTF("Unexpected write (len %d)\n", len); + DPRINTF("Unexpected write (len %zd)\n", p->iov.size); goto fail; } break; @@ -431,18 +420,20 @@ static int usb_msd_handle_data(USBDevice *dev, USBPacket *p) switch (s->mode) { case USB_MSDM_DATAOUT: - if (s->data_len != 0 || len < 13) + if (s->data_len != 0 || p->iov.size < 13) { goto fail; + } /* Waiting for SCSI write to complete. */ s->packet = p; ret = USB_RET_ASYNC; break; case USB_MSDM_CSW: - DPRINTF("Command status %d tag 0x%x, len %d\n", - s->result, s->tag, len); - if (len < 13) + DPRINTF("Command status %d tag 0x%x, len %zd\n", + s->result, s->tag, p->iov.size); + if (p->iov.size < 13) { goto fail; + } usb_msd_send_status(s, p); s->mode = USB_MSDM_CBW; @@ -450,32 +441,32 @@ static int usb_msd_handle_data(USBDevice *dev, USBPacket *p) break; case USB_MSDM_DATAIN: - DPRINTF("Data in %d/%d, scsi_len %d\n", len, s->data_len, s->scsi_len); - if (len > s->data_len) - len = s->data_len; - s->usb_buf = data; - s->usb_len = len; + DPRINTF("Data in %zd/%d, scsi_len %d\n", + p->iov.size, s->data_len, s->scsi_len); if (s->scsi_len) { - usb_msd_copy_data(s); + usb_msd_copy_data(s, p); } - if (s->residue && s->usb_len) { - s->data_len -= s->usb_len; - memset(s->usb_buf, 0, s->usb_len); - if (s->data_len == 0) - s->mode = USB_MSDM_CSW; - s->usb_len = 0; + if (s->residue) { + int len = p->iov.size - p->result; + if (len) { + usb_packet_skip(p, len); + s->data_len -= len; + if (s->data_len == 0) { + s->mode = USB_MSDM_CSW; + } + } } - if (s->usb_len) { + if (p->result < p->iov.size) { DPRINTF("Deferring packet %p\n", p); s->packet = p; ret = USB_RET_ASYNC; } else { - ret = len; + ret = p->result; } break; default: - DPRINTF("Unexpected read (len %d)\n", len); + DPRINTF("Unexpected read (len %zd)\n", p->iov.size); goto fail; } break; From df5e66eefb8fb05891c49d0be88e3ed9656993c5 Mon Sep 17 00:00:00 2001 From: Gerd Hoffmann Date: Wed, 13 Jul 2011 15:37:29 +0200 Subject: [PATCH 207/230] uhci: remove buffer Map guest memory and pass on a direct pointer instead of copying the bits to a indirect buffer. Signed-off-by: Gerd Hoffmann --- hw/usb-uhci.c | 15 +++++++-------- 1 file changed, 7 insertions(+), 8 deletions(-) diff --git a/hw/usb-uhci.c b/hw/usb-uhci.c index 20b829ba55..824e3a5e8b 100644 --- a/hw/usb-uhci.c +++ b/hw/usb-uhci.c @@ -31,6 +31,7 @@ #include "qemu-timer.h" #include "usb-uhci.h" #include "iov.h" +#include "dma.h" //#define DEBUG //#define DEBUG_DUMP_DATA @@ -111,6 +112,7 @@ typedef struct UHCIState UHCIState; */ typedef struct UHCIAsync { USBPacket packet; + QEMUSGList sgl; UHCIState *uhci; QTAILQ_ENTRY(UHCIAsync) next; uint32_t td; @@ -118,7 +120,6 @@ typedef struct UHCIAsync { int8_t valid; uint8_t isoc; uint8_t done; - uint8_t buffer[2048]; } UHCIAsync; typedef struct UHCIPort { @@ -176,6 +177,7 @@ static UHCIAsync *uhci_async_alloc(UHCIState *s) async->done = 0; async->isoc = 0; usb_packet_init(&async->packet); + qemu_sglist_init(&async->sgl, 1); return async; } @@ -183,6 +185,7 @@ static UHCIAsync *uhci_async_alloc(UHCIState *s) static void uhci_async_free(UHCIState *s, UHCIAsync *async) { usb_packet_cleanup(&async->packet); + qemu_sglist_destroy(&async->sgl); qemu_free(async); } @@ -706,11 +709,6 @@ static int uhci_complete_td(UHCIState *s, UHCI_TD *td, UHCIAsync *async, uint32_ goto out; } - if (len > 0) { - /* write the data back */ - cpu_physical_memory_write(td->buffer, async->buffer, len); - } - if ((td->ctrl & TD_CTRL_SPD) && len < max_len) { *int_mask |= 0x02; /* short packet: do not update QH */ @@ -827,12 +825,12 @@ static int uhci_handle_td(UHCIState *s, uint32_t addr, UHCI_TD *td, uint32_t *in usb_packet_setup(&async->packet, pid, (td->token >> 8) & 0x7f, (td->token >> 15) & 0xf); - usb_packet_addbuf(&async->packet, async->buffer, max_len); + qemu_sglist_add(&async->sgl, td->buffer, max_len); + usb_packet_map(&async->packet, &async->sgl); switch(pid) { case USB_TOKEN_OUT: case USB_TOKEN_SETUP: - cpu_physical_memory_read(td->buffer, async->buffer, max_len); len = uhci_broadcast_packet(s, &async->packet); if (len >= 0) len = max_len; @@ -859,6 +857,7 @@ static int uhci_handle_td(UHCIState *s, uint32_t addr, UHCI_TD *td, uint32_t *in done: len = uhci_complete_td(s, td, async, int_mask); + usb_packet_unmap(&async->packet); uhci_async_free(s, async); return len; } From 0ce668bc5284ffebd2d0b269ae141f9a696dbd01 Mon Sep 17 00:00:00 2001 From: Gerd Hoffmann Date: Wed, 13 Jul 2011 17:36:46 +0200 Subject: [PATCH 208/230] ehci: iovec support, remove buffer Map guest memory and pass on a direct pointer instead of copying the bits to a indirect buffer. EHCI transfer descriptors can reference multiple (physical guest) pages so we'll actually start seeing usb packets wich carry iovec with more than one element. Signed-off-by: Gerd Hoffmann --- hw/usb-ehci.c | 151 +++++++++++++++++++++----------------------------- 1 file changed, 64 insertions(+), 87 deletions(-) diff --git a/hw/usb-ehci.c b/hw/usb-ehci.c index 799e31a319..2b43895315 100644 --- a/hw/usb-ehci.c +++ b/hw/usb-ehci.c @@ -28,6 +28,7 @@ #include "pci.h" #include "monitor.h" #include "trace.h" +#include "dma.h" #define EHCI_DEBUG 0 @@ -269,6 +270,7 @@ typedef struct EHCIqtd { uint32_t bufptr[5]; // Standard buffer pointer #define QTD_BUFPTR_MASK 0xfffff000 +#define QTD_BUFPTR_SH 12 } EHCIqtd; /* EHCI spec version 1.0 Section 3.6 @@ -357,7 +359,7 @@ struct EHCIQueue { uint32_t qtdaddr; // address QTD read from USBPacket packet; - uint8_t buffer[BUFF_SIZE]; + QEMUSGList sgl; int pid; uint32_t tbytes; enum async_state async; @@ -414,7 +416,7 @@ struct EHCIState { uint32_t p_fetch_addr; // which address to look at next USBPacket ipacket; - uint8_t ibuffer[BUFF_SIZE]; + QEMUSGList isgl; int isoch_pause; uint64_t last_run_ns; @@ -1165,60 +1167,58 @@ static int ehci_qh_do_overlay(EHCIQueue *q) return 0; } -static int ehci_buffer_rw(EHCIQueue *q, int bytes, int rw) +static int ehci_init_transfer(EHCIQueue *q) { - int bufpos = 0; - int cpage, offset; - uint32_t head; - uint32_t tail; - - - if (!bytes) { - return 0; - } - - cpage = get_field(q->qh.token, QTD_TOKEN_CPAGE); - if (cpage > 4) { - fprintf(stderr, "cpage out of range (%d)\n", cpage); - return USB_RET_PROCERR; - } + uint32_t cpage, offset, bytes, plen; + target_phys_addr_t page; + cpage = get_field(q->qh.token, QTD_TOKEN_CPAGE); + bytes = get_field(q->qh.token, QTD_TOKEN_TBYTES); offset = q->qh.bufptr[0] & ~QTD_BUFPTR_MASK; + qemu_sglist_init(&q->sgl, 5); - do { - /* start and end of this page */ - head = q->qh.bufptr[cpage] & QTD_BUFPTR_MASK; - tail = head + ~QTD_BUFPTR_MASK + 1; - /* add offset into page */ - head |= offset; - - if (bytes <= (tail - head)) { - tail = head + bytes; + while (bytes > 0) { + if (cpage > 4) { + fprintf(stderr, "cpage out of range (%d)\n", cpage); + return USB_RET_PROCERR; } - trace_usb_ehci_data(rw, cpage, offset, head, tail-head, bufpos); - cpu_physical_memory_rw(head, q->buffer + bufpos, tail - head, rw); - - bufpos += (tail - head); - offset += (tail - head); - bytes -= (tail - head); - - if (bytes > 0) { - cpage++; + page = q->qh.bufptr[cpage] & QTD_BUFPTR_MASK; + page += offset; + plen = bytes; + if (plen > 4096 - offset) { + plen = 4096 - offset; offset = 0; + cpage++; } - } while (bytes > 0); - - /* save cpage */ - set_field(&q->qh.token, cpage, QTD_TOKEN_CPAGE); - - /* save offset into cpage */ - q->qh.bufptr[0] &= QTD_BUFPTR_MASK; - q->qh.bufptr[0] |= offset; + qemu_sglist_add(&q->sgl, page, plen); + bytes -= plen; + } return 0; } +static void ehci_finish_transfer(EHCIQueue *q, int status) +{ + uint32_t cpage, offset; + + qemu_sglist_destroy(&q->sgl); + + if (status > 0) { + /* update cpage & offset */ + cpage = get_field(q->qh.token, QTD_TOKEN_CPAGE); + offset = q->qh.bufptr[0] & ~QTD_BUFPTR_MASK; + + offset += status; + cpage += offset >> QTD_BUFPTR_SH; + offset &= ~QTD_BUFPTR_MASK; + + set_field(&q->qh.token, cpage, QTD_TOKEN_CPAGE); + q->qh.bufptr[0] &= QTD_BUFPTR_MASK; + q->qh.bufptr[0] |= offset; + } +} + static void ehci_async_complete_packet(USBPort *port, USBPacket *packet) { EHCIQueue *q; @@ -1295,10 +1295,6 @@ err: } if (q->tbytes && q->pid == USB_TOKEN_IN) { - if (ehci_buffer_rw(q, q->usb_status, 1) != 0) { - q->usb_status = USB_RET_PROCERR; - return; - } q->tbytes -= q->usb_status; } else { q->tbytes = 0; @@ -1307,6 +1303,8 @@ err: DPRINTF("updating tbytes to %d\n", q->tbytes); set_field(&q->qh.token, q->tbytes, QTD_TOKEN_TBYTES); } + ehci_finish_transfer(q, q->usb_status); + usb_packet_unmap(&q->packet); q->qh.token ^= QTD_TOKEN_DTOGGLE; q->qh.token &= ~QTD_TOKEN_ACTIVE; @@ -1346,8 +1344,7 @@ static int ehci_execute(EHCIQueue *q) default: fprintf(stderr, "bad token\n"); break; } - if ((q->tbytes && q->pid != USB_TOKEN_IN) && - (ehci_buffer_rw(q, q->tbytes, 0) != 0)) { + if (ehci_init_transfer(q) != 0) { return USB_RET_PROCERR; } @@ -1356,6 +1353,9 @@ static int ehci_execute(EHCIQueue *q) ret = USB_RET_NODEV; + usb_packet_setup(&q->packet, q->pid, devadr, endp); + usb_packet_map(&q->packet, &q->sgl); + // TO-DO: associating device with ehci port for(i = 0; i < NB_PORTS; i++) { port = &q->ehci->ports[i]; @@ -1367,9 +1367,6 @@ static int ehci_execute(EHCIQueue *q) continue; } - usb_packet_setup(&q->packet, q->pid, devadr, endp); - usb_packet_addbuf(&q->packet, q->buffer, q->tbytes); - ret = usb_handle_packet(dev, &q->packet); DPRINTF("submit: qh %x next %x qtd %x pid %x len %zd " @@ -1399,7 +1396,7 @@ static int ehci_process_itd(EHCIState *ehci, USBPort *port; USBDevice *dev; int ret; - uint32_t i, j, len, len1, len2, pid, dir, devaddr, endp; + uint32_t i, j, len, pid, dir, devaddr, endp; uint32_t pg, off, ptr1, ptr2, max, mult; dir =(itd->bufptr[1] & ITD_BUFPTR_DIRECTION); @@ -1424,29 +1421,23 @@ static int ehci_process_itd(EHCIState *ehci, return USB_RET_PROCERR; } + qemu_sglist_init(&ehci->isgl, 2); if (off + len > 4096) { /* transfer crosses page border */ - len2 = off + len - 4096; - len1 = len - len2; + uint32_t len2 = off + len - 4096; + uint32_t len1 = len - len2; + qemu_sglist_add(&ehci->isgl, ptr1 + off, len1); + qemu_sglist_add(&ehci->isgl, ptr2, len2); } else { - len1 = len; - len2 = 0; + qemu_sglist_add(&ehci->isgl, ptr1 + off, len); } - if (!dir) { - pid = USB_TOKEN_OUT; - trace_usb_ehci_data(0, pg, off, ptr1 + off, len1, 0); - cpu_physical_memory_rw(ptr1 + off, &ehci->ibuffer[0], len1, 0); - if (len2) { - trace_usb_ehci_data(0, pg+1, 0, ptr2, len2, len1); - cpu_physical_memory_rw(ptr2, &ehci->ibuffer[len1], len2, 0); - } - } else { - pid = USB_TOKEN_IN; - } + pid = dir ? USB_TOKEN_IN : USB_TOKEN_OUT; + + usb_packet_setup(&ehci->ipacket, pid, devaddr, endp); + usb_packet_map(&ehci->ipacket, &ehci->isgl); ret = USB_RET_NODEV; - for (j = 0; j < NB_PORTS; j++) { port = &ehci->ports[j]; dev = port->dev; @@ -1455,9 +1446,6 @@ static int ehci_process_itd(EHCIState *ehci, continue; } - usb_packet_setup(&ehci->ipacket, pid, devaddr, endp); - usb_packet_addbuf(&ehci->ipacket, ehci->ibuffer, len); - ret = usb_handle_packet(dev, &ehci->ipacket); if (ret != USB_RET_NODEV) { @@ -1465,6 +1453,9 @@ static int ehci_process_itd(EHCIState *ehci, } } + usb_packet_unmap(&ehci->ipacket); + qemu_sglist_destroy(&ehci->isgl); + #if 0 /* In isoch, there is no facility to indicate a NAK so let's * instead just complete a zero-byte transaction. Setting @@ -1502,20 +1493,6 @@ static int ehci_process_itd(EHCIState *ehci, set_field(&itd->transact[i], len - ret, ITD_XACT_LENGTH); } else { /* IN */ - if (len1 > ret) { - len1 = ret; - } - if (len2 > ret - len1) { - len2 = ret - len1; - } - if (len1) { - trace_usb_ehci_data(1, pg, off, ptr1 + off, len1, 0); - cpu_physical_memory_rw(ptr1 + off, &ehci->ibuffer[0], len1, 1); - } - if (len2) { - trace_usb_ehci_data(1, pg+1, 0, ptr2, len2, len1); - cpu_physical_memory_rw(ptr2, &ehci->ibuffer[len1], len2, 1); - } set_field(&itd->transact[i], ret, ITD_XACT_LENGTH); } From 0d878eec1eddcf01f07c38c6040ea91184139299 Mon Sep 17 00:00:00 2001 From: Gerd Hoffmann Date: Fri, 15 Jul 2011 13:12:44 +0200 Subject: [PATCH 209/230] usb-hid: create & use HIDState First step in separating out the HID emulation code from usb-hid, so it can be reused without creating a dummy usb device like bluetooth does. This creates a HIDState struct, moves the non-usbish fields from USBHIDStruct there. Renames non-usbish structs, defines and functions from usb* to hid*. Adapts the code to that. Also cleans up a bunch of code style issues along the way. Signed-off-by: Gerd Hoffmann --- hw/usb-hid.c | 318 +++++++++++++++++++++++++++------------------------ 1 file changed, 169 insertions(+), 149 deletions(-) diff --git a/hw/usb-hid.c b/hw/usb-hid.c index 541644a06d..f5d6c613a7 100644 --- a/hw/usb-hid.c +++ b/hw/usb-hid.c @@ -41,42 +41,46 @@ #define USB_DT_REPORT 0x22 #define USB_DT_PHY 0x23 -#define USB_MOUSE 1 -#define USB_TABLET 2 -#define USB_KEYBOARD 3 +#define HID_MOUSE 1 +#define HID_TABLET 2 +#define HID_KEYBOARD 3 -typedef struct USBPointerEvent { +typedef struct HIDPointerEvent { int32_t xdx, ydy; /* relative iff it's a mouse, otherwise absolute */ int32_t dz, buttons_state; -} USBPointerEvent; +} HIDPointerEvent; #define QUEUE_LENGTH 16 /* should be enough for a triple-click */ #define QUEUE_MASK (QUEUE_LENGTH-1u) #define QUEUE_INCR(v) ((v)++, (v) &= QUEUE_MASK) -typedef struct USBMouseState { - USBPointerEvent queue[QUEUE_LENGTH]; +typedef struct HIDMouseState { + HIDPointerEvent queue[QUEUE_LENGTH]; int mouse_grabbed; QEMUPutMouseEntry *eh_entry; -} USBMouseState; +} HIDMouseState; -typedef struct USBKeyboardState { +typedef struct HIDKeyboardState { uint32_t keycodes[QUEUE_LENGTH]; uint16_t modifiers; uint8_t leds; uint8_t key[16]; int32_t keys; -} USBKeyboardState; +} HIDKeyboardState; -typedef struct USBHIDState { - USBDevice dev; +typedef struct HIDState { union { - USBMouseState ptr; - USBKeyboardState kbd; + HIDMouseState ptr; + HIDKeyboardState kbd; }; uint32_t head; /* index into circular queue */ uint32_t n; int kind; +} HIDState; + +typedef struct USBHIDState { + USBDevice dev; + HIDState hid; int32_t protocol; uint8_t idle; int64_t next_idle_clock; @@ -446,12 +450,13 @@ static void usb_hid_changed(USBHIDState *hs) usb_wakeup(&hs->dev); } -static void usb_pointer_event_clear(USBPointerEvent *e, int buttons) { +static void hid_pointer_event_clear(HIDPointerEvent *e, int buttons) +{ e->xdx = e->ydy = e->dz = 0; e->buttons_state = buttons; } -static void usb_pointer_event_combine(USBPointerEvent *e, int xyrel, +static void hid_pointer_event_combine(HIDPointerEvent *e, int xyrel, int x1, int y1, int z1) { if (xyrel) { e->xdx += x1; @@ -471,8 +476,8 @@ static void usb_pointer_event_combine(USBPointerEvent *e, int xyrel, static void usb_pointer_event(void *opaque, int x1, int y1, int z1, int buttons_state) { - USBHIDState *hs = opaque; - USBMouseState *s = &hs->ptr; + USBHIDState *us = opaque; + HIDState *hs = &us->hid; unsigned use_slot = (hs->head + hs->n - 1) & QUEUE_MASK; unsigned previous_slot = (use_slot - 1) & QUEUE_MASK; @@ -483,25 +488,26 @@ static void usb_pointer_event(void *opaque, * the first event changed the button state. */ if (hs->n == QUEUE_LENGTH) { /* Queue full. Discard old button state, combine motion normally. */ - s->queue[use_slot].buttons_state = buttons_state; + hs->ptr.queue[use_slot].buttons_state = buttons_state; } else if (hs->n < 2 || - s->queue[use_slot].buttons_state != buttons_state || - s->queue[previous_slot].buttons_state != s->queue[use_slot].buttons_state) { + hs->ptr.queue[use_slot].buttons_state != buttons_state || + hs->ptr.queue[previous_slot].buttons_state != + hs->ptr.queue[use_slot].buttons_state) { /* Cannot or should not combine, so add an empty item to the queue. */ QUEUE_INCR(use_slot); hs->n++; - usb_pointer_event_clear(&s->queue[use_slot], buttons_state); + hid_pointer_event_clear(&hs->ptr.queue[use_slot], buttons_state); } - usb_pointer_event_combine(&s->queue[use_slot], - hs->kind == USB_MOUSE, + hid_pointer_event_combine(&hs->ptr.queue[use_slot], + hs->kind == HID_MOUSE, x1, y1, z1); - usb_hid_changed(hs); + usb_hid_changed(us); } static void usb_keyboard_event(void *opaque, int keycode) { - USBHIDState *hs = opaque; - USBKeyboardState *s = &hs->kbd; + USBHIDState *us = opaque; + HIDState *hs = &us->hid; int slot; if (hs->n == QUEUE_LENGTH) { @@ -509,13 +515,12 @@ static void usb_keyboard_event(void *opaque, int keycode) return; } slot = (hs->head + hs->n) & QUEUE_MASK; hs->n++; - s->keycodes[slot] = keycode; - usb_hid_changed(hs); + hs->kbd.keycodes[slot] = keycode; + usb_hid_changed(us); } -static void usb_keyboard_process_keycode(USBHIDState *hs) +static void hid_keyboard_process_keycode(HIDState *hs) { - USBKeyboardState *s = &hs->kbd; uint8_t hid_code, key; int i, keycode, slot; @@ -523,49 +528,55 @@ static void usb_keyboard_process_keycode(USBHIDState *hs) return; } slot = hs->head & QUEUE_MASK; QUEUE_INCR(hs->head); hs->n--; - keycode = s->keycodes[slot]; + keycode = hs->kbd.keycodes[slot]; key = keycode & 0x7f; - hid_code = usb_hid_usage_keys[key | ((s->modifiers >> 1) & (1 << 7))]; - s->modifiers &= ~(1 << 8); + hid_code = usb_hid_usage_keys[key | ((hs->kbd.modifiers >> 1) & (1 << 7))]; + hs->kbd.modifiers &= ~(1 << 8); switch (hid_code) { case 0x00: return; case 0xe0: - if (s->modifiers & (1 << 9)) { - s->modifiers ^= 3 << 8; + if (hs->kbd.modifiers & (1 << 9)) { + hs->kbd.modifiers ^= 3 << 8; return; } case 0xe1 ... 0xe7: if (keycode & (1 << 7)) { - s->modifiers &= ~(1 << (hid_code & 0x0f)); + hs->kbd.modifiers &= ~(1 << (hid_code & 0x0f)); return; } case 0xe8 ... 0xef: - s->modifiers |= 1 << (hid_code & 0x0f); + hs->kbd.modifiers |= 1 << (hid_code & 0x0f); return; } if (keycode & (1 << 7)) { - for (i = s->keys - 1; i >= 0; i --) - if (s->key[i] == hid_code) { - s->key[i] = s->key[-- s->keys]; - s->key[s->keys] = 0x00; + for (i = hs->kbd.keys - 1; i >= 0; i--) { + if (hs->kbd.key[i] == hid_code) { + hs->kbd.key[i] = hs->kbd.key[-- hs->kbd.keys]; + hs->kbd.key[hs->kbd.keys] = 0x00; break; } - if (i < 0) - return; - } else { - for (i = s->keys - 1; i >= 0; i --) - if (s->key[i] == hid_code) - break; + } if (i < 0) { - if (s->keys < sizeof(s->key)) - s->key[s->keys ++] = hid_code; - } else return; + } + } else { + for (i = hs->kbd.keys - 1; i >= 0; i--) { + if (hs->kbd.key[i] == hid_code) { + break; + } + } + if (i < 0) { + if (hs->kbd.keys < sizeof(hs->kbd.key)) { + hs->kbd.key[hs->kbd.keys++] = hid_code; + } + } else { + return; + } } } @@ -579,24 +590,23 @@ static inline int int_clamp(int val, int vmin, int vmax) return val; } -static int usb_pointer_poll(USBHIDState *hs, uint8_t *buf, int len) +static int hid_pointer_poll(HIDState *hs, uint8_t *buf, int len) { int dx, dy, dz, b, l; int index; - USBMouseState *s = &hs->ptr; - USBPointerEvent *e; + HIDPointerEvent *e; - if (!s->mouse_grabbed) { - qemu_activate_mouse_event_handler(s->eh_entry); - s->mouse_grabbed = 1; + if (!hs->ptr.mouse_grabbed) { + qemu_activate_mouse_event_handler(hs->ptr.eh_entry); + hs->ptr.mouse_grabbed = 1; } /* When the buffer is empty, return the last event. Relative movements will all be zero. */ index = (hs->n ? hs->head : hs->head - 1); - e = &s->queue[index & QUEUE_MASK]; + e = &hs->ptr.queue[index & QUEUE_MASK]; - if (hs->kind == USB_MOUSE) { + if (hs->kind == HID_MOUSE) { dx = int_clamp(e->xdx, -127, 127); dy = int_clamp(e->ydy, -127, 127); e->xdx -= dx; @@ -618,7 +628,7 @@ static int usb_pointer_poll(USBHIDState *hs, uint8_t *buf, int len) if (hs->n && !e->dz && - (hs->kind == USB_TABLET || (!e->xdx && !e->ydy))) { + (hs->kind == HID_TABLET || (!e->xdx && !e->ydy))) { /* that deals with this event */ QUEUE_INCR(hs->head); hs->n--; @@ -628,7 +638,7 @@ static int usb_pointer_poll(USBHIDState *hs, uint8_t *buf, int len) dz = 0 - dz; l = 0; switch (hs->kind) { - case USB_MOUSE: + case HID_MOUSE: if (len > l) buf[l++] = b; if (len > l) @@ -639,7 +649,7 @@ static int usb_pointer_poll(USBHIDState *hs, uint8_t *buf, int len) buf[l++] = dz; break; - case USB_TABLET: + case HID_TABLET: if (len > l) buf[l++] = b; if (len > l) @@ -661,25 +671,25 @@ static int usb_pointer_poll(USBHIDState *hs, uint8_t *buf, int len) return l; } -static int usb_keyboard_poll(USBHIDState *hs, uint8_t *buf, int len) +static int hid_keyboard_poll(HIDState *hs, uint8_t *buf, int len) { - USBKeyboardState *s = &hs->kbd; if (len < 2) return 0; - usb_keyboard_process_keycode(hs); + hid_keyboard_process_keycode(hs); - buf[0] = s->modifiers & 0xff; + buf[0] = hs->kbd.modifiers & 0xff; buf[1] = 0; - if (s->keys > 6) + if (hs->kbd.keys > 6) { memset(buf + 2, USB_HID_USAGE_ERROR_ROLLOVER, MIN(8, len) - 2); - else - memcpy(buf + 2, s->key, MIN(8, len) - 2); + } else { + memcpy(buf + 2, hs->kbd.key, MIN(8, len) - 2); + } return MIN(8, len); } -static int usb_keyboard_write(USBKeyboardState *s, uint8_t *buf, int len) +static int hid_keyboard_write(HIDState *hs, uint8_t *buf, int len) { if (len > 0) { int ledstate = 0; @@ -688,13 +698,16 @@ static int usb_keyboard_write(USBKeyboardState *s, uint8_t *buf, int len) * 0x04: Scroll Lock LED * 0x08: Compose LED * 0x10: Kana LED */ - s->leds = buf[0]; - if (s->leds & 0x04) + hs->kbd.leds = buf[0]; + if (hs->kbd.leds & 0x04) { ledstate |= QEMU_SCROLL_LOCK_LED; - if (s->leds & 0x01) + } + if (hs->kbd.leds & 0x01) { ledstate |= QEMU_NUM_LOCK_LED; - if (s->leds & 0x02) + } + if (hs->kbd.leds & 0x02) { ledstate |= QEMU_CAPS_LOCK_LED; + } kbd_put_ledstate(ledstate); } return 0; @@ -702,25 +715,25 @@ static int usb_keyboard_write(USBKeyboardState *s, uint8_t *buf, int len) static void usb_mouse_handle_reset(USBDevice *dev) { - USBHIDState *s = (USBHIDState *)dev; + USBHIDState *us = DO_UPCAST(USBHIDState, dev, dev); - memset(s->ptr.queue, 0, sizeof (s->ptr.queue)); - s->head = 0; - s->n = 0; - s->protocol = 1; + memset(us->hid.ptr.queue, 0, sizeof(us->hid.ptr.queue)); + us->hid.head = 0; + us->hid.n = 0; + us->protocol = 1; } static void usb_keyboard_handle_reset(USBDevice *dev) { - USBHIDState *s = (USBHIDState *)dev; + USBHIDState *us = DO_UPCAST(USBHIDState, dev, dev); - qemu_add_kbd_event_handler(usb_keyboard_event, s); - memset(s->kbd.keycodes, 0, sizeof (s->kbd.keycodes)); - s->head = 0; - s->n = 0; - memset(s->kbd.key, 0, sizeof (s->kbd.key)); - s->kbd.keys = 0; - s->protocol = 1; + qemu_add_kbd_event_handler(usb_keyboard_event, us); + memset(us->hid.kbd.keycodes, 0, sizeof(us->hid.kbd.keycodes)); + us->hid.head = 0; + us->hid.n = 0; + memset(us->hid.kbd.key, 0, sizeof(us->hid.kbd.key)); + us->hid.kbd.keys = 0; + us->protocol = 1; } static void usb_hid_set_next_idle(USBHIDState *s, int64_t curtime) @@ -731,7 +744,8 @@ static void usb_hid_set_next_idle(USBHIDState *s, int64_t curtime) static int usb_hid_handle_control(USBDevice *dev, USBPacket *p, int request, int value, int index, int length, uint8_t *data) { - USBHIDState *s = (USBHIDState *)dev; + USBHIDState *us = DO_UPCAST(USBHIDState, dev, dev); + HIDState *hs = &us->hid; int ret; ret = usb_desc_handle_control(dev, p, request, value, index, length, data); @@ -740,7 +754,7 @@ static int usb_hid_handle_control(USBDevice *dev, USBPacket *p, } ret = 0; - switch(request) { + switch (request) { case DeviceRequest | USB_REQ_GET_INTERFACE: data[0] = 0; ret = 1; @@ -750,17 +764,17 @@ static int usb_hid_handle_control(USBDevice *dev, USBPacket *p, break; /* hid specific requests */ case InterfaceRequest | USB_REQ_GET_DESCRIPTOR: - switch(value >> 8) { + switch (value >> 8) { case 0x22: - if (s->kind == USB_MOUSE) { + if (hs->kind == HID_MOUSE) { memcpy(data, qemu_mouse_hid_report_descriptor, sizeof(qemu_mouse_hid_report_descriptor)); ret = sizeof(qemu_mouse_hid_report_descriptor); - } else if (s->kind == USB_TABLET) { - memcpy(data, qemu_tablet_hid_report_descriptor, + } else if (hs->kind == HID_TABLET) { + memcpy(data, qemu_tablet_hid_report_descriptor, sizeof(qemu_tablet_hid_report_descriptor)); ret = sizeof(qemu_tablet_hid_report_descriptor); - } else if (s->kind == USB_KEYBOARD) { + } else if (hs->kind == HID_KEYBOARD) { memcpy(data, qemu_keyboard_hid_report_descriptor, sizeof(qemu_keyboard_hid_report_descriptor)); ret = sizeof(qemu_keyboard_hid_report_descriptor); @@ -771,38 +785,41 @@ static int usb_hid_handle_control(USBDevice *dev, USBPacket *p, } break; case GET_REPORT: - if (s->kind == USB_MOUSE || s->kind == USB_TABLET) { - ret = usb_pointer_poll(s, data, length); - } else if (s->kind == USB_KEYBOARD) { - ret = usb_keyboard_poll(s, data, length); + if (hs->kind == HID_MOUSE || hs->kind == HID_TABLET) { + ret = hid_pointer_poll(hs, data, length); + } else if (hs->kind == HID_KEYBOARD) { + ret = hid_keyboard_poll(hs, data, length); } - s->changed = s->n > 0; + us->changed = hs->n > 0; break; case SET_REPORT: - if (s->kind == USB_KEYBOARD) - ret = usb_keyboard_write(&s->kbd, data, length); - else + if (hs->kind == HID_KEYBOARD) { + ret = hid_keyboard_write(hs, data, length); + } else { goto fail; + } break; case GET_PROTOCOL: - if (s->kind != USB_KEYBOARD && s->kind != USB_MOUSE) + if (hs->kind != HID_KEYBOARD && hs->kind != HID_MOUSE) { goto fail; + } ret = 1; - data[0] = s->protocol; + data[0] = us->protocol; break; case SET_PROTOCOL: - if (s->kind != USB_KEYBOARD && s->kind != USB_MOUSE) + if (hs->kind != HID_KEYBOARD && hs->kind != HID_MOUSE) { goto fail; + } ret = 0; - s->protocol = value; + us->protocol = value; break; case GET_IDLE: ret = 1; - data[0] = s->idle; + data[0] = us->idle; break; case SET_IDLE: - s->idle = (uint8_t) (value >> 8); - usb_hid_set_next_idle(s, qemu_get_clock_ns(vm_clock)); + us->idle = (uint8_t) (value >> 8); + usb_hid_set_next_idle(us, qemu_get_clock_ns(vm_clock)); ret = 0; break; default: @@ -815,25 +832,27 @@ static int usb_hid_handle_control(USBDevice *dev, USBPacket *p, static int usb_hid_handle_data(USBDevice *dev, USBPacket *p) { - USBHIDState *s = (USBHIDState *)dev; + USBHIDState *us = DO_UPCAST(USBHIDState, dev, dev); + HIDState *hs = &us->hid; uint8_t buf[p->iov.size]; int ret = 0; - switch(p->pid) { + switch (p->pid) { case USB_TOKEN_IN: if (p->devep == 1) { int64_t curtime = qemu_get_clock_ns(vm_clock); - if (!s->changed && (!s->idle || s->next_idle_clock - curtime > 0)) + if (!us->changed && + (!us->idle || us->next_idle_clock - curtime > 0)) { return USB_RET_NAK; - usb_hid_set_next_idle(s, curtime); - if (s->kind == USB_MOUSE || s->kind == USB_TABLET) { - ret = usb_pointer_poll(s, buf, p->iov.size); } - else if (s->kind == USB_KEYBOARD) { - ret = usb_keyboard_poll(s, buf, p->iov.size); + usb_hid_set_next_idle(us, curtime); + if (hs->kind == HID_MOUSE || hs->kind == HID_TABLET) { + ret = hid_pointer_poll(hs, buf, p->iov.size); + } else if (hs->kind == HID_KEYBOARD) { + ret = hid_keyboard_poll(hs, buf, p->iov.size); } usb_packet_copy(p, buf, ret); - s->changed = s->n > 0; + us->changed = hs->n > 0; } else { goto fail; } @@ -849,50 +868,51 @@ static int usb_hid_handle_data(USBDevice *dev, USBPacket *p) static void usb_hid_handle_destroy(USBDevice *dev) { - USBHIDState *s = (USBHIDState *)dev; + USBHIDState *us = DO_UPCAST(USBHIDState, dev, dev); - switch(s->kind) { - case USB_KEYBOARD: + switch (us->hid.kind) { + case HID_KEYBOARD: qemu_remove_kbd_event_handler(); break; default: - qemu_remove_mouse_event_handler(s->ptr.eh_entry); + qemu_remove_mouse_event_handler(us->hid.ptr.eh_entry); } } static int usb_hid_initfn(USBDevice *dev, int kind) { - USBHIDState *s = DO_UPCAST(USBHIDState, dev, dev); + USBHIDState *us = DO_UPCAST(USBHIDState, dev, dev); + HIDState *hs = &us->hid; usb_desc_init(dev); - s->kind = kind; + hs->kind = kind; - if (s->kind == USB_MOUSE) { - s->ptr.eh_entry = qemu_add_mouse_event_handler(usb_pointer_event, s, - 0, "QEMU USB Mouse"); - } else if (s->kind == USB_TABLET) { - s->ptr.eh_entry = qemu_add_mouse_event_handler(usb_pointer_event, s, - 1, "QEMU USB Tablet"); + if (hs->kind == HID_MOUSE) { + hs->ptr.eh_entry = qemu_add_mouse_event_handler(usb_pointer_event, us, + 0, "QEMU HID Mouse"); + } else if (hs->kind == HID_TABLET) { + hs->ptr.eh_entry = qemu_add_mouse_event_handler(usb_pointer_event, us, + 1, "QEMU HID Tablet"); } /* Force poll routine to be run and grab input the first time. */ - s->changed = 1; + us->changed = 1; return 0; } static int usb_tablet_initfn(USBDevice *dev) { - return usb_hid_initfn(dev, USB_TABLET); + return usb_hid_initfn(dev, HID_TABLET); } static int usb_mouse_initfn(USBDevice *dev) { - return usb_hid_initfn(dev, USB_MOUSE); + return usb_hid_initfn(dev, HID_MOUSE); } static int usb_keyboard_initfn(USBDevice *dev) { - return usb_hid_initfn(dev, USB_KEYBOARD); + return usb_hid_initfn(dev, HID_KEYBOARD); } void usb_hid_datain_cb(USBDevice *dev, void *opaque, void (*datain)(void *)) @@ -918,10 +938,10 @@ static const VMStateDescription vmstate_usb_ptr_queue = { .version_id = 1, .minimum_version_id = 1, .fields = (VMStateField []) { - VMSTATE_INT32(xdx, USBPointerEvent), - VMSTATE_INT32(ydy, USBPointerEvent), - VMSTATE_INT32(dz, USBPointerEvent), - VMSTATE_INT32(buttons_state, USBPointerEvent), + VMSTATE_INT32(xdx, HIDPointerEvent), + VMSTATE_INT32(ydy, HIDPointerEvent), + VMSTATE_INT32(dz, HIDPointerEvent), + VMSTATE_INT32(buttons_state, HIDPointerEvent), VMSTATE_END_OF_LIST() } }; @@ -932,10 +952,10 @@ static const VMStateDescription vmstate_usb_ptr = { .post_load = usb_hid_post_load, .fields = (VMStateField []) { VMSTATE_USB_DEVICE(dev, USBHIDState), - VMSTATE_STRUCT_ARRAY(ptr.queue, USBHIDState, QUEUE_LENGTH, 0, - vmstate_usb_ptr_queue, USBPointerEvent), - VMSTATE_UINT32(head, USBHIDState), - VMSTATE_UINT32(n, USBHIDState), + VMSTATE_STRUCT_ARRAY(hid.ptr.queue, USBHIDState, QUEUE_LENGTH, 0, + vmstate_usb_ptr_queue, HIDPointerEvent), + VMSTATE_UINT32(hid.head, USBHIDState), + VMSTATE_UINT32(hid.n, USBHIDState), VMSTATE_INT32(protocol, USBHIDState), VMSTATE_UINT8(idle, USBHIDState), VMSTATE_END_OF_LIST() @@ -949,13 +969,13 @@ static const VMStateDescription vmstate_usb_kbd = { .post_load = usb_hid_post_load, .fields = (VMStateField []) { VMSTATE_USB_DEVICE(dev, USBHIDState), - VMSTATE_UINT32_ARRAY(kbd.keycodes, USBHIDState, QUEUE_LENGTH), - VMSTATE_UINT32(head, USBHIDState), - VMSTATE_UINT32(n, USBHIDState), - VMSTATE_UINT16(kbd.modifiers, USBHIDState), - VMSTATE_UINT8(kbd.leds, USBHIDState), - VMSTATE_UINT8_ARRAY(kbd.key, USBHIDState, 16), - VMSTATE_INT32(kbd.keys, USBHIDState), + VMSTATE_UINT32_ARRAY(hid.kbd.keycodes, USBHIDState, QUEUE_LENGTH), + VMSTATE_UINT32(hid.head, USBHIDState), + VMSTATE_UINT32(hid.n, USBHIDState), + VMSTATE_UINT16(hid.kbd.modifiers, USBHIDState), + VMSTATE_UINT8(hid.kbd.leds, USBHIDState), + VMSTATE_UINT8_ARRAY(hid.kbd.key, USBHIDState, 16), + VMSTATE_INT32(hid.kbd.keys, USBHIDState), VMSTATE_INT32(protocol, USBHIDState), VMSTATE_UINT8(idle, USBHIDState), VMSTATE_END_OF_LIST() From 8bde6805412a2808009a84f1ce5f47b88b0352d0 Mon Sep 17 00:00:00 2001 From: Gerd Hoffmann Date: Fri, 15 Jul 2011 14:37:15 +0200 Subject: [PATCH 210/230] usb-hid: add event callback Add callback for event notification, which allows to un-usbify more functions. Also split separate hid_* functions for reset and release. Signed-off-by: Gerd Hoffmann --- hw/usb-hid.c | 116 ++++++++++++++++++++++++++++++--------------------- 1 file changed, 68 insertions(+), 48 deletions(-) diff --git a/hw/usb-hid.c b/hw/usb-hid.c index f5d6c613a7..870cc66272 100644 --- a/hw/usb-hid.c +++ b/hw/usb-hid.c @@ -54,6 +54,9 @@ typedef struct HIDPointerEvent { #define QUEUE_MASK (QUEUE_LENGTH-1u) #define QUEUE_INCR(v) ((v)++, (v) &= QUEUE_MASK) +typedef struct HIDState HIDState; +typedef void (*HIDEventFunc)(HIDState *s); + typedef struct HIDMouseState { HIDPointerEvent queue[QUEUE_LENGTH]; int mouse_grabbed; @@ -68,7 +71,7 @@ typedef struct HIDKeyboardState { int32_t keys; } HIDKeyboardState; -typedef struct HIDState { +struct HIDState { union { HIDMouseState ptr; HIDKeyboardState kbd; @@ -76,7 +79,8 @@ typedef struct HIDState { uint32_t head; /* index into circular queue */ uint32_t n; int kind; -} HIDState; + HIDEventFunc event; +}; typedef struct USBHIDState { USBDevice dev; @@ -440,14 +444,17 @@ static const uint8_t usb_hid_usage_keys[0x100] = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, }; -static void usb_hid_changed(USBHIDState *hs) +static void usb_hid_changed(HIDState *hs) { - hs->changed = 1; + USBHIDState *us = container_of(hs, USBHIDState, hid); - if (hs->datain) - hs->datain(hs->datain_opaque); + us->changed = 1; - usb_wakeup(&hs->dev); + if (us->datain) { + us->datain(us->datain_opaque); + } + + usb_wakeup(&us->dev); } static void hid_pointer_event_clear(HIDPointerEvent *e, int buttons) @@ -473,11 +480,10 @@ static void hid_pointer_event_combine(HIDPointerEvent *e, int xyrel, e->dz += z1; } -static void usb_pointer_event(void *opaque, +static void hid_pointer_event(void *opaque, int x1, int y1, int z1, int buttons_state) { - USBHIDState *us = opaque; - HIDState *hs = &us->hid; + HIDState *hs = opaque; unsigned use_slot = (hs->head + hs->n - 1) & QUEUE_MASK; unsigned previous_slot = (use_slot - 1) & QUEUE_MASK; @@ -501,13 +507,12 @@ static void usb_pointer_event(void *opaque, hid_pointer_event_combine(&hs->ptr.queue[use_slot], hs->kind == HID_MOUSE, x1, y1, z1); - usb_hid_changed(us); + hs->event(hs); } -static void usb_keyboard_event(void *opaque, int keycode) +static void hid_keyboard_event(void *opaque, int keycode) { - USBHIDState *us = opaque; - HIDState *hs = &us->hid; + HIDState *hs = opaque; int slot; if (hs->n == QUEUE_LENGTH) { @@ -516,7 +521,7 @@ static void usb_keyboard_event(void *opaque, int keycode) } slot = (hs->head + hs->n) & QUEUE_MASK; hs->n++; hs->kbd.keycodes[slot] = keycode; - usb_hid_changed(us); + hs->event(hs); } static void hid_keyboard_process_keycode(HIDState *hs) @@ -713,26 +718,29 @@ static int hid_keyboard_write(HIDState *hs, uint8_t *buf, int len) return 0; } -static void usb_mouse_handle_reset(USBDevice *dev) +static void hid_handle_reset(HIDState *hs) { - USBHIDState *us = DO_UPCAST(USBHIDState, dev, dev); - - memset(us->hid.ptr.queue, 0, sizeof(us->hid.ptr.queue)); - us->hid.head = 0; - us->hid.n = 0; - us->protocol = 1; + switch (hs->kind) { + case HID_KEYBOARD: + qemu_add_kbd_event_handler(hid_keyboard_event, hs); + memset(hs->kbd.keycodes, 0, sizeof(hs->kbd.keycodes)); + memset(hs->kbd.key, 0, sizeof(hs->kbd.key)); + hs->kbd.keys = 0; + break; + case HID_MOUSE: + case HID_TABLET: + memset(hs->ptr.queue, 0, sizeof(hs->ptr.queue)); + break; + } + hs->head = 0; + hs->n = 0; } -static void usb_keyboard_handle_reset(USBDevice *dev) +static void usb_hid_handle_reset(USBDevice *dev) { USBHIDState *us = DO_UPCAST(USBHIDState, dev, dev); - qemu_add_kbd_event_handler(usb_keyboard_event, us); - memset(us->hid.kbd.keycodes, 0, sizeof(us->hid.kbd.keycodes)); - us->hid.head = 0; - us->hid.n = 0; - memset(us->hid.kbd.key, 0, sizeof(us->hid.kbd.key)); - us->hid.kbd.keys = 0; + hid_handle_reset(&us->hid); us->protocol = 1; } @@ -866,34 +874,46 @@ static int usb_hid_handle_data(USBDevice *dev, USBPacket *p) return ret; } +static void hid_free(HIDState *hs) +{ + switch (hs->kind) { + case HID_KEYBOARD: + qemu_remove_kbd_event_handler(); + break; + case HID_MOUSE: + case HID_TABLET: + qemu_remove_mouse_event_handler(hs->ptr.eh_entry); + break; + } +} + static void usb_hid_handle_destroy(USBDevice *dev) { USBHIDState *us = DO_UPCAST(USBHIDState, dev, dev); - switch (us->hid.kind) { - case HID_KEYBOARD: - qemu_remove_kbd_event_handler(); - break; - default: - qemu_remove_mouse_event_handler(us->hid.ptr.eh_entry); + hid_free(&us->hid); +} + +static void hid_init(HIDState *hs, int kind, HIDEventFunc event) +{ + hs->kind = kind; + hs->event = event; + + if (hs->kind == HID_MOUSE) { + hs->ptr.eh_entry = qemu_add_mouse_event_handler(hid_pointer_event, hs, + 0, "QEMU HID Mouse"); + } else if (hs->kind == HID_TABLET) { + hs->ptr.eh_entry = qemu_add_mouse_event_handler(hid_pointer_event, hs, + 1, "QEMU HID Tablet"); } } static int usb_hid_initfn(USBDevice *dev, int kind) { USBHIDState *us = DO_UPCAST(USBHIDState, dev, dev); - HIDState *hs = &us->hid; usb_desc_init(dev); - hs->kind = kind; - - if (hs->kind == HID_MOUSE) { - hs->ptr.eh_entry = qemu_add_mouse_event_handler(usb_pointer_event, us, - 0, "QEMU HID Mouse"); - } else if (hs->kind == HID_TABLET) { - hs->ptr.eh_entry = qemu_add_mouse_event_handler(usb_pointer_event, us, - 1, "QEMU HID Tablet"); - } + hid_init(&us->hid, kind, usb_hid_changed); /* Force poll routine to be run and grab input the first time. */ us->changed = 1; @@ -992,7 +1012,7 @@ static struct USBDeviceInfo hid_info[] = { .usb_desc = &desc_tablet, .init = usb_tablet_initfn, .handle_packet = usb_generic_handle_packet, - .handle_reset = usb_mouse_handle_reset, + .handle_reset = usb_hid_handle_reset, .handle_control = usb_hid_handle_control, .handle_data = usb_hid_handle_data, .handle_destroy = usb_hid_handle_destroy, @@ -1005,7 +1025,7 @@ static struct USBDeviceInfo hid_info[] = { .usb_desc = &desc_mouse, .init = usb_mouse_initfn, .handle_packet = usb_generic_handle_packet, - .handle_reset = usb_mouse_handle_reset, + .handle_reset = usb_hid_handle_reset, .handle_control = usb_hid_handle_control, .handle_data = usb_hid_handle_data, .handle_destroy = usb_hid_handle_destroy, @@ -1018,7 +1038,7 @@ static struct USBDeviceInfo hid_info[] = { .usb_desc = &desc_keyboard, .init = usb_keyboard_initfn, .handle_packet = usb_generic_handle_packet, - .handle_reset = usb_keyboard_handle_reset, + .handle_reset = usb_hid_handle_reset, .handle_control = usb_hid_handle_control, .handle_data = usb_hid_handle_data, .handle_destroy = usb_hid_handle_destroy, From 38931fa8cfb074a08ce65fd1982bd4a5bef9d6fb Mon Sep 17 00:00:00 2001 From: Gerd Hoffmann Date: Fri, 15 Jul 2011 14:46:39 +0200 Subject: [PATCH 211/230] usb-hid: add hid_has_events() Add hid_has_events function, use it to figure whenever there are pending events instead of checking and updating USBHIDState->changed. Setting ->changed to 1 on init is removed, that should have absolutely no effect as the initial state of ->idle is 0 so we report hid state anyway until the guest configures some idle time. Also should clear ->idle on reset. Signed-off-by: Gerd Hoffmann --- hw/usb-hid.c | 16 +++++++--------- 1 file changed, 7 insertions(+), 9 deletions(-) diff --git a/hw/usb-hid.c b/hw/usb-hid.c index 870cc66272..b730692e0e 100644 --- a/hw/usb-hid.c +++ b/hw/usb-hid.c @@ -88,7 +88,6 @@ typedef struct USBHIDState { int32_t protocol; uint8_t idle; int64_t next_idle_clock; - int changed; void *datain_opaque; void (*datain)(void *); } USBHIDState; @@ -444,12 +443,15 @@ static const uint8_t usb_hid_usage_keys[0x100] = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, }; +static bool hid_has_events(HIDState *hs) +{ + return hs->n > 0; +} + static void usb_hid_changed(HIDState *hs) { USBHIDState *us = container_of(hs, USBHIDState, hid); - us->changed = 1; - if (us->datain) { us->datain(us->datain_opaque); } @@ -742,6 +744,7 @@ static void usb_hid_handle_reset(USBDevice *dev) hid_handle_reset(&us->hid); us->protocol = 1; + us->idle = 0; } static void usb_hid_set_next_idle(USBHIDState *s, int64_t curtime) @@ -798,7 +801,6 @@ static int usb_hid_handle_control(USBDevice *dev, USBPacket *p, } else if (hs->kind == HID_KEYBOARD) { ret = hid_keyboard_poll(hs, data, length); } - us->changed = hs->n > 0; break; case SET_REPORT: if (hs->kind == HID_KEYBOARD) { @@ -849,7 +851,7 @@ static int usb_hid_handle_data(USBDevice *dev, USBPacket *p) case USB_TOKEN_IN: if (p->devep == 1) { int64_t curtime = qemu_get_clock_ns(vm_clock); - if (!us->changed && + if (!hid_has_events(hs) && (!us->idle || us->next_idle_clock - curtime > 0)) { return USB_RET_NAK; } @@ -860,7 +862,6 @@ static int usb_hid_handle_data(USBDevice *dev, USBPacket *p) ret = hid_keyboard_poll(hs, buf, p->iov.size); } usb_packet_copy(p, buf, ret); - us->changed = hs->n > 0; } else { goto fail; } @@ -914,9 +915,6 @@ static int usb_hid_initfn(USBDevice *dev, int kind) usb_desc_init(dev); hid_init(&us->hid, kind, usb_hid_changed); - - /* Force poll routine to be run and grab input the first time. */ - us->changed = 1; return 0; } From dcfda673101313472524bfac8c2fe2e1d03c8214 Mon Sep 17 00:00:00 2001 From: Gerd Hoffmann Date: Fri, 15 Jul 2011 15:08:01 +0200 Subject: [PATCH 212/230] usb-hid: split hid code to hw/hid.[ch] Almost pure code motion. Unstatic hid interface functions and add them to the header file. Some renames. Some code style cleanups. Signed-off-by: Gerd Hoffmann --- Makefile.objs | 1 + hw/hid.c | 395 +++++++++++++++++++++++++++++++++++++++++++++++++ hw/hid.h | 54 +++++++ hw/usb-hid.c | 397 +------------------------------------------------- 4 files changed, 452 insertions(+), 395 deletions(-) create mode 100644 hw/hid.c create mode 100644 hw/hid.h diff --git a/Makefile.objs b/Makefile.objs index 3d1a4de33c..eb5e1dcece 100644 --- a/Makefile.objs +++ b/Makefile.objs @@ -89,6 +89,7 @@ common-obj-y += i2c.o smbus.o smbus_eeprom.o common-obj-y += eeprom93xx.o common-obj-y += scsi-disk.o cdrom.o common-obj-y += scsi-generic.o scsi-bus.o +common-obj-y += hid.o common-obj-y += usb.o usb-hub.o usb-$(HOST_USB).o usb-hid.o usb-msd.o usb-wacom.o common-obj-y += usb-serial.o usb-net.o usb-bus.o usb-desc.o common-obj-$(CONFIG_SSI) += ssi.o diff --git a/hw/hid.c b/hw/hid.c new file mode 100644 index 0000000000..1893ae59f0 --- /dev/null +++ b/hw/hid.c @@ -0,0 +1,395 @@ +/* + * QEMU HID devices + * + * Copyright (c) 2005 Fabrice Bellard + * Copyright (c) 2007 OpenMoko, Inc. (andrew@openedhand.com) + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +#include "hw.h" +#include "console.h" +#include "hid.h" + +#define HID_USAGE_ERROR_ROLLOVER 0x01 +#define HID_USAGE_POSTFAIL 0x02 +#define HID_USAGE_ERROR_UNDEFINED 0x03 + +/* Indices are QEMU keycodes, values are from HID Usage Table. Indices + * above 0x80 are for keys that come after 0xe0 or 0xe1+0x1d or 0xe1+0x9d. */ +static const uint8_t hid_usage_keys[0x100] = { + 0x00, 0x29, 0x1e, 0x1f, 0x20, 0x21, 0x22, 0x23, + 0x24, 0x25, 0x26, 0x27, 0x2d, 0x2e, 0x2a, 0x2b, + 0x14, 0x1a, 0x08, 0x15, 0x17, 0x1c, 0x18, 0x0c, + 0x12, 0x13, 0x2f, 0x30, 0x28, 0xe0, 0x04, 0x16, + 0x07, 0x09, 0x0a, 0x0b, 0x0d, 0x0e, 0x0f, 0x33, + 0x34, 0x35, 0xe1, 0x31, 0x1d, 0x1b, 0x06, 0x19, + 0x05, 0x11, 0x10, 0x36, 0x37, 0x38, 0xe5, 0x55, + 0xe2, 0x2c, 0x32, 0x3a, 0x3b, 0x3c, 0x3d, 0x3e, + 0x3f, 0x40, 0x41, 0x42, 0x43, 0x53, 0x47, 0x5f, + 0x60, 0x61, 0x56, 0x5c, 0x5d, 0x5e, 0x57, 0x59, + 0x5a, 0x5b, 0x62, 0x63, 0x00, 0x00, 0x00, 0x44, + 0x45, 0x68, 0x69, 0x6a, 0x6b, 0x6c, 0x6d, 0x6e, + 0xe8, 0xe9, 0x71, 0x72, 0x73, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x85, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0xe3, 0xe7, 0x65, + + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x58, 0xe4, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x54, 0x00, 0x46, + 0xe6, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x48, 0x00, 0x4a, + 0x52, 0x4b, 0x00, 0x50, 0x00, 0x4f, 0x00, 0x4d, + 0x51, 0x4e, 0x49, 0x4c, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0xe3, 0xe7, 0x65, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +}; + +bool hid_has_events(HIDState *hs) +{ + return hs->n > 0; +} + +static void hid_pointer_event_clear(HIDPointerEvent *e, int buttons) +{ + e->xdx = e->ydy = e->dz = 0; + e->buttons_state = buttons; +} + +static void hid_pointer_event_combine(HIDPointerEvent *e, int xyrel, + int x1, int y1, int z1) { + if (xyrel) { + e->xdx += x1; + e->ydy += y1; + } else { + e->xdx = x1; + e->ydy = y1; + /* Windows drivers do not like the 0/0 position and ignore such + * events. */ + if (!(x1 | y1)) { + x1 = 1; + } + } + e->dz += z1; +} + +static void hid_pointer_event(void *opaque, + int x1, int y1, int z1, int buttons_state) +{ + HIDState *hs = opaque; + unsigned use_slot = (hs->head + hs->n - 1) & QUEUE_MASK; + unsigned previous_slot = (use_slot - 1) & QUEUE_MASK; + + /* We combine events where feasible to keep the queue small. We shouldn't + * combine anything with the first event of a particular button state, as + * that would change the location of the button state change. When the + * queue is empty, a second event is needed because we don't know if + * the first event changed the button state. */ + if (hs->n == QUEUE_LENGTH) { + /* Queue full. Discard old button state, combine motion normally. */ + hs->ptr.queue[use_slot].buttons_state = buttons_state; + } else if (hs->n < 2 || + hs->ptr.queue[use_slot].buttons_state != buttons_state || + hs->ptr.queue[previous_slot].buttons_state != + hs->ptr.queue[use_slot].buttons_state) { + /* Cannot or should not combine, so add an empty item to the queue. */ + QUEUE_INCR(use_slot); + hs->n++; + hid_pointer_event_clear(&hs->ptr.queue[use_slot], buttons_state); + } + hid_pointer_event_combine(&hs->ptr.queue[use_slot], + hs->kind == HID_MOUSE, + x1, y1, z1); + hs->event(hs); +} + +static void hid_keyboard_event(void *opaque, int keycode) +{ + HIDState *hs = opaque; + int slot; + + if (hs->n == QUEUE_LENGTH) { + fprintf(stderr, "usb-kbd: warning: key event queue full\n"); + return; + } + slot = (hs->head + hs->n) & QUEUE_MASK; hs->n++; + hs->kbd.keycodes[slot] = keycode; + hs->event(hs); +} + +static void hid_keyboard_process_keycode(HIDState *hs) +{ + uint8_t hid_code, key; + int i, keycode, slot; + + if (hs->n == 0) { + return; + } + slot = hs->head & QUEUE_MASK; QUEUE_INCR(hs->head); hs->n--; + keycode = hs->kbd.keycodes[slot]; + + key = keycode & 0x7f; + hid_code = hid_usage_keys[key | ((hs->kbd.modifiers >> 1) & (1 << 7))]; + hs->kbd.modifiers &= ~(1 << 8); + + switch (hid_code) { + case 0x00: + return; + + case 0xe0: + if (hs->kbd.modifiers & (1 << 9)) { + hs->kbd.modifiers ^= 3 << 8; + return; + } + case 0xe1 ... 0xe7: + if (keycode & (1 << 7)) { + hs->kbd.modifiers &= ~(1 << (hid_code & 0x0f)); + return; + } + case 0xe8 ... 0xef: + hs->kbd.modifiers |= 1 << (hid_code & 0x0f); + return; + } + + if (keycode & (1 << 7)) { + for (i = hs->kbd.keys - 1; i >= 0; i--) { + if (hs->kbd.key[i] == hid_code) { + hs->kbd.key[i] = hs->kbd.key[-- hs->kbd.keys]; + hs->kbd.key[hs->kbd.keys] = 0x00; + break; + } + } + if (i < 0) { + return; + } + } else { + for (i = hs->kbd.keys - 1; i >= 0; i--) { + if (hs->kbd.key[i] == hid_code) { + break; + } + } + if (i < 0) { + if (hs->kbd.keys < sizeof(hs->kbd.key)) { + hs->kbd.key[hs->kbd.keys++] = hid_code; + } + } else { + return; + } + } +} + +static inline int int_clamp(int val, int vmin, int vmax) +{ + if (val < vmin) { + return vmin; + } else if (val > vmax) { + return vmax; + } else { + return val; + } +} + +int hid_pointer_poll(HIDState *hs, uint8_t *buf, int len) +{ + int dx, dy, dz, b, l; + int index; + HIDPointerEvent *e; + + if (!hs->ptr.mouse_grabbed) { + qemu_activate_mouse_event_handler(hs->ptr.eh_entry); + hs->ptr.mouse_grabbed = 1; + } + + /* When the buffer is empty, return the last event. Relative + movements will all be zero. */ + index = (hs->n ? hs->head : hs->head - 1); + e = &hs->ptr.queue[index & QUEUE_MASK]; + + if (hs->kind == HID_MOUSE) { + dx = int_clamp(e->xdx, -127, 127); + dy = int_clamp(e->ydy, -127, 127); + e->xdx -= dx; + e->ydy -= dy; + } else { + dx = e->xdx; + dy = e->ydy; + } + dz = int_clamp(e->dz, -127, 127); + e->dz -= dz; + + b = 0; + if (e->buttons_state & MOUSE_EVENT_LBUTTON) { + b |= 0x01; + } + if (e->buttons_state & MOUSE_EVENT_RBUTTON) { + b |= 0x02; + } + if (e->buttons_state & MOUSE_EVENT_MBUTTON) { + b |= 0x04; + } + + if (hs->n && + !e->dz && + (hs->kind == HID_TABLET || (!e->xdx && !e->ydy))) { + /* that deals with this event */ + QUEUE_INCR(hs->head); + hs->n--; + } + + /* Appears we have to invert the wheel direction */ + dz = 0 - dz; + l = 0; + switch (hs->kind) { + case HID_MOUSE: + if (len > l) { + buf[l++] = b; + } + if (len > l) { + buf[l++] = dx; + } + if (len > l) { + buf[l++] = dy; + } + if (len > l) { + buf[l++] = dz; + } + break; + + case HID_TABLET: + if (len > l) { + buf[l++] = b; + } + if (len > l) { + buf[l++] = dx & 0xff; + } + if (len > l) { + buf[l++] = dx >> 8; + } + if (len > l) { + buf[l++] = dy & 0xff; + } + if (len > l) { + buf[l++] = dy >> 8; + } + if (len > l) { + buf[l++] = dz; + } + break; + + default: + abort(); + } + + return l; +} + +int hid_keyboard_poll(HIDState *hs, uint8_t *buf, int len) +{ + if (len < 2) { + return 0; + } + + hid_keyboard_process_keycode(hs); + + buf[0] = hs->kbd.modifiers & 0xff; + buf[1] = 0; + if (hs->kbd.keys > 6) { + memset(buf + 2, HID_USAGE_ERROR_ROLLOVER, MIN(8, len) - 2); + } else { + memcpy(buf + 2, hs->kbd.key, MIN(8, len) - 2); + } + + return MIN(8, len); +} + +int hid_keyboard_write(HIDState *hs, uint8_t *buf, int len) +{ + if (len > 0) { + int ledstate = 0; + /* 0x01: Num Lock LED + * 0x02: Caps Lock LED + * 0x04: Scroll Lock LED + * 0x08: Compose LED + * 0x10: Kana LED */ + hs->kbd.leds = buf[0]; + if (hs->kbd.leds & 0x04) { + ledstate |= QEMU_SCROLL_LOCK_LED; + } + if (hs->kbd.leds & 0x01) { + ledstate |= QEMU_NUM_LOCK_LED; + } + if (hs->kbd.leds & 0x02) { + ledstate |= QEMU_CAPS_LOCK_LED; + } + kbd_put_ledstate(ledstate); + } + return 0; +} + +void hid_reset(HIDState *hs) +{ + switch (hs->kind) { + case HID_KEYBOARD: + qemu_add_kbd_event_handler(hid_keyboard_event, hs); + memset(hs->kbd.keycodes, 0, sizeof(hs->kbd.keycodes)); + memset(hs->kbd.key, 0, sizeof(hs->kbd.key)); + hs->kbd.keys = 0; + break; + case HID_MOUSE: + case HID_TABLET: + memset(hs->ptr.queue, 0, sizeof(hs->ptr.queue)); + break; + } + hs->head = 0; + hs->n = 0; +} + +void hid_free(HIDState *hs) +{ + switch (hs->kind) { + case HID_KEYBOARD: + qemu_remove_kbd_event_handler(); + break; + case HID_MOUSE: + case HID_TABLET: + qemu_remove_mouse_event_handler(hs->ptr.eh_entry); + break; + } +} + +void hid_init(HIDState *hs, int kind, HIDEventFunc event) +{ + hs->kind = kind; + hs->event = event; + + if (hs->kind == HID_MOUSE) { + hs->ptr.eh_entry = qemu_add_mouse_event_handler(hid_pointer_event, hs, + 0, "QEMU HID Mouse"); + } else if (hs->kind == HID_TABLET) { + hs->ptr.eh_entry = qemu_add_mouse_event_handler(hid_pointer_event, hs, + 1, "QEMU HID Tablet"); + } +} diff --git a/hw/hid.h b/hw/hid.h new file mode 100644 index 0000000000..99910c3a86 --- /dev/null +++ b/hw/hid.h @@ -0,0 +1,54 @@ +#ifndef QEMU_HID_H +#define QEMU_HID_H + +#define HID_MOUSE 1 +#define HID_TABLET 2 +#define HID_KEYBOARD 3 + +typedef struct HIDPointerEvent { + int32_t xdx, ydy; /* relative iff it's a mouse, otherwise absolute */ + int32_t dz, buttons_state; +} HIDPointerEvent; + +#define QUEUE_LENGTH 16 /* should be enough for a triple-click */ +#define QUEUE_MASK (QUEUE_LENGTH-1u) +#define QUEUE_INCR(v) ((v)++, (v) &= QUEUE_MASK) + +typedef struct HIDState HIDState; +typedef void (*HIDEventFunc)(HIDState *s); + +typedef struct HIDMouseState { + HIDPointerEvent queue[QUEUE_LENGTH]; + int mouse_grabbed; + QEMUPutMouseEntry *eh_entry; +} HIDMouseState; + +typedef struct HIDKeyboardState { + uint32_t keycodes[QUEUE_LENGTH]; + uint16_t modifiers; + uint8_t leds; + uint8_t key[16]; + int32_t keys; +} HIDKeyboardState; + +struct HIDState { + union { + HIDMouseState ptr; + HIDKeyboardState kbd; + }; + uint32_t head; /* index into circular queue */ + uint32_t n; + int kind; + HIDEventFunc event; +}; + +void hid_init(HIDState *hs, int kind, HIDEventFunc event); +void hid_reset(HIDState *hs); +void hid_free(HIDState *hs); + +bool hid_has_events(HIDState *hs); +int hid_pointer_poll(HIDState *hs, uint8_t *buf, int len); +int hid_keyboard_poll(HIDState *hs, uint8_t *buf, int len); +int hid_keyboard_write(HIDState *hs, uint8_t *buf, int len); + +#endif /* QEMU_HID_H */ diff --git a/hw/usb-hid.c b/hw/usb-hid.c index b730692e0e..48ce743988 100644 --- a/hw/usb-hid.c +++ b/hw/usb-hid.c @@ -27,6 +27,7 @@ #include "usb.h" #include "usb-desc.h" #include "qemu-timer.h" +#include "hid.h" /* HID interface requests */ #define GET_REPORT 0xa101 @@ -41,47 +42,6 @@ #define USB_DT_REPORT 0x22 #define USB_DT_PHY 0x23 -#define HID_MOUSE 1 -#define HID_TABLET 2 -#define HID_KEYBOARD 3 - -typedef struct HIDPointerEvent { - int32_t xdx, ydy; /* relative iff it's a mouse, otherwise absolute */ - int32_t dz, buttons_state; -} HIDPointerEvent; - -#define QUEUE_LENGTH 16 /* should be enough for a triple-click */ -#define QUEUE_MASK (QUEUE_LENGTH-1u) -#define QUEUE_INCR(v) ((v)++, (v) &= QUEUE_MASK) - -typedef struct HIDState HIDState; -typedef void (*HIDEventFunc)(HIDState *s); - -typedef struct HIDMouseState { - HIDPointerEvent queue[QUEUE_LENGTH]; - int mouse_grabbed; - QEMUPutMouseEntry *eh_entry; -} HIDMouseState; - -typedef struct HIDKeyboardState { - uint32_t keycodes[QUEUE_LENGTH]; - uint16_t modifiers; - uint8_t leds; - uint8_t key[16]; - int32_t keys; -} HIDKeyboardState; - -struct HIDState { - union { - HIDMouseState ptr; - HIDKeyboardState kbd; - }; - uint32_t head; /* index into circular queue */ - uint32_t n; - int kind; - HIDEventFunc event; -}; - typedef struct USBHIDState { USBDevice dev; HIDState hid; @@ -401,53 +361,6 @@ static const uint8_t qemu_keyboard_hid_report_descriptor[] = { 0xc0, /* End Collection */ }; -#define USB_HID_USAGE_ERROR_ROLLOVER 0x01 -#define USB_HID_USAGE_POSTFAIL 0x02 -#define USB_HID_USAGE_ERROR_UNDEFINED 0x03 - -/* Indices are QEMU keycodes, values are from HID Usage Table. Indices - * above 0x80 are for keys that come after 0xe0 or 0xe1+0x1d or 0xe1+0x9d. */ -static const uint8_t usb_hid_usage_keys[0x100] = { - 0x00, 0x29, 0x1e, 0x1f, 0x20, 0x21, 0x22, 0x23, - 0x24, 0x25, 0x26, 0x27, 0x2d, 0x2e, 0x2a, 0x2b, - 0x14, 0x1a, 0x08, 0x15, 0x17, 0x1c, 0x18, 0x0c, - 0x12, 0x13, 0x2f, 0x30, 0x28, 0xe0, 0x04, 0x16, - 0x07, 0x09, 0x0a, 0x0b, 0x0d, 0x0e, 0x0f, 0x33, - 0x34, 0x35, 0xe1, 0x31, 0x1d, 0x1b, 0x06, 0x19, - 0x05, 0x11, 0x10, 0x36, 0x37, 0x38, 0xe5, 0x55, - 0xe2, 0x2c, 0x32, 0x3a, 0x3b, 0x3c, 0x3d, 0x3e, - 0x3f, 0x40, 0x41, 0x42, 0x43, 0x53, 0x47, 0x5f, - 0x60, 0x61, 0x56, 0x5c, 0x5d, 0x5e, 0x57, 0x59, - 0x5a, 0x5b, 0x62, 0x63, 0x00, 0x00, 0x00, 0x44, - 0x45, 0x68, 0x69, 0x6a, 0x6b, 0x6c, 0x6d, 0x6e, - 0xe8, 0xe9, 0x71, 0x72, 0x73, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x85, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0xe3, 0xe7, 0x65, - - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x58, 0xe4, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x54, 0x00, 0x46, - 0xe6, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x48, 0x00, 0x4a, - 0x52, 0x4b, 0x00, 0x50, 0x00, 0x4f, 0x00, 0x4d, - 0x51, 0x4e, 0x49, 0x4c, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0xe3, 0xe7, 0x65, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -}; - -static bool hid_has_events(HIDState *hs) -{ - return hs->n > 0; -} - static void usb_hid_changed(HIDState *hs) { USBHIDState *us = container_of(hs, USBHIDState, hid); @@ -459,290 +372,11 @@ static void usb_hid_changed(HIDState *hs) usb_wakeup(&us->dev); } -static void hid_pointer_event_clear(HIDPointerEvent *e, int buttons) -{ - e->xdx = e->ydy = e->dz = 0; - e->buttons_state = buttons; -} - -static void hid_pointer_event_combine(HIDPointerEvent *e, int xyrel, - int x1, int y1, int z1) { - if (xyrel) { - e->xdx += x1; - e->ydy += y1; - } else { - e->xdx = x1; - e->ydy = y1; - /* Windows drivers do not like the 0/0 position and ignore such - * events. */ - if (!(x1 | y1)) { - x1 = 1; - } - } - e->dz += z1; -} - -static void hid_pointer_event(void *opaque, - int x1, int y1, int z1, int buttons_state) -{ - HIDState *hs = opaque; - unsigned use_slot = (hs->head + hs->n - 1) & QUEUE_MASK; - unsigned previous_slot = (use_slot - 1) & QUEUE_MASK; - - /* We combine events where feasible to keep the queue small. We shouldn't - * combine anything with the first event of a particular button state, as - * that would change the location of the button state change. When the - * queue is empty, a second event is needed because we don't know if - * the first event changed the button state. */ - if (hs->n == QUEUE_LENGTH) { - /* Queue full. Discard old button state, combine motion normally. */ - hs->ptr.queue[use_slot].buttons_state = buttons_state; - } else if (hs->n < 2 || - hs->ptr.queue[use_slot].buttons_state != buttons_state || - hs->ptr.queue[previous_slot].buttons_state != - hs->ptr.queue[use_slot].buttons_state) { - /* Cannot or should not combine, so add an empty item to the queue. */ - QUEUE_INCR(use_slot); - hs->n++; - hid_pointer_event_clear(&hs->ptr.queue[use_slot], buttons_state); - } - hid_pointer_event_combine(&hs->ptr.queue[use_slot], - hs->kind == HID_MOUSE, - x1, y1, z1); - hs->event(hs); -} - -static void hid_keyboard_event(void *opaque, int keycode) -{ - HIDState *hs = opaque; - int slot; - - if (hs->n == QUEUE_LENGTH) { - fprintf(stderr, "usb-kbd: warning: key event queue full\n"); - return; - } - slot = (hs->head + hs->n) & QUEUE_MASK; hs->n++; - hs->kbd.keycodes[slot] = keycode; - hs->event(hs); -} - -static void hid_keyboard_process_keycode(HIDState *hs) -{ - uint8_t hid_code, key; - int i, keycode, slot; - - if (hs->n == 0) { - return; - } - slot = hs->head & QUEUE_MASK; QUEUE_INCR(hs->head); hs->n--; - keycode = hs->kbd.keycodes[slot]; - - key = keycode & 0x7f; - hid_code = usb_hid_usage_keys[key | ((hs->kbd.modifiers >> 1) & (1 << 7))]; - hs->kbd.modifiers &= ~(1 << 8); - - switch (hid_code) { - case 0x00: - return; - - case 0xe0: - if (hs->kbd.modifiers & (1 << 9)) { - hs->kbd.modifiers ^= 3 << 8; - return; - } - case 0xe1 ... 0xe7: - if (keycode & (1 << 7)) { - hs->kbd.modifiers &= ~(1 << (hid_code & 0x0f)); - return; - } - case 0xe8 ... 0xef: - hs->kbd.modifiers |= 1 << (hid_code & 0x0f); - return; - } - - if (keycode & (1 << 7)) { - for (i = hs->kbd.keys - 1; i >= 0; i--) { - if (hs->kbd.key[i] == hid_code) { - hs->kbd.key[i] = hs->kbd.key[-- hs->kbd.keys]; - hs->kbd.key[hs->kbd.keys] = 0x00; - break; - } - } - if (i < 0) { - return; - } - } else { - for (i = hs->kbd.keys - 1; i >= 0; i--) { - if (hs->kbd.key[i] == hid_code) { - break; - } - } - if (i < 0) { - if (hs->kbd.keys < sizeof(hs->kbd.key)) { - hs->kbd.key[hs->kbd.keys++] = hid_code; - } - } else { - return; - } - } -} - -static inline int int_clamp(int val, int vmin, int vmax) -{ - if (val < vmin) - return vmin; - else if (val > vmax) - return vmax; - else - return val; -} - -static int hid_pointer_poll(HIDState *hs, uint8_t *buf, int len) -{ - int dx, dy, dz, b, l; - int index; - HIDPointerEvent *e; - - if (!hs->ptr.mouse_grabbed) { - qemu_activate_mouse_event_handler(hs->ptr.eh_entry); - hs->ptr.mouse_grabbed = 1; - } - - /* When the buffer is empty, return the last event. Relative - movements will all be zero. */ - index = (hs->n ? hs->head : hs->head - 1); - e = &hs->ptr.queue[index & QUEUE_MASK]; - - if (hs->kind == HID_MOUSE) { - dx = int_clamp(e->xdx, -127, 127); - dy = int_clamp(e->ydy, -127, 127); - e->xdx -= dx; - e->ydy -= dy; - } else { - dx = e->xdx; - dy = e->ydy; - } - dz = int_clamp(e->dz, -127, 127); - e->dz -= dz; - - b = 0; - if (e->buttons_state & MOUSE_EVENT_LBUTTON) - b |= 0x01; - if (e->buttons_state & MOUSE_EVENT_RBUTTON) - b |= 0x02; - if (e->buttons_state & MOUSE_EVENT_MBUTTON) - b |= 0x04; - - if (hs->n && - !e->dz && - (hs->kind == HID_TABLET || (!e->xdx && !e->ydy))) { - /* that deals with this event */ - QUEUE_INCR(hs->head); - hs->n--; - } - - /* Appears we have to invert the wheel direction */ - dz = 0 - dz; - l = 0; - switch (hs->kind) { - case HID_MOUSE: - if (len > l) - buf[l++] = b; - if (len > l) - buf[l++] = dx; - if (len > l) - buf[l++] = dy; - if (len > l) - buf[l++] = dz; - break; - - case HID_TABLET: - if (len > l) - buf[l++] = b; - if (len > l) - buf[l++] = dx & 0xff; - if (len > l) - buf[l++] = dx >> 8; - if (len > l) - buf[l++] = dy & 0xff; - if (len > l) - buf[l++] = dy >> 8; - if (len > l) - buf[l++] = dz; - break; - - default: - abort(); - } - - return l; -} - -static int hid_keyboard_poll(HIDState *hs, uint8_t *buf, int len) -{ - if (len < 2) - return 0; - - hid_keyboard_process_keycode(hs); - - buf[0] = hs->kbd.modifiers & 0xff; - buf[1] = 0; - if (hs->kbd.keys > 6) { - memset(buf + 2, USB_HID_USAGE_ERROR_ROLLOVER, MIN(8, len) - 2); - } else { - memcpy(buf + 2, hs->kbd.key, MIN(8, len) - 2); - } - - return MIN(8, len); -} - -static int hid_keyboard_write(HIDState *hs, uint8_t *buf, int len) -{ - if (len > 0) { - int ledstate = 0; - /* 0x01: Num Lock LED - * 0x02: Caps Lock LED - * 0x04: Scroll Lock LED - * 0x08: Compose LED - * 0x10: Kana LED */ - hs->kbd.leds = buf[0]; - if (hs->kbd.leds & 0x04) { - ledstate |= QEMU_SCROLL_LOCK_LED; - } - if (hs->kbd.leds & 0x01) { - ledstate |= QEMU_NUM_LOCK_LED; - } - if (hs->kbd.leds & 0x02) { - ledstate |= QEMU_CAPS_LOCK_LED; - } - kbd_put_ledstate(ledstate); - } - return 0; -} - -static void hid_handle_reset(HIDState *hs) -{ - switch (hs->kind) { - case HID_KEYBOARD: - qemu_add_kbd_event_handler(hid_keyboard_event, hs); - memset(hs->kbd.keycodes, 0, sizeof(hs->kbd.keycodes)); - memset(hs->kbd.key, 0, sizeof(hs->kbd.key)); - hs->kbd.keys = 0; - break; - case HID_MOUSE: - case HID_TABLET: - memset(hs->ptr.queue, 0, sizeof(hs->ptr.queue)); - break; - } - hs->head = 0; - hs->n = 0; -} - static void usb_hid_handle_reset(USBDevice *dev) { USBHIDState *us = DO_UPCAST(USBHIDState, dev, dev); - hid_handle_reset(&us->hid); + hid_reset(&us->hid); us->protocol = 1; us->idle = 0; } @@ -875,19 +509,6 @@ static int usb_hid_handle_data(USBDevice *dev, USBPacket *p) return ret; } -static void hid_free(HIDState *hs) -{ - switch (hs->kind) { - case HID_KEYBOARD: - qemu_remove_kbd_event_handler(); - break; - case HID_MOUSE: - case HID_TABLET: - qemu_remove_mouse_event_handler(hs->ptr.eh_entry); - break; - } -} - static void usb_hid_handle_destroy(USBDevice *dev) { USBHIDState *us = DO_UPCAST(USBHIDState, dev, dev); @@ -895,20 +516,6 @@ static void usb_hid_handle_destroy(USBDevice *dev) hid_free(&us->hid); } -static void hid_init(HIDState *hs, int kind, HIDEventFunc event) -{ - hs->kind = kind; - hs->event = event; - - if (hs->kind == HID_MOUSE) { - hs->ptr.eh_entry = qemu_add_mouse_event_handler(hid_pointer_event, hs, - 0, "QEMU HID Mouse"); - } else if (hs->kind == HID_TABLET) { - hs->ptr.eh_entry = qemu_add_mouse_event_handler(hid_pointer_event, hs, - 1, "QEMU HID Tablet"); - } -} - static int usb_hid_initfn(USBDevice *dev, int kind) { USBHIDState *us = DO_UPCAST(USBHIDState, dev, dev); From b069d3488f327da97fb0da1939518a00637f01a3 Mon Sep 17 00:00:00 2001 From: Gerd Hoffmann Date: Fri, 15 Jul 2011 15:52:33 +0200 Subject: [PATCH 213/230] hid: move idle+protocol from usb-hid to hid too. Signed-off-by: Gerd Hoffmann --- hw/hid.c | 8 ++++++++ hw/hid.h | 4 ++++ hw/usb-hid.c | 36 +++++++++++++----------------------- 3 files changed, 25 insertions(+), 23 deletions(-) diff --git a/hw/hid.c b/hw/hid.c index 1893ae59f0..7b5ef5fc92 100644 --- a/hw/hid.c +++ b/hw/hid.c @@ -24,6 +24,7 @@ */ #include "hw.h" #include "console.h" +#include "qemu-timer.h" #include "hid.h" #define HID_USAGE_ERROR_ROLLOVER 0x01 @@ -73,6 +74,11 @@ bool hid_has_events(HIDState *hs) return hs->n > 0; } +void hid_set_next_idle(HIDState *hs, int64_t curtime) +{ + hs->next_idle_clock = curtime + (get_ticks_per_sec() * hs->idle * 4) / 1000; +} + static void hid_pointer_event_clear(HIDPointerEvent *e, int buttons) { e->xdx = e->ydy = e->dz = 0; @@ -365,6 +371,8 @@ void hid_reset(HIDState *hs) } hs->head = 0; hs->n = 0; + hs->protocol = 1; + hs->idle = 0; } void hid_free(HIDState *hs) diff --git a/hw/hid.h b/hw/hid.h index 99910c3a86..4a8fa5b63f 100644 --- a/hw/hid.h +++ b/hw/hid.h @@ -39,6 +39,9 @@ struct HIDState { uint32_t head; /* index into circular queue */ uint32_t n; int kind; + int32_t protocol; + uint8_t idle; + int64_t next_idle_clock; HIDEventFunc event; }; @@ -47,6 +50,7 @@ void hid_reset(HIDState *hs); void hid_free(HIDState *hs); bool hid_has_events(HIDState *hs); +void hid_set_next_idle(HIDState *hs, int64_t curtime); int hid_pointer_poll(HIDState *hs, uint8_t *buf, int len); int hid_keyboard_poll(HIDState *hs, uint8_t *buf, int len); int hid_keyboard_write(HIDState *hs, uint8_t *buf, int len); diff --git a/hw/usb-hid.c b/hw/usb-hid.c index 48ce743988..e5d57de888 100644 --- a/hw/usb-hid.c +++ b/hw/usb-hid.c @@ -45,9 +45,6 @@ typedef struct USBHIDState { USBDevice dev; HIDState hid; - int32_t protocol; - uint8_t idle; - int64_t next_idle_clock; void *datain_opaque; void (*datain)(void *); } USBHIDState; @@ -377,13 +374,6 @@ static void usb_hid_handle_reset(USBDevice *dev) USBHIDState *us = DO_UPCAST(USBHIDState, dev, dev); hid_reset(&us->hid); - us->protocol = 1; - us->idle = 0; -} - -static void usb_hid_set_next_idle(USBHIDState *s, int64_t curtime) -{ - s->next_idle_clock = curtime + (get_ticks_per_sec() * s->idle * 4) / 1000; } static int usb_hid_handle_control(USBDevice *dev, USBPacket *p, @@ -448,22 +438,22 @@ static int usb_hid_handle_control(USBDevice *dev, USBPacket *p, goto fail; } ret = 1; - data[0] = us->protocol; + data[0] = hs->protocol; break; case SET_PROTOCOL: if (hs->kind != HID_KEYBOARD && hs->kind != HID_MOUSE) { goto fail; } ret = 0; - us->protocol = value; + hs->protocol = value; break; case GET_IDLE: ret = 1; - data[0] = us->idle; + data[0] = hs->idle; break; case SET_IDLE: - us->idle = (uint8_t) (value >> 8); - usb_hid_set_next_idle(us, qemu_get_clock_ns(vm_clock)); + hs->idle = (uint8_t) (value >> 8); + hid_set_next_idle(hs, qemu_get_clock_ns(vm_clock)); ret = 0; break; default: @@ -486,10 +476,10 @@ static int usb_hid_handle_data(USBDevice *dev, USBPacket *p) if (p->devep == 1) { int64_t curtime = qemu_get_clock_ns(vm_clock); if (!hid_has_events(hs) && - (!us->idle || us->next_idle_clock - curtime > 0)) { + (!hs->idle || hs->next_idle_clock - curtime > 0)) { return USB_RET_NAK; } - usb_hid_set_next_idle(us, curtime); + hid_set_next_idle(hs, curtime); if (hs->kind == HID_MOUSE || hs->kind == HID_TABLET) { ret = hid_pointer_poll(hs, buf, p->iov.size); } else if (hs->kind == HID_KEYBOARD) { @@ -552,8 +542,8 @@ static int usb_hid_post_load(void *opaque, int version_id) { USBHIDState *s = opaque; - if (s->idle) { - usb_hid_set_next_idle(s, qemu_get_clock_ns(vm_clock)); + if (s->hid.idle) { + hid_set_next_idle(&s->hid, qemu_get_clock_ns(vm_clock)); } return 0; } @@ -581,8 +571,8 @@ static const VMStateDescription vmstate_usb_ptr = { vmstate_usb_ptr_queue, HIDPointerEvent), VMSTATE_UINT32(hid.head, USBHIDState), VMSTATE_UINT32(hid.n, USBHIDState), - VMSTATE_INT32(protocol, USBHIDState), - VMSTATE_UINT8(idle, USBHIDState), + VMSTATE_INT32(hid.protocol, USBHIDState), + VMSTATE_UINT8(hid.idle, USBHIDState), VMSTATE_END_OF_LIST() } }; @@ -601,8 +591,8 @@ static const VMStateDescription vmstate_usb_kbd = { VMSTATE_UINT8(hid.kbd.leds, USBHIDState), VMSTATE_UINT8_ARRAY(hid.kbd.key, USBHIDState, 16), VMSTATE_INT32(hid.kbd.keys, USBHIDState), - VMSTATE_INT32(protocol, USBHIDState), - VMSTATE_UINT8(idle, USBHIDState), + VMSTATE_INT32(hid.protocol, USBHIDState), + VMSTATE_UINT8(hid.idle, USBHIDState), VMSTATE_END_OF_LIST() } }; From fb8f4ceeb837a7410e018619daae6bc821060503 Mon Sep 17 00:00:00 2001 From: Gerd Hoffmann Date: Fri, 15 Jul 2011 16:14:59 +0200 Subject: [PATCH 214/230] bluetooth: kill dummy usb device, use hid code directly. Signed-off-by: Gerd Hoffmann --- hw/bt-hid.c | 62 +++++++++++++++++++---------------------------------- 1 file changed, 22 insertions(+), 40 deletions(-) diff --git a/hw/bt-hid.c b/hw/bt-hid.c index a4204f98e7..5f1afe3e89 100644 --- a/hw/bt-hid.c +++ b/hw/bt-hid.c @@ -19,7 +19,9 @@ */ #include "qemu-common.h" -#include "usb.h" +#include "qemu-timer.h" +#include "console.h" +#include "hid.h" #include "bt.h" enum hid_transaction_req { @@ -86,7 +88,7 @@ struct bt_hid_device_s { struct bt_l2cap_device_s btdev; struct bt_l2cap_conn_params_s *control; struct bt_l2cap_conn_params_s *interrupt; - USBDevice *usbdev; + HIDState hid; int proto; int connected; @@ -111,7 +113,7 @@ static void bt_hid_reset(struct bt_hid_device_s *s) bt_l2cap_device_done(&s->btdev); bt_l2cap_device_init(&s->btdev, net); - s->usbdev->info->handle_reset(s->usbdev); + hid_reset(&s->hid); s->proto = BT_HID_PROTO_REPORT; s->state = bt_state_ready; s->dataother.len = 0; @@ -124,23 +126,16 @@ static void bt_hid_reset(struct bt_hid_device_s *s) static int bt_hid_out(struct bt_hid_device_s *s) { - USBPacket p; - if (s->data_type == BT_DATA_OUTPUT) { - usb_packet_init(&p); - usb_packet_setup(&p, USB_TOKEN_OUT, 0, 1); - usb_packet_addbuf(&p, s->dataout.buffer, s->dataout.len); - s->dataout.len = s->usbdev->info->handle_data(s->usbdev, &p); - usb_packet_cleanup(&p); - - return s->dataout.len; + /* nothing */ + ; } if (s->data_type == BT_DATA_FEATURE) { /* XXX: * does this send a USB_REQ_CLEAR_FEATURE/USB_REQ_SET_FEATURE * or a SET_REPORT? */ - p.devep = 0; + ; } return -1; @@ -148,14 +143,8 @@ static int bt_hid_out(struct bt_hid_device_s *s) static int bt_hid_in(struct bt_hid_device_s *s) { - USBPacket p; - - usb_packet_init(&p); - usb_packet_setup(&p, USB_TOKEN_IN, 0, 1); - usb_packet_addbuf(&p, s->dataout.buffer, sizeof(s->datain.buffer)); - s->datain.len = s->usbdev->info->handle_data(s->usbdev, &p); - usb_packet_cleanup(&p); - + s->datain.len = hid_keyboard_poll(&s->hid, s->datain.buffer, + sizeof(s->datain.buffer)); return s->datain.len; } @@ -323,8 +312,7 @@ static void bt_hid_control_transaction(struct bt_hid_device_s *s, break; } s->proto = parameter; - s->usbdev->info->handle_control(s->usbdev, NULL, SET_PROTOCOL, s->proto, 0, 0, - NULL); + s->hid.protocol = parameter; ret = BT_HS_SUCCESSFUL; break; @@ -333,8 +321,7 @@ static void bt_hid_control_transaction(struct bt_hid_device_s *s, ret = BT_HS_ERR_INVALID_PARAMETER; break; } - s->usbdev->info->handle_control(s->usbdev, NULL, GET_IDLE, 0, 0, 1, - s->control->sdu_out(s->control, 1)); + *s->control->sdu_out(s->control, 1) = s->hid.idle; s->control->sdu_submit(s->control); break; @@ -344,11 +331,7 @@ static void bt_hid_control_transaction(struct bt_hid_device_s *s, break; } - /* We don't need to know about the Idle Rate here really, - * so just pass it on to the device. */ - ret = s->usbdev->info->handle_control(s->usbdev, NULL, - SET_IDLE, data[1], 0, 0, NULL) ? - BT_HS_SUCCESSFUL : BT_HS_ERR_INVALID_PARAMETER; + s->hid.idle = data[1]; /* XXX: Does this generate a handshake? */ break; @@ -385,9 +368,10 @@ static void bt_hid_control_sdu(void *opaque, const uint8_t *data, int len) bt_hid_control_transaction(hid, data, len); } -static void bt_hid_datain(void *opaque) +static void bt_hid_datain(HIDState *hs) { - struct bt_hid_device_s *hid = opaque; + struct bt_hid_device_s *hid = + container_of(hs, struct bt_hid_device_s, hid); /* If suspended, wake-up and send a wake-up event first. We might * want to also inspect the input report and ignore event like @@ -450,7 +434,7 @@ static void bt_hid_connected_update(struct bt_hid_device_s *hid) hid->btdev.device.inquiry_scan = !hid->connected; if (hid->connected && !prev) { - hid->usbdev->info->handle_reset(hid->usbdev); + hid_reset(&hid->hid); hid->proto = BT_HID_PROTO_REPORT; } @@ -518,7 +502,7 @@ static void bt_hid_destroy(struct bt_device_s *dev) bt_hid_send_control(hid, BT_HC_VIRTUAL_CABLE_UNPLUG); bt_l2cap_device_done(&hid->btdev); - hid->usbdev->info->handle_destroy(hid->usbdev); + hid_free(&hid->hid); qemu_free(hid); } @@ -531,7 +515,7 @@ enum peripheral_minor_class { }; static struct bt_device_s *bt_hid_init(struct bt_scatternet_s *net, - USBDevice *dev, enum peripheral_minor_class minor) + enum peripheral_minor_class minor) { struct bt_hid_device_s *s = qemu_mallocz(sizeof(*s)); uint32_t class = @@ -551,9 +535,8 @@ static struct bt_device_s *bt_hid_init(struct bt_scatternet_s *net, bt_l2cap_psm_register(&s->btdev, BT_PSM_HID_INTR, BT_HID_MTU, bt_hid_new_interrupt_ch); - s->usbdev = dev; - s->btdev.device.lmp_name = s->usbdev->product_desc; - usb_hid_datain_cb(s->usbdev, s, bt_hid_datain); + hid_init(&s->hid, HID_KEYBOARD, bt_hid_datain); + s->btdev.device.lmp_name = "BT Keyboard"; s->btdev.device.handle_destroy = bt_hid_destroy; @@ -566,6 +549,5 @@ static struct bt_device_s *bt_hid_init(struct bt_scatternet_s *net, struct bt_device_s *bt_keyboard_init(struct bt_scatternet_s *net) { - USBDevice *dev = usb_create_simple(NULL /* FIXME */, "usb-kbd"); - return bt_hid_init(net, dev, class_keyboard); + return bt_hid_init(net, class_keyboard); } From 0a2a30d5add5100d0c6cd4b466b227f7d76d19cf Mon Sep 17 00:00:00 2001 From: Amit Shah Date: Wed, 20 Jul 2011 13:08:46 +0530 Subject: [PATCH 215/230] balloon: Make functions, local vars static balloon.h had function declarations for a couple of functions that are local to balloon.c. Make them static. Drop the 'qemu_' prefix for balloon.c-local variables, and make them static. Signed-off-by: Amit Shah Reviewed-by: Markus Armbruster --- balloon.c | 22 +++++++++++----------- balloon.h | 4 ---- 2 files changed, 11 insertions(+), 15 deletions(-) diff --git a/balloon.c b/balloon.c index 248c1b50a9..f9bcf07b19 100644 --- a/balloon.c +++ b/balloon.c @@ -31,30 +31,30 @@ #include "trace.h" -static QEMUBalloonEvent *qemu_balloon_event; -void *qemu_balloon_event_opaque; +static QEMUBalloonEvent *balloon_event_fn; +static void *balloon_opaque; void qemu_add_balloon_handler(QEMUBalloonEvent *func, void *opaque) { - qemu_balloon_event = func; - qemu_balloon_event_opaque = opaque; + balloon_event_fn = func; + balloon_opaque = opaque; } -int qemu_balloon(ram_addr_t target, MonitorCompletion cb, void *opaque) +static int qemu_balloon(ram_addr_t target, MonitorCompletion cb, void *opaque) { - if (qemu_balloon_event) { - trace_balloon_event(qemu_balloon_event_opaque, target); - qemu_balloon_event(qemu_balloon_event_opaque, target, cb, opaque); + if (balloon_event_fn) { + trace_balloon_event(balloon_opaque, target); + balloon_event_fn(balloon_opaque, target, cb, opaque); return 1; } else { return 0; } } -int qemu_balloon_status(MonitorCompletion cb, void *opaque) +static int qemu_balloon_status(MonitorCompletion cb, void *opaque) { - if (qemu_balloon_event) { - qemu_balloon_event(qemu_balloon_event_opaque, 0, cb, opaque); + if (balloon_event_fn) { + balloon_event_fn(balloon_opaque, 0, cb, opaque); return 1; } else { return 0; diff --git a/balloon.h b/balloon.h index d478e28475..06a8a46b50 100644 --- a/balloon.h +++ b/balloon.h @@ -21,10 +21,6 @@ typedef void (QEMUBalloonEvent)(void *opaque, ram_addr_t target, void qemu_add_balloon_handler(QEMUBalloonEvent *func, void *opaque); -int qemu_balloon(ram_addr_t target, MonitorCompletion cb, void *opaque); - -int qemu_balloon_status(MonitorCompletion cb, void *opaque); - void monitor_print_balloon(Monitor *mon, const QObject *data); int do_info_balloon(Monitor *mon, MonitorCompletion cb, void *opaque); int do_balloon(Monitor *mon, const QDict *params, From b80bc1ddb2e5838f8bc86f7cc8a45d16c8d8dcba Mon Sep 17 00:00:00 2001 From: Amit Shah Date: Wed, 20 Jul 2011 13:12:15 +0530 Subject: [PATCH 216/230] balloon: Add braces around if statements Signed-off-by: Amit Shah Reviewed-by: Markus Armbruster --- balloon.c | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/balloon.c b/balloon.c index f9bcf07b19..86f629e77a 100644 --- a/balloon.c +++ b/balloon.c @@ -65,9 +65,10 @@ static void print_balloon_stat(const char *key, QObject *obj, void *opaque) { Monitor *mon = opaque; - if (strcmp(key, "actual")) + if (strcmp(key, "actual")) { monitor_printf(mon, ",%s=%" PRId64, key, qint_get_int(qobject_to_qint(obj))); + } } void monitor_print_balloon(Monitor *mon, const QObject *data) @@ -75,9 +76,9 @@ void monitor_print_balloon(Monitor *mon, const QObject *data) QDict *qdict; qdict = qobject_to_qdict(data); - if (!qdict_haskey(qdict, "actual")) + if (!qdict_haskey(qdict, "actual")) { return; - + } monitor_printf(mon, "balloon: actual=%" PRId64, qdict_get_int(qdict, "actual") >> 20); qdict_iter(qdict, print_balloon_stat, mon); From 182b9203f8f17198b1f818c23d80a2c698f29fa5 Mon Sep 17 00:00:00 2001 From: Amit Shah Date: Wed, 20 Jul 2011 13:14:12 +0530 Subject: [PATCH 217/230] balloon: Simplify code flow Replace: if (foo) { ... } else { return 0; } by if (!foo) { return 0; } ... Signed-off-by: Amit Shah Reviewed-by: Markus Armbruster --- balloon.c | 16 +++++++--------- 1 file changed, 7 insertions(+), 9 deletions(-) diff --git a/balloon.c b/balloon.c index 86f629e77a..d40be39b3b 100644 --- a/balloon.c +++ b/balloon.c @@ -42,23 +42,21 @@ void qemu_add_balloon_handler(QEMUBalloonEvent *func, void *opaque) static int qemu_balloon(ram_addr_t target, MonitorCompletion cb, void *opaque) { - if (balloon_event_fn) { - trace_balloon_event(balloon_opaque, target); - balloon_event_fn(balloon_opaque, target, cb, opaque); - return 1; - } else { + if (!balloon_event_fn) { return 0; } + trace_balloon_event(balloon_opaque, target); + balloon_event_fn(balloon_opaque, target, cb, opaque); + return 1; } static int qemu_balloon_status(MonitorCompletion cb, void *opaque) { - if (balloon_event_fn) { - balloon_event_fn(balloon_opaque, 0, cb, opaque); - return 1; - } else { + if (!balloon_event_fn) { return 0; } + balloon_event_fn(balloon_opaque, 0, cb, opaque); + return 1; } static void print_balloon_stat(const char *key, QObject *obj, void *opaque) From dce911c753489609238f91d29bcf945c87a19911 Mon Sep 17 00:00:00 2001 From: Amit Shah Date: Wed, 20 Jul 2011 13:19:07 +0530 Subject: [PATCH 218/230] virtio-balloon: Separate status handling into separate function Separate out the code to retrieve balloon info from the code that sets balloon values. This will be used to separate the two callbacks from balloon.c and help cope with 'balloon 0' on the monitor. Currently, 'balloon 0' causes a segfault in monitor_resume(). Signed-off-by: Amit Shah Reviewed-by: Markus Armbruster --- hw/virtio-balloon.c | 51 +++++++++++++++++++++++++++------------------ 1 file changed, 31 insertions(+), 20 deletions(-) diff --git a/hw/virtio-balloon.c b/hw/virtio-balloon.c index 70a8710343..2f371f2f69 100644 --- a/hw/virtio-balloon.c +++ b/hw/virtio-balloon.c @@ -199,36 +199,47 @@ static uint32_t virtio_balloon_get_features(VirtIODevice *vdev, uint32_t f) return f; } +static void virtio_balloon_stat(void *opaque, MonitorCompletion cb, + void *cb_data) +{ + VirtIOBalloon *dev = opaque; + + /* For now, only allow one request at a time. This restriction can be + * removed later by queueing callback and data pairs. + */ + if (dev->stats_callback != NULL) { + return; + } + dev->stats_callback = cb; + dev->stats_opaque_callback_data = cb_data; + + if (ENABLE_GUEST_STATS + && (dev->vdev.guest_features & (1 << VIRTIO_BALLOON_F_STATS_VQ))) { + virtqueue_push(dev->svq, &dev->stats_vq_elem, dev->stats_vq_offset); + virtio_notify(&dev->vdev, dev->svq); + return; + } + + /* Stats are not supported. Clear out any stale values that might + * have been set by a more featureful guest kernel. + */ + reset_stats(dev); + complete_stats_request(dev); +} + static void virtio_balloon_to_target(void *opaque, ram_addr_t target, MonitorCompletion cb, void *cb_data) { VirtIOBalloon *dev = opaque; - if (target > ram_size) + if (target > ram_size) { target = ram_size; - + } if (target) { dev->num_pages = (ram_size - target) >> VIRTIO_BALLOON_PFN_SHIFT; virtio_notify_config(&dev->vdev); } else { - /* For now, only allow one request at a time. This restriction can be - * removed later by queueing callback and data pairs. - */ - if (dev->stats_callback != NULL) { - return; - } - dev->stats_callback = cb; - dev->stats_opaque_callback_data = cb_data; - if (ENABLE_GUEST_STATS && (dev->vdev.guest_features & (1 << VIRTIO_BALLOON_F_STATS_VQ))) { - virtqueue_push(dev->svq, &dev->stats_vq_elem, dev->stats_vq_offset); - virtio_notify(&dev->vdev, dev->svq); - } else { - /* Stats are not supported. Clear out any stale values that might - * have been set by a more featureful guest kernel. - */ - reset_stats(dev); - complete_stats_request(dev); - } + virtio_balloon_stat(opaque, cb, cb_data); } } From 30fb2ca603e8b8d0f02630ef18bc0d0637a88ffa Mon Sep 17 00:00:00 2001 From: Amit Shah Date: Wed, 20 Jul 2011 13:30:56 +0530 Subject: [PATCH 219/230] balloon: Separate out stat and balloon handling Passing on '0' as ballooning target to indicate retrieval of stats is bad API. It also makes 'balloon 0' in the monitor cause a segfault. Have two different functions handle the different functionality instead. Detailed explanation from Markus's review: 1. do_info_balloon() is an info_async() method. It receives a callback with argument, to be called exactly once (callback frees the argument). It passes the callback via qemu_balloon_status() and indirectly through qemu_balloon_event to virtio_balloon_to_target(). virtio_balloon_to_target() executes its balloon stats half. It stores the callback in the device state. If it can't send a stats request, it resets stats and calls the callback right away. Else, it sends a stats request. The device model runs the callback when it receives the answer. Works. 2. do_balloon() is a cmd_async() method. It receives a callback with argument, to be called when the command completes. do_balloon() calls it right before it succeeds. Odd, but should work. Nevertheless, it passes the callback on via qemu_ballon() and indirectly through qemu_balloon_event to virtio_balloon_to_target(). a. If the argument is non-zero, virtio_balloon_to_target() executes its balloon half, which doesn't use the callback in any way. Odd, but works. b. If the argument is zero, virtio_balloon_to_target() executes its balloon stats half, just like in 1. It either calls the callback right away, or arranges for it to be called later. Thus, the callback runs twice: use after free and double free. Test case: start with -S -device virtio-balloon, execute "balloon 0" in human monitor. Runs the callback first from virtio_balloon_to_target(), then again from do_balloon(). Reported-by: Mike Cao Signed-off-by: Amit Shah Reviewed-by: Markus Armbruster --- balloon.c | 17 ++++++++++------- balloon.h | 8 +++++--- hw/virtio-balloon.c | 7 ++----- 3 files changed, 17 insertions(+), 15 deletions(-) diff --git a/balloon.c b/balloon.c index d40be39b3b..8be3812ee2 100644 --- a/balloon.c +++ b/balloon.c @@ -32,30 +32,33 @@ static QEMUBalloonEvent *balloon_event_fn; +static QEMUBalloonStatus *balloon_stat_fn; static void *balloon_opaque; -void qemu_add_balloon_handler(QEMUBalloonEvent *func, void *opaque) +void qemu_add_balloon_handler(QEMUBalloonEvent *event_func, + QEMUBalloonStatus *stat_func, void *opaque) { - balloon_event_fn = func; + balloon_event_fn = event_func; + balloon_stat_fn = stat_func; balloon_opaque = opaque; } -static int qemu_balloon(ram_addr_t target, MonitorCompletion cb, void *opaque) +static int qemu_balloon(ram_addr_t target) { if (!balloon_event_fn) { return 0; } trace_balloon_event(balloon_opaque, target); - balloon_event_fn(balloon_opaque, target, cb, opaque); + balloon_event_fn(balloon_opaque, target); return 1; } static int qemu_balloon_status(MonitorCompletion cb, void *opaque) { - if (!balloon_event_fn) { + if (!balloon_stat_fn) { return 0; } - balloon_event_fn(balloon_opaque, 0, cb, opaque); + balloon_stat_fn(balloon_opaque, cb, opaque); return 1; } @@ -135,7 +138,7 @@ int do_balloon(Monitor *mon, const QDict *params, return -1; } - ret = qemu_balloon(qdict_get_int(params, "value"), cb, opaque); + ret = qemu_balloon(qdict_get_int(params, "value")); if (ret == 0) { qerror_report(QERR_DEVICE_NOT_ACTIVE, "balloon"); return -1; diff --git a/balloon.h b/balloon.h index 06a8a46b50..a6c31d587c 100644 --- a/balloon.h +++ b/balloon.h @@ -16,10 +16,12 @@ #include "monitor.h" -typedef void (QEMUBalloonEvent)(void *opaque, ram_addr_t target, - MonitorCompletion cb, void *cb_data); +typedef void (QEMUBalloonEvent)(void *opaque, ram_addr_t target); +typedef void (QEMUBalloonStatus)(void *opaque, MonitorCompletion cb, + void *cb_data); -void qemu_add_balloon_handler(QEMUBalloonEvent *func, void *opaque); +void qemu_add_balloon_handler(QEMUBalloonEvent *event_func, + QEMUBalloonStatus *stat_func, void *opaque); void monitor_print_balloon(Monitor *mon, const QObject *data); int do_info_balloon(Monitor *mon, MonitorCompletion cb, void *opaque); diff --git a/hw/virtio-balloon.c b/hw/virtio-balloon.c index 2f371f2f69..40b43b0606 100644 --- a/hw/virtio-balloon.c +++ b/hw/virtio-balloon.c @@ -227,8 +227,7 @@ static void virtio_balloon_stat(void *opaque, MonitorCompletion cb, complete_stats_request(dev); } -static void virtio_balloon_to_target(void *opaque, ram_addr_t target, - MonitorCompletion cb, void *cb_data) +static void virtio_balloon_to_target(void *opaque, ram_addr_t target) { VirtIOBalloon *dev = opaque; @@ -238,8 +237,6 @@ static void virtio_balloon_to_target(void *opaque, ram_addr_t target, if (target) { dev->num_pages = (ram_size - target) >> VIRTIO_BALLOON_PFN_SHIFT; virtio_notify_config(&dev->vdev); - } else { - virtio_balloon_stat(opaque, cb, cb_data); } } @@ -284,7 +281,7 @@ VirtIODevice *virtio_balloon_init(DeviceState *dev) s->svq = virtio_add_queue(&s->vdev, 128, virtio_balloon_receive_stats); reset_stats(s); - qemu_add_balloon_handler(virtio_balloon_to_target, s); + qemu_add_balloon_handler(virtio_balloon_to_target, virtio_balloon_stat, s); register_savevm(dev, "virtio-balloon", -1, 1, virtio_balloon_save, virtio_balloon_load, s); From 73428a8ed53b7c7b1a52141e0ab71c50a42ce931 Mon Sep 17 00:00:00 2001 From: Amit Shah Date: Wed, 20 Jul 2011 13:35:30 +0530 Subject: [PATCH 220/230] balloon: Fix header comment; add Copyright Signed-off-by: Amit Shah --- balloon.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/balloon.c b/balloon.c index 8be3812ee2..a938475270 100644 --- a/balloon.c +++ b/balloon.c @@ -1,7 +1,9 @@ /* - * QEMU System Emulator + * Generic Balloon handlers and management * * Copyright (c) 2003-2008 Fabrice Bellard + * Copyright (C) 2011 Red Hat, Inc. + * Copyright (C) 2011 Amit Shah * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal @@ -30,7 +32,6 @@ #include "balloon.h" #include "trace.h" - static QEMUBalloonEvent *balloon_event_fn; static QEMUBalloonStatus *balloon_stat_fn; static void *balloon_opaque; From d4443cb616a62619b3b133c44094c3b056ecd3b0 Mon Sep 17 00:00:00 2001 From: Amit Shah Date: Wed, 20 Jul 2011 13:37:01 +0530 Subject: [PATCH 221/230] virtio-balloon: Fix header comment; add Copyright Signed-off-by: Amit Shah --- hw/virtio-balloon.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/hw/virtio-balloon.c b/hw/virtio-balloon.c index 40b43b0606..2ba7e95607 100644 --- a/hw/virtio-balloon.c +++ b/hw/virtio-balloon.c @@ -1,7 +1,9 @@ /* - * Virtio Block Device + * Virtio Balloon Device * * Copyright IBM, Corp. 2008 + * Copyright (C) 2011 Red Hat, Inc. + * Copyright (C) 2011 Amit Shah * * Authors: * Anthony Liguori From 6c6ec1821a2631b21e680051e2dedaa1be5b83dc Mon Sep 17 00:00:00 2001 From: Amit Shah Date: Wed, 27 Jul 2011 12:28:19 +0530 Subject: [PATCH 222/230] balloon: Don't allow multiple balloon handler registrations Multiple balloon devices don't make sense; disallow more than one registration attempt to register handlers. Signed-off-by: Amit Shah Reviewed-by: Markus Armbruster Acked-by: Michael S. Tsirkin --- balloon.c | 12 ++++++++++-- balloon.h | 4 ++-- 2 files changed, 12 insertions(+), 4 deletions(-) diff --git a/balloon.c b/balloon.c index a938475270..5200565cb2 100644 --- a/balloon.c +++ b/balloon.c @@ -36,12 +36,20 @@ static QEMUBalloonEvent *balloon_event_fn; static QEMUBalloonStatus *balloon_stat_fn; static void *balloon_opaque; -void qemu_add_balloon_handler(QEMUBalloonEvent *event_func, - QEMUBalloonStatus *stat_func, void *opaque) +int qemu_add_balloon_handler(QEMUBalloonEvent *event_func, + QEMUBalloonStatus *stat_func, void *opaque) { + if (balloon_event_fn || balloon_stat_fn || balloon_opaque) { + /* We're already registered one balloon handler. How many can + * a guest really have? + */ + error_report("Another balloon device already registered"); + return -1; + } balloon_event_fn = event_func; balloon_stat_fn = stat_func; balloon_opaque = opaque; + return 0; } static int qemu_balloon(ram_addr_t target) diff --git a/balloon.h b/balloon.h index a6c31d587c..3df14e645a 100644 --- a/balloon.h +++ b/balloon.h @@ -20,8 +20,8 @@ typedef void (QEMUBalloonEvent)(void *opaque, ram_addr_t target); typedef void (QEMUBalloonStatus)(void *opaque, MonitorCompletion cb, void *cb_data); -void qemu_add_balloon_handler(QEMUBalloonEvent *event_func, - QEMUBalloonStatus *stat_func, void *opaque); +int qemu_add_balloon_handler(QEMUBalloonEvent *event_func, + QEMUBalloonStatus *stat_func, void *opaque); void monitor_print_balloon(Monitor *mon, const QObject *data); int do_info_balloon(Monitor *mon, MonitorCompletion cb, void *opaque); From f76f665547f4a954a2c83552a88816fc2a316be0 Mon Sep 17 00:00:00 2001 From: Amit Shah Date: Wed, 27 Jul 2011 12:29:33 +0530 Subject: [PATCH 223/230] virtio-balloon: Check if balloon registration failed Multiple balloon registrations are not allowed; check if the registration with the qemu balloon api succeeded. If not, fail the device init. Signed-off-by: Amit Shah Reviewed-by: Markus Armbruster Acked-by: Michael S. Tsirkin --- hw/virtio-balloon.c | 9 ++++++++- hw/virtio-pci.c | 3 +++ 2 files changed, 11 insertions(+), 1 deletion(-) diff --git a/hw/virtio-balloon.c b/hw/virtio-balloon.c index 2ba7e95607..26ee36472b 100644 --- a/hw/virtio-balloon.c +++ b/hw/virtio-balloon.c @@ -269,6 +269,7 @@ static int virtio_balloon_load(QEMUFile *f, void *opaque, int version_id) VirtIODevice *virtio_balloon_init(DeviceState *dev) { VirtIOBalloon *s; + int ret; s = (VirtIOBalloon *)virtio_common_init("virtio-balloon", VIRTIO_ID_BALLOON, @@ -278,12 +279,18 @@ VirtIODevice *virtio_balloon_init(DeviceState *dev) s->vdev.set_config = virtio_balloon_set_config; s->vdev.get_features = virtio_balloon_get_features; + ret = qemu_add_balloon_handler(virtio_balloon_to_target, + virtio_balloon_stat, s); + if (ret < 0) { + virtio_cleanup(&s->vdev); + return NULL; + } + s->ivq = virtio_add_queue(&s->vdev, 128, virtio_balloon_handle_output); s->dvq = virtio_add_queue(&s->vdev, 128, virtio_balloon_handle_output); s->svq = virtio_add_queue(&s->vdev, 128, virtio_balloon_receive_stats); reset_stats(s); - qemu_add_balloon_handler(virtio_balloon_to_target, virtio_balloon_stat, s); register_savevm(dev, "virtio-balloon", -1, 1, virtio_balloon_save, virtio_balloon_load, s); diff --git a/hw/virtio-pci.c b/hw/virtio-pci.c index d685243728..ca5f125dae 100644 --- a/hw/virtio-pci.c +++ b/hw/virtio-pci.c @@ -788,6 +788,9 @@ static int virtio_balloon_init_pci(PCIDevice *pci_dev) VirtIODevice *vdev; vdev = virtio_balloon_init(&pci_dev->qdev); + if (!vdev) { + return -1; + } virtio_init_pci(proxy, vdev); return 0; } From 514e73ecebc0aeadef218e91e36ee42b3d145c93 Mon Sep 17 00:00:00 2001 From: Amit Shah Date: Wed, 27 Jul 2011 16:50:54 +0530 Subject: [PATCH 224/230] balloon: Reject negative balloon values Negative balloon values don't make sense, reject them and throw a qerror with QERR_INVALID_PARAMETER_VALUE. Reported-by: Mike Cao Signed-off-by: Amit Shah Reviewed-by: Markus Armbruster Acked-by: Michael S. Tsirkin --- balloon.c | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/balloon.c b/balloon.c index 5200565cb2..f56fdc1c4b 100644 --- a/balloon.c +++ b/balloon.c @@ -140,6 +140,7 @@ int do_info_balloon(Monitor *mon, MonitorCompletion cb, void *opaque) int do_balloon(Monitor *mon, const QDict *params, MonitorCompletion cb, void *opaque) { + int64_t target; int ret; if (kvm_enabled() && !kvm_has_sync_mmu()) { @@ -147,7 +148,12 @@ int do_balloon(Monitor *mon, const QDict *params, return -1; } - ret = qemu_balloon(qdict_get_int(params, "value")); + target = qdict_get_int(params, "value"); + if (target <= 0) { + qerror_report(QERR_INVALID_PARAMETER_VALUE, "target", "a size"); + return -1; + } + ret = qemu_balloon(target); if (ret == 0) { qerror_report(QERR_DEVICE_NOT_ACTIVE, "balloon"); return -1; From 855d7e259fe2a804f08698ca5c97d6b07fa79da1 Mon Sep 17 00:00:00 2001 From: Amit Shah Date: Wed, 27 Jul 2011 13:50:41 +0530 Subject: [PATCH 225/230] virtio-balloon: Add exit handler, fix memleaks Add an exit handler that will free up RAM after a virtio-balloon device is unplugged. Signed-off-by: Amit Shah Reviewed-by: Markus Armbruster Acked-by: Michael S. Tsirkin --- hw/virtio-balloon.c | 5 +++++ hw/virtio-pci.c | 11 ++++++++++- hw/virtio.h | 1 + 3 files changed, 16 insertions(+), 1 deletion(-) diff --git a/hw/virtio-balloon.c b/hw/virtio-balloon.c index 26ee36472b..0ce00495e2 100644 --- a/hw/virtio-balloon.c +++ b/hw/virtio-balloon.c @@ -297,3 +297,8 @@ VirtIODevice *virtio_balloon_init(DeviceState *dev) return &s->vdev; } + +void virtio_balloon_exit(VirtIODevice *vdev) +{ + virtio_cleanup(vdev); +} diff --git a/hw/virtio-pci.c b/hw/virtio-pci.c index ca5f125dae..316bf92db0 100644 --- a/hw/virtio-pci.c +++ b/hw/virtio-pci.c @@ -795,6 +795,15 @@ static int virtio_balloon_init_pci(PCIDevice *pci_dev) return 0; } +static int virtio_balloon_exit_pci(PCIDevice *pci_dev) +{ + VirtIOPCIProxy *proxy = DO_UPCAST(VirtIOPCIProxy, pci_dev, pci_dev); + + virtio_pci_stop_ioeventfd(proxy); + virtio_balloon_exit(proxy->vdev); + return virtio_exit_pci(pci_dev); +} + static PCIDeviceInfo virtio_info[] = { { .qdev.name = "virtio-blk-pci", @@ -869,7 +878,7 @@ static PCIDeviceInfo virtio_info[] = { .qdev.alias = "virtio-balloon", .qdev.size = sizeof(VirtIOPCIProxy), .init = virtio_balloon_init_pci, - .exit = virtio_exit_pci, + .exit = virtio_balloon_exit_pci, .vendor_id = PCI_VENDOR_ID_REDHAT_QUMRANET, .device_id = PCI_DEVICE_ID_VIRTIO_BALLOON, .revision = VIRTIO_PCI_ABI_VERSION, diff --git a/hw/virtio.h b/hw/virtio.h index 0fd0bb0ac5..c1292647fe 100644 --- a/hw/virtio.h +++ b/hw/virtio.h @@ -213,6 +213,7 @@ VirtIODevice *virtio_9p_init(DeviceState *dev, V9fsConf *conf); void virtio_net_exit(VirtIODevice *vdev); void virtio_blk_exit(VirtIODevice *vdev); void virtio_serial_exit(VirtIODevice *vdev); +void virtio_balloon_exit(VirtIODevice *vdev); #define DEFINE_VIRTIO_COMMON_FEATURES(_state, _field) \ DEFINE_PROP_BIT("indirect_desc", _state, _field, \ From ac720400e1730ff910d42442e4393044e7c132e0 Mon Sep 17 00:00:00 2001 From: Amit Shah Date: Thu, 28 Jul 2011 11:36:26 +0530 Subject: [PATCH 226/230] virtio-balloon: Unregister savevm section on device unplug Migrating after unplugging a virtio-balloon device resulted in an error message on the destination: Unknown savevm section or instance '0000:00:04.0/virtio-balloon' 0 load of migration failed Fix this by unregistering the section on device unplug. Signed-off-by: Amit Shah Reviewed-by: Markus Armbruster Acked-by: Michael S. Tsirkin --- hw/virtio-balloon.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/hw/virtio-balloon.c b/hw/virtio-balloon.c index 0ce00495e2..072a88a382 100644 --- a/hw/virtio-balloon.c +++ b/hw/virtio-balloon.c @@ -45,6 +45,7 @@ typedef struct VirtIOBalloon size_t stats_vq_offset; MonitorCompletion *stats_callback; void *stats_opaque_callback_data; + DeviceState *qdev; } VirtIOBalloon; static VirtIOBalloon *to_virtio_balloon(VirtIODevice *vdev) @@ -292,6 +293,7 @@ VirtIODevice *virtio_balloon_init(DeviceState *dev) reset_stats(s); + s->qdev = dev; register_savevm(dev, "virtio-balloon", -1, 1, virtio_balloon_save, virtio_balloon_load, s); @@ -300,5 +302,7 @@ VirtIODevice *virtio_balloon_init(DeviceState *dev) void virtio_balloon_exit(VirtIODevice *vdev) { + VirtIOBalloon *s = DO_UPCAST(VirtIOBalloon, vdev, vdev); + unregister_savevm(s->qdev, "virtio-balloon", s); virtio_cleanup(vdev); } From 1ba16968ab1920e65303d814ba65793b0a83e93e Mon Sep 17 00:00:00 2001 From: Stefan Weil Date: Fri, 29 Jul 2011 22:40:45 +0200 Subject: [PATCH 227/230] configure: Fix bad shell expression for non-Linux hosts With vhost_net="" (most non-Linux hosts), configure prints an error message: test: 2551: =: unexpected operator Fix this and similar code by adding the missing "". Cc: Wolfgang Mauerer Cc: Stefan Hajnoczi Reviewed-by: Stefan Hajnoczi Signed-off-by: Stefan Weil Signed-off-by: Anthony Liguori --- configure | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/configure b/configure index 77194cf9a7..8546573196 100755 --- a/configure +++ b/configure @@ -2521,7 +2521,7 @@ fi # __sync_fetch_and_and requires at least -march=i486. Many toolchains # use i686 as default anyway, but for those that don't, an explicit # specification is necessary -if test $vhost_net = "yes" && test $cpu = "i386"; then +if test "$vhost_net" = "yes" && test "$cpu" = "i386"; then cat > $TMPC << EOF int sfaa(unsigned *ptr) { @@ -2700,7 +2700,7 @@ echo "nss used $smartcard_nss" echo "usb net redir $usb_redir" echo "OpenGL support $opengl" -if test $sdl_too_old = "yes"; then +if test "$sdl_too_old" = "yes"; then echo "-> Your SDL version is too old - please upgrade to have SDL support" fi @@ -2788,7 +2788,7 @@ fi if test "$static" = "yes" ; then echo "CONFIG_STATIC=y" >> $config_host_mak fi -if test $profiler = "yes" ; then +if test "$profiler" = "yes" ; then echo "CONFIG_PROFILER=y" >> $config_host_mak fi if test "$slirp" = "yes" ; then @@ -3342,7 +3342,7 @@ case "$target_arch2" in \( "$target_arch2" = "x86_64" -a "$cpu" = "i386" \) -o \ \( "$target_arch2" = "i386" -a "$cpu" = "x86_64" \) \) ; then echo "CONFIG_KVM=y" >> $config_target_mak - if test $vhost_net = "yes" ; then + if test "$vhost_net" = "yes" ; then echo "CONFIG_VHOST_NET=y" >> $config_target_mak fi fi From 46f08792bb4a69ab8aab897c174d82b006026140 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Sun, 31 Jul 2011 16:47:20 -0700 Subject: [PATCH 228/230] alpha-softmmu: Disable for the 0.15 release branch. The system emulation code was not merged before the branch. Let's leave that work for the next release. Signed-off-by: Richard Henderson Signed-off-by: Anthony Liguori --- configure | 1 - 1 file changed, 1 deletion(-) diff --git a/configure b/configure index 8546573196..fc60888843 100755 --- a/configure +++ b/configure @@ -846,7 +846,6 @@ if [ "$softmmu" = "yes" ] ; then default_target_list="\ i386-softmmu \ x86_64-softmmu \ -alpha-softmmu \ arm-softmmu \ cris-softmmu \ lm32-softmmu \ From 9af8025e24185172a8857ed9b32d1d0ccd6aa79b Mon Sep 17 00:00:00 2001 From: Brad Date: Sat, 30 Jul 2011 01:45:55 -0400 Subject: [PATCH 229/230] Add support for finding libpng via pkg-config. Signed-off-by: Brad Smith Signed-off-by: Anthony Liguori --- configure | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/configure b/configure index fc60888843..269bf43e13 100755 --- a/configure +++ b/configure @@ -1512,11 +1512,17 @@ int main(void) { return 0; } EOF + if $pkg_config libpng --modversion >/dev/null 2>&1; then + vnc_png_cflags=`$pkg_config libpng --cflags 2> /dev/null` + vnc_png_libs=`$pkg_config libpng --libs 2> /dev/null` + else vnc_png_cflags="" vnc_png_libs="-lpng" + fi if compile_prog "$vnc_png_cflags" "$vnc_png_libs" ; then vnc_png=yes libs_softmmu="$vnc_png_libs $libs_softmmu" + QEMU_CFLAGS="$QEMU_CFLAGS $vnc_png_cflags" else if test "$vnc_png" = "yes" ; then feature_not_found "vnc-png" From d138cee907b36f217ad030fb2c75c027b7d5731b Mon Sep 17 00:00:00 2001 From: Michael Roth Date: Mon, 1 Aug 2011 14:52:57 -0500 Subject: [PATCH 230/230] guest agent: add --enable-guest-agent config option QAPI will require glib/python, but for now the guest agent is the only user. For now, make these dependencies an explicit guest agent one, and give users the option to disable it if need be. Once QAPI is adopted in core QEMU code, we would basically revert this patch. Signed-off-by: Michael Roth Signed-off-by: Anthony Liguori --- configure | 40 +++++++++++++++++++++++++++------------- 1 file changed, 27 insertions(+), 13 deletions(-) diff --git a/configure b/configure index 269bf43e13..e41fa0f20e 100755 --- a/configure +++ b/configure @@ -181,6 +181,7 @@ smartcard_nss="" usb_redir="" opengl="" zlib="yes" +guest_agent="yes" # parse CC options first for opt do @@ -757,6 +758,10 @@ for opt do ;; --disable-zlib-test) zlib="no" ;; + --enable-guest-agent) guest_agent="yes" + ;; + --disable-guest-agent) guest_agent="no" + ;; *) echo "ERROR: unknown option $opt"; show_help="yes" ;; esac @@ -1034,6 +1039,8 @@ echo " --disable-smartcard-nss disable smartcard nss support" echo " --enable-smartcard-nss enable smartcard nss support" echo " --disable-usb-redir disable usb network redirection support" echo " --enable-usb-redir enable usb network redirection support" +echo " --disable-guest-agent disable building of the QEMU Guest Agent" +echo " --enable-guest-agent enable building of the QEMU Guest Agent" echo "" echo "NOTE: The object files are built at the place where configure is launched" exit 1 @@ -1093,11 +1100,13 @@ if test "$solaris" = "yes" ; then fi fi -if has $python; then - : -else - echo "Python not found. Use --python=/path/to/python" - exit 1 +if test "$guest_agent" != "no" ; then + if has $python; then + : + else + echo "Python not found. Use --python=/path/to/python" + exit 1 + fi fi if test -z "$target_list" ; then @@ -1835,14 +1844,16 @@ fi ########################################## # glib support probe -if $pkg_config --modversion glib-2.0 > /dev/null 2>&1 ; then - glib_cflags=`$pkg_config --cflags glib-2.0 2>/dev/null` - glib_libs=`$pkg_config --libs glib-2.0 2>/dev/null` - libs_softmmu="$glib_libs $libs_softmmu" - libs_tools="$glib_libs $libs_tools" -else - echo "glib-2.0 required to compile QEMU" - exit 1 +if test "$guest_agent" != "no" ; then + if $pkg_config --modversion glib-2.0 > /dev/null 2>&1 ; then + glib_cflags=`$pkg_config --cflags glib-2.0 2>/dev/null` + glib_libs=`$pkg_config --libs glib-2.0 2>/dev/null` + libs_softmmu="$glib_libs $libs_softmmu" + libs_tools="$glib_libs $libs_tools" + else + echo "glib-2.0 required to compile QEMU" + exit 1 + fi fi ########################################## @@ -2602,7 +2613,9 @@ if test "$softmmu" = yes ; then tools="qemu-img\$(EXESUF) qemu-io\$(EXESUF) $tools" if [ "$linux" = "yes" -o "$bsd" = "yes" -o "$solaris" = "yes" ] ; then tools="qemu-nbd\$(EXESUF) $tools" + if [ "$guest_agent" = "yes" ]; then tools="qemu-ga\$(EXESUF) $tools" + fi if [ "$check_utests" = "yes" ]; then tools="check-qint check-qstring check-qdict check-qlist $tools" tools="check-qfloat check-qjson $tools" @@ -2704,6 +2717,7 @@ echo "xfsctl support $xfs" echo "nss used $smartcard_nss" echo "usb net redir $usb_redir" echo "OpenGL support $opengl" +echo "build guest agent $guest_agent" if test "$sdl_too_old" = "yes"; then echo "-> Your SDL version is too old - please upgrade to have SDL support"