diff options
Diffstat (limited to 'hw')
-rw-r--r-- | hw/core/qdev-properties.c | 32 | ||||
-rw-r--r-- | hw/i386/pc_q35.c | 4 | ||||
-rw-r--r-- | hw/i386/xen/xen-hvm.c | 105 | ||||
-rw-r--r-- | hw/i386/xen/xen-mapcache.c | 121 | ||||
-rw-r--r-- | hw/i386/xen/xen_platform.c | 47 | ||||
-rw-r--r-- | hw/ide/ahci.c | 10 | ||||
-rw-r--r-- | hw/ide/ahci_internal.h | 359 | ||||
-rw-r--r-- | hw/ide/ich.c | 2 | ||||
-rw-r--r-- | hw/ide/qdev.c | 3 | ||||
-rw-r--r-- | hw/mips/boston.c | 4 | ||||
-rw-r--r-- | hw/scsi/scsi-disk.c | 5 | ||||
-rw-r--r-- | hw/xen/xen_pt_config_init.c | 1 | ||||
-rw-r--r-- | hw/xen/xen_pt_msi.c | 12 |
13 files changed, 637 insertions, 68 deletions
diff --git a/hw/core/qdev-properties.c b/hw/core/qdev-properties.c index 58a8f92d92..078fc5d239 100644 --- a/hw/core/qdev-properties.c +++ b/hw/core/qdev-properties.c @@ -404,6 +404,31 @@ static void set_uint64(Object *obj, Visitor *v, const char *name, visit_type_uint64(v, name, ptr, errp); } +static void get_int64(Object *obj, Visitor *v, const char *name, + void *opaque, Error **errp) +{ + DeviceState *dev = DEVICE(obj); + Property *prop = opaque; + int64_t *ptr = qdev_get_prop_ptr(dev, prop); + + visit_type_int64(v, name, ptr, errp); +} + +static void set_int64(Object *obj, Visitor *v, const char *name, + void *opaque, Error **errp) +{ + DeviceState *dev = DEVICE(obj); + Property *prop = opaque; + int64_t *ptr = qdev_get_prop_ptr(dev, prop); + + if (dev->realized) { + qdev_prop_set_after_realize(dev, name, errp); + return; + } + + visit_type_int64(v, name, ptr, errp); +} + const PropertyInfo qdev_prop_uint64 = { .name = "uint64", .get = get_uint64, @@ -411,6 +436,13 @@ const PropertyInfo qdev_prop_uint64 = { .set_default_value = set_default_value_uint, }; +const PropertyInfo qdev_prop_int64 = { + .name = "int64", + .get = get_int64, + .set = set_int64, + .set_default_value = set_default_value_int, +}; + /* --- string --- */ static void release_string(Object *obj, const char *name, void *opaque) diff --git a/hw/i386/pc_q35.c b/hw/i386/pc_q35.c index 1653a47f0a..169a214d50 100644 --- a/hw/i386/pc_q35.c +++ b/hw/i386/pc_q35.c @@ -242,8 +242,8 @@ static void pc_q35_init(MachineState *machine) true, "ich9-ahci"); idebus[0] = qdev_get_child_bus(&ahci->qdev, "ide.0"); idebus[1] = qdev_get_child_bus(&ahci->qdev, "ide.1"); - g_assert(MAX_SATA_PORTS == ICH_AHCI(ahci)->ahci.ports); - ide_drive_get(hd, ICH_AHCI(ahci)->ahci.ports); + g_assert(MAX_SATA_PORTS == ahci_get_num_ports(ahci)); + ide_drive_get(hd, ahci_get_num_ports(ahci)); ahci_ide_create_devs(ahci, hd); } else { idebus[0] = idebus[1] = NULL; diff --git a/hw/i386/xen/xen-hvm.c b/hw/i386/xen/xen-hvm.c index 3d951a3794..d9ccd5d0d6 100644 --- a/hw/i386/xen/xen-hvm.c +++ b/hw/i386/xen/xen-hvm.c @@ -288,6 +288,7 @@ static XenPhysmap *get_physmapping(XenIOState *state, return NULL; } +#ifdef XEN_COMPAT_PHYSMAP static hwaddr xen_phys_offset_to_gaddr(hwaddr start_addr, ram_addr_t size, void *opaque) { @@ -304,6 +305,42 @@ static hwaddr xen_phys_offset_to_gaddr(hwaddr start_addr, return start_addr; } +static int xen_save_physmap(XenIOState *state, XenPhysmap *physmap) +{ + char path[80], value[17]; + + snprintf(path, sizeof(path), + "/local/domain/0/device-model/%d/physmap/%"PRIx64"/start_addr", + xen_domid, (uint64_t)physmap->phys_offset); + snprintf(value, sizeof(value), "%"PRIx64, (uint64_t)physmap->start_addr); + if (!xs_write(state->xenstore, 0, path, value, strlen(value))) { + return -1; + } + snprintf(path, sizeof(path), + "/local/domain/0/device-model/%d/physmap/%"PRIx64"/size", + xen_domid, (uint64_t)physmap->phys_offset); + snprintf(value, sizeof(value), "%"PRIx64, (uint64_t)physmap->size); + if (!xs_write(state->xenstore, 0, path, value, strlen(value))) { + return -1; + } + if (physmap->name) { + snprintf(path, sizeof(path), + "/local/domain/0/device-model/%d/physmap/%"PRIx64"/name", + xen_domid, (uint64_t)physmap->phys_offset); + if (!xs_write(state->xenstore, 0, path, + physmap->name, strlen(physmap->name))) { + return -1; + } + } + return 0; +} +#else +static int xen_save_physmap(XenIOState *state, XenPhysmap *physmap) +{ + return 0; +} +#endif + static int xen_add_to_physmap(XenIOState *state, hwaddr start_addr, ram_addr_t size, @@ -315,7 +352,6 @@ static int xen_add_to_physmap(XenIOState *state, XenPhysmap *physmap = NULL; hwaddr pfn, start_gpfn; hwaddr phys_offset = memory_region_get_ram_addr(mr); - char path[80], value[17]; const char *mr_name; if (get_physmapping(state, start_addr, size)) { @@ -338,6 +374,26 @@ go_physmap: DPRINTF("mapping vram to %"HWADDR_PRIx" - %"HWADDR_PRIx"\n", start_addr, start_addr + size); + mr_name = memory_region_name(mr); + + physmap = g_malloc(sizeof(XenPhysmap)); + + physmap->start_addr = start_addr; + physmap->size = size; + physmap->name = mr_name; + physmap->phys_offset = phys_offset; + + QLIST_INSERT_HEAD(&state->physmap, physmap, list); + + if (runstate_check(RUN_STATE_INMIGRATE)) { + /* Now when we have a physmap entry we can replace a dummy mapping with + * a real one of guest foreign memory. */ + uint8_t *p = xen_replace_cache_entry(phys_offset, start_addr, size); + assert(p && p == memory_region_get_ram_ptr(mr)); + + return 0; + } + pfn = phys_offset >> TARGET_PAGE_BITS; start_gpfn = start_addr >> TARGET_PAGE_BITS; for (i = 0; i < size >> TARGET_PAGE_BITS; i++) { @@ -352,46 +408,11 @@ go_physmap: } } - mr_name = memory_region_name(mr); - - physmap = g_malloc(sizeof (XenPhysmap)); - - physmap->start_addr = start_addr; - physmap->size = size; - physmap->name = mr_name; - physmap->phys_offset = phys_offset; - - QLIST_INSERT_HEAD(&state->physmap, physmap, list); - xc_domain_pin_memory_cacheattr(xen_xc, xen_domid, start_addr >> TARGET_PAGE_BITS, (start_addr + size - 1) >> TARGET_PAGE_BITS, XEN_DOMCTL_MEM_CACHEATTR_WB); - - snprintf(path, sizeof(path), - "/local/domain/0/device-model/%d/physmap/%"PRIx64"/start_addr", - xen_domid, (uint64_t)phys_offset); - snprintf(value, sizeof(value), "%"PRIx64, (uint64_t)start_addr); - if (!xs_write(state->xenstore, 0, path, value, strlen(value))) { - return -1; - } - snprintf(path, sizeof(path), - "/local/domain/0/device-model/%d/physmap/%"PRIx64"/size", - xen_domid, (uint64_t)phys_offset); - snprintf(value, sizeof(value), "%"PRIx64, (uint64_t)size); - if (!xs_write(state->xenstore, 0, path, value, strlen(value))) { - return -1; - } - if (mr_name) { - snprintf(path, sizeof(path), - "/local/domain/0/device-model/%d/physmap/%"PRIx64"/name", - xen_domid, (uint64_t)phys_offset); - if (!xs_write(state->xenstore, 0, path, mr_name, strlen(mr_name))) { - return -1; - } - } - - return 0; + return xen_save_physmap(state, physmap); } static int xen_remove_from_physmap(XenIOState *state, @@ -1152,6 +1173,7 @@ static void xen_exit_notifier(Notifier *n, void *data) xs_daemon_close(state->xenstore); } +#ifdef XEN_COMPAT_PHYSMAP static void xen_read_physmap(XenIOState *state) { XenPhysmap *physmap = NULL; @@ -1199,6 +1221,11 @@ static void xen_read_physmap(XenIOState *state) } free(entries); } +#else +static void xen_read_physmap(XenIOState *state) +{ +} +#endif static void xen_wakeup_notifier(Notifier *notifier, void *data) { @@ -1325,7 +1352,11 @@ void xen_hvm_init(PCMachineState *pcms, MemoryRegion **ram_memory) state->bufioreq_local_port = rc; /* Init RAM management */ +#ifdef XEN_COMPAT_PHYSMAP xen_map_cache_init(xen_phys_offset_to_gaddr, state); +#else + xen_map_cache_init(NULL, state); +#endif xen_ram_init(pcms, ram_size, ram_memory); qemu_add_vm_change_state_handler(xen_hvm_change_state_handler, state); diff --git a/hw/i386/xen/xen-mapcache.c b/hw/i386/xen/xen-mapcache.c index e60156c04f..2a1fbd13cc 100644 --- a/hw/i386/xen/xen-mapcache.c +++ b/hw/i386/xen/xen-mapcache.c @@ -53,6 +53,8 @@ typedef struct MapCacheEntry { uint8_t *vaddr_base; unsigned long *valid_mapping; uint8_t lock; +#define XEN_MAPCACHE_ENTRY_DUMMY (1 << 0) + uint8_t flags; hwaddr size; struct MapCacheEntry *next; } MapCacheEntry; @@ -149,8 +151,10 @@ void xen_map_cache_init(phys_offset_to_gaddr_t f, void *opaque) } static void xen_remap_bucket(MapCacheEntry *entry, + void *vaddr, hwaddr size, - hwaddr address_index) + hwaddr address_index, + bool dummy) { uint8_t *vaddr_base; xen_pfn_t *pfns; @@ -164,7 +168,9 @@ static void xen_remap_bucket(MapCacheEntry *entry, err = g_malloc0(nb_pfn * sizeof (int)); if (entry->vaddr_base != NULL) { - ram_block_notify_remove(entry->vaddr_base, entry->size); + if (!(entry->flags & XEN_MAPCACHE_ENTRY_DUMMY)) { + ram_block_notify_remove(entry->vaddr_base, entry->size); + } if (munmap(entry->vaddr_base, entry->size) != 0) { perror("unmap fails"); exit(-1); @@ -177,11 +183,29 @@ static void xen_remap_bucket(MapCacheEntry *entry, pfns[i] = (address_index << (MCACHE_BUCKET_SHIFT-XC_PAGE_SHIFT)) + i; } - vaddr_base = xenforeignmemory_map(xen_fmem, xen_domid, PROT_READ|PROT_WRITE, - nb_pfn, pfns, err); - if (vaddr_base == NULL) { - perror("xenforeignmemory_map"); - exit(-1); + if (!dummy) { + vaddr_base = xenforeignmemory_map2(xen_fmem, xen_domid, vaddr, + PROT_READ | PROT_WRITE, 0, + nb_pfn, pfns, err); + if (vaddr_base == NULL) { + perror("xenforeignmemory_map2"); + exit(-1); + } + } else { + /* + * We create dummy mappings where we are unable to create a foreign + * mapping immediately due to certain circumstances (i.e. on resume now) + */ + vaddr_base = mmap(vaddr, size, PROT_READ | PROT_WRITE, + MAP_ANON | MAP_SHARED, -1, 0); + if (vaddr_base == NULL) { + perror("mmap"); + exit(-1); + } + } + + if (!(entry->flags & XEN_MAPCACHE_ENTRY_DUMMY)) { + ram_block_notify_add(vaddr_base, size); } entry->vaddr_base = vaddr_base; @@ -190,7 +214,12 @@ static void xen_remap_bucket(MapCacheEntry *entry, entry->valid_mapping = (unsigned long *) g_malloc0(sizeof(unsigned long) * BITS_TO_LONGS(size >> XC_PAGE_SHIFT)); - ram_block_notify_add(entry->vaddr_base, entry->size); + if (dummy) { + entry->flags |= XEN_MAPCACHE_ENTRY_DUMMY; + } else { + entry->flags &= ~(XEN_MAPCACHE_ENTRY_DUMMY); + } + bitmap_zero(entry->valid_mapping, nb_pfn); for (i = 0; i < nb_pfn; i++) { if (!err[i]) { @@ -210,7 +239,8 @@ static uint8_t *xen_map_cache_unlocked(hwaddr phys_addr, hwaddr size, hwaddr address_offset; hwaddr cache_size = size; hwaddr test_bit_size; - bool translated = false; + bool translated G_GNUC_UNUSED = false; + bool dummy = false; tryagain: address_index = phys_addr >> MCACHE_BUCKET_SHIFT; @@ -262,14 +292,14 @@ tryagain: if (!entry) { entry = g_malloc0(sizeof (MapCacheEntry)); pentry->next = entry; - xen_remap_bucket(entry, cache_size, address_index); + xen_remap_bucket(entry, NULL, cache_size, address_index, dummy); } else if (!entry->lock) { if (!entry->vaddr_base || entry->paddr_index != address_index || entry->size != cache_size || !test_bits(address_offset >> XC_PAGE_SHIFT, test_bit_size >> XC_PAGE_SHIFT, entry->valid_mapping)) { - xen_remap_bucket(entry, cache_size, address_index); + xen_remap_bucket(entry, NULL, cache_size, address_index, dummy); } } @@ -277,11 +307,17 @@ tryagain: test_bit_size >> XC_PAGE_SHIFT, entry->valid_mapping)) { mapcache->last_entry = NULL; +#ifdef XEN_COMPAT_PHYSMAP if (!translated && mapcache->phys_offset_to_gaddr) { phys_addr = mapcache->phys_offset_to_gaddr(phys_addr, size, mapcache->opaque); translated = true; goto tryagain; } +#endif + if (!dummy && runstate_check(RUN_STATE_INMIGRATE)) { + dummy = true; + goto tryagain; + } trace_xen_map_cache_return(NULL); return NULL; } @@ -462,3 +498,66 @@ void xen_invalidate_map_cache(void) mapcache_unlock(); } + +static uint8_t *xen_replace_cache_entry_unlocked(hwaddr old_phys_addr, + hwaddr new_phys_addr, + hwaddr size) +{ + MapCacheEntry *entry; + hwaddr address_index, address_offset; + hwaddr test_bit_size, cache_size = size; + + address_index = old_phys_addr >> MCACHE_BUCKET_SHIFT; + address_offset = old_phys_addr & (MCACHE_BUCKET_SIZE - 1); + + assert(size); + /* test_bit_size is always a multiple of XC_PAGE_SIZE */ + test_bit_size = size + (old_phys_addr & (XC_PAGE_SIZE - 1)); + if (test_bit_size % XC_PAGE_SIZE) { + test_bit_size += XC_PAGE_SIZE - (test_bit_size % XC_PAGE_SIZE); + } + cache_size = size + address_offset; + if (cache_size % MCACHE_BUCKET_SIZE) { + cache_size += MCACHE_BUCKET_SIZE - (cache_size % MCACHE_BUCKET_SIZE); + } + + entry = &mapcache->entry[address_index % mapcache->nr_buckets]; + while (entry && !(entry->paddr_index == address_index && + entry->size == cache_size)) { + entry = entry->next; + } + if (!entry) { + DPRINTF("Trying to update an entry for %lx " \ + "that is not in the mapcache!\n", old_phys_addr); + return NULL; + } + + address_index = new_phys_addr >> MCACHE_BUCKET_SHIFT; + address_offset = new_phys_addr & (MCACHE_BUCKET_SIZE - 1); + + fprintf(stderr, "Replacing a dummy mapcache entry for %lx with %lx\n", + old_phys_addr, new_phys_addr); + + xen_remap_bucket(entry, entry->vaddr_base, + cache_size, address_index, false); + if (!test_bits(address_offset >> XC_PAGE_SHIFT, + test_bit_size >> XC_PAGE_SHIFT, + entry->valid_mapping)) { + DPRINTF("Unable to update a mapcache entry for %lx!\n", old_phys_addr); + return NULL; + } + + return entry->vaddr_base + address_offset; +} + +uint8_t *xen_replace_cache_entry(hwaddr old_phys_addr, + hwaddr new_phys_addr, + hwaddr size) +{ + uint8_t *p; + + mapcache_lock(); + p = xen_replace_cache_entry_unlocked(old_phys_addr, new_phys_addr, size); + mapcache_unlock(); + return p; +} diff --git a/hw/i386/xen/xen_platform.c b/hw/i386/xen/xen_platform.c index f23155832b..9ba7474566 100644 --- a/hw/i386/xen/xen_platform.c +++ b/hw/i386/xen/xen_platform.c @@ -87,10 +87,30 @@ static void log_writeb(PCIXenPlatformState *s, char val) } } -/* Xen Platform, Fixed IOPort */ -#define UNPLUG_ALL_DISKS 1 -#define UNPLUG_ALL_NICS 2 -#define UNPLUG_AUX_IDE_DISKS 4 +/* + * Unplug device flags. + * + * The logic got a little confused at some point in the past but this is + * what they do now. + * + * bit 0: Unplug all IDE and SCSI disks. + * bit 1: Unplug all NICs. + * bit 2: Unplug IDE disks except primary master. This is overridden if + * bit 0 is also present in the mask. + * bit 3: Unplug all NVMe disks. + * + */ +#define _UNPLUG_IDE_SCSI_DISKS 0 +#define UNPLUG_IDE_SCSI_DISKS (1u << _UNPLUG_IDE_SCSI_DISKS) + +#define _UNPLUG_ALL_NICS 1 +#define UNPLUG_ALL_NICS (1u << _UNPLUG_ALL_NICS) + +#define _UNPLUG_AUX_IDE_DISKS 2 +#define UNPLUG_AUX_IDE_DISKS (1u << _UNPLUG_AUX_IDE_DISKS) + +#define _UNPLUG_NVME_DISKS 3 +#define UNPLUG_NVME_DISKS (1u << _UNPLUG_NVME_DISKS) static void unplug_nic(PCIBus *b, PCIDevice *d, void *o) { @@ -122,7 +142,7 @@ static void unplug_disks(PCIBus *b, PCIDevice *d, void *opaque) { uint32_t flags = *(uint32_t *)opaque; bool aux = (flags & UNPLUG_AUX_IDE_DISKS) && - !(flags & UNPLUG_ALL_DISKS); + !(flags & UNPLUG_IDE_SCSI_DISKS); /* We have to ignore passthrough devices */ if (!strcmp(d->name, "xen-pci-passthrough")) { @@ -135,12 +155,16 @@ static void unplug_disks(PCIBus *b, PCIDevice *d, void *opaque) break; case PCI_CLASS_STORAGE_SCSI: - case PCI_CLASS_STORAGE_EXPRESS: if (!aux) { object_unparent(OBJECT(d)); } break; + case PCI_CLASS_STORAGE_EXPRESS: + if (flags & UNPLUG_NVME_DISKS) { + object_unparent(OBJECT(d)); + } + default: break; } @@ -158,10 +182,9 @@ static void platform_fixed_ioport_writew(void *opaque, uint32_t addr, uint32_t v switch (addr) { case 0: { PCIDevice *pci_dev = PCI_DEVICE(s); - /* Unplug devices. Value is a bitmask of which devices to - unplug, with bit 0 the disk devices, bit 1 the network - devices, and bit 2 the non-primary-master IDE devices. */ - if (val & (UNPLUG_ALL_DISKS | UNPLUG_AUX_IDE_DISKS)) { + /* Unplug devices. See comment above flag definitions */ + if (val & (UNPLUG_IDE_SCSI_DISKS | UNPLUG_AUX_IDE_DISKS | + UNPLUG_NVME_DISKS)) { DPRINTF("unplug disks\n"); pci_unplug_disks(pci_dev->bus, val); } @@ -349,14 +372,14 @@ static void xen_platform_ioport_writeb(void *opaque, hwaddr addr, * If VMDP was to control both disk and LAN it would use 4. * If it controlled just disk or just LAN, it would use 8 below. */ - pci_unplug_disks(pci_dev->bus, UNPLUG_ALL_DISKS); + pci_unplug_disks(pci_dev->bus, UNPLUG_IDE_SCSI_DISKS); pci_unplug_nics(pci_dev->bus); } break; case 8: switch (val) { case 1: - pci_unplug_disks(pci_dev->bus, UNPLUG_ALL_DISKS); + pci_unplug_disks(pci_dev->bus, UNPLUG_IDE_SCSI_DISKS); break; case 2: pci_unplug_nics(pci_dev->bus); diff --git a/hw/ide/ahci.c b/hw/ide/ahci.c index 874d3fe280..406a1b5579 100644 --- a/hw/ide/ahci.c +++ b/hw/ide/ahci.c @@ -32,7 +32,7 @@ #include "sysemu/dma.h" #include "hw/ide/internal.h" #include "hw/ide/pci.h" -#include "hw/ide/ahci.h" +#include "hw/ide/ahci_internal.h" #define DEBUG_AHCI 0 @@ -1833,6 +1833,14 @@ static void sysbus_ahci_register_types(void) type_init(sysbus_ahci_register_types) +int32_t ahci_get_num_ports(PCIDevice *dev) +{ + AHCIPCIState *d = ICH_AHCI(dev); + AHCIState *ahci = &d->ahci; + + return ahci->ports; +} + void ahci_ide_create_devs(PCIDevice *dev, DriveInfo **hd) { AHCIPCIState *d = ICH_AHCI(dev); diff --git a/hw/ide/ahci_internal.h b/hw/ide/ahci_internal.h new file mode 100644 index 0000000000..1e21169e79 --- /dev/null +++ b/hw/ide/ahci_internal.h @@ -0,0 +1,359 @@ +/* + * QEMU AHCI Emulation + * + * Copyright (c) 2010 qiaochong@loongson.cn + * Copyright (c) 2010 Roland Elek <elek.roland@gmail.com> + * Copyright (c) 2010 Sebastian Herbszt <herbszt@gmx.de> + * Copyright (c) 2010 Alexander Graf <agraf@suse.de> + * + * 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 <http://www.gnu.org/licenses/>. + * + */ + +#ifndef HW_IDE_AHCI_INTERNAL_H +#define HW_IDE_AHCI_INTERNAL_H + +#include "hw/ide/ahci.h" +#include "hw/sysbus.h" + +#define AHCI_MEM_BAR_SIZE 0x1000 +#define AHCI_MAX_PORTS 32 +#define AHCI_MAX_SG 168 /* hardware max is 64K */ +#define AHCI_DMA_BOUNDARY 0xffffffff +#define AHCI_USE_CLUSTERING 0 +#define AHCI_MAX_CMDS 32 +#define AHCI_CMD_SZ 32 +#define AHCI_CMD_SLOT_SZ (AHCI_MAX_CMDS * AHCI_CMD_SZ) +#define AHCI_RX_FIS_SZ 256 +#define AHCI_CMD_TBL_CDB 0x40 +#define AHCI_CMD_TBL_HDR_SZ 0x80 +#define AHCI_CMD_TBL_SZ (AHCI_CMD_TBL_HDR_SZ + (AHCI_MAX_SG * 16)) +#define AHCI_CMD_TBL_AR_SZ (AHCI_CMD_TBL_SZ * AHCI_MAX_CMDS) +#define AHCI_PORT_PRIV_DMA_SZ (AHCI_CMD_SLOT_SZ + AHCI_CMD_TBL_AR_SZ + \ + AHCI_RX_FIS_SZ) + +#define AHCI_IRQ_ON_SG (1U << 31) +#define AHCI_CMD_ATAPI (1 << 5) +#define AHCI_CMD_WRITE (1 << 6) +#define AHCI_CMD_PREFETCH (1 << 7) +#define AHCI_CMD_RESET (1 << 8) +#define AHCI_CMD_CLR_BUSY (1 << 10) + +#define RX_FIS_D2H_REG 0x40 /* offset of D2H Register FIS data */ +#define RX_FIS_SDB 0x58 /* offset of SDB FIS data */ +#define RX_FIS_UNK 0x60 /* offset of Unknown FIS data */ + +/* global controller registers */ +#define HOST_CAP 0x00 /* host capabilities */ +#define HOST_CTL 0x04 /* global host control */ +#define HOST_IRQ_STAT 0x08 /* interrupt status */ +#define HOST_PORTS_IMPL 0x0c /* bitmap of implemented ports */ +#define HOST_VERSION 0x10 /* AHCI spec. version compliancy */ + +/* HOST_CTL bits */ +#define HOST_CTL_RESET (1 << 0) /* reset controller; self-clear */ +#define HOST_CTL_IRQ_EN (1 << 1) /* global IRQ enable */ +#define HOST_CTL_AHCI_EN (1U << 31) /* AHCI enabled */ + +/* HOST_CAP bits */ +#define HOST_CAP_SSC (1 << 14) /* Slumber capable */ +#define HOST_CAP_AHCI (1 << 18) /* AHCI only */ +#define HOST_CAP_CLO (1 << 24) /* Command List Override support */ +#define HOST_CAP_SSS (1 << 27) /* Staggered Spin-up */ +#define HOST_CAP_NCQ (1 << 30) /* Native Command Queueing */ +#define HOST_CAP_64 (1U << 31) /* PCI DAC (64-bit DMA) support */ + +/* registers for each SATA port */ +#define PORT_LST_ADDR 0x00 /* command list DMA addr */ +#define PORT_LST_ADDR_HI 0x04 /* command list DMA addr hi */ +#define PORT_FIS_ADDR 0x08 /* FIS rx buf addr */ +#define PORT_FIS_ADDR_HI 0x0c /* FIS rx buf addr hi */ +#define PORT_IRQ_STAT 0x10 /* interrupt status */ +#define PORT_IRQ_MASK 0x14 /* interrupt enable/disable mask */ +#define PORT_CMD 0x18 /* port command */ +#define PORT_TFDATA 0x20 /* taskfile data */ +#define PORT_SIG 0x24 /* device TF signature */ +#define PORT_SCR_STAT 0x28 /* SATA phy register: SStatus */ +#define PORT_SCR_CTL 0x2c /* SATA phy register: SControl */ +#define PORT_SCR_ERR 0x30 /* SATA phy register: SError */ +#define PORT_SCR_ACT 0x34 /* SATA phy register: SActive */ +#define PORT_CMD_ISSUE 0x38 /* command issue */ +#define PORT_RESERVED 0x3c /* reserved */ + +/* PORT_IRQ_{STAT,MASK} bits */ +#define PORT_IRQ_COLD_PRES (1U << 31) /* cold presence detect */ +#define PORT_IRQ_TF_ERR (1 << 30) /* task file error */ +#define PORT_IRQ_HBUS_ERR (1 << 29) /* host bus fatal error */ +#define PORT_IRQ_HBUS_DATA_ERR (1 << 28) /* host bus data error */ +#define PORT_IRQ_IF_ERR (1 << 27) /* interface fatal error */ +#define PORT_IRQ_IF_NONFATAL (1 << 26) /* interface non-fatal error */ +#define PORT_IRQ_OVERFLOW (1 << 24) /* xfer exhausted available S/G */ +#define PORT_IRQ_BAD_PMP (1 << 23) /* incorrect port multiplier */ + +#define PORT_IRQ_PHYRDY (1 << 22) /* PhyRdy changed */ +#define PORT_IRQ_DEV_ILCK (1 << 7) /* device interlock */ +#define PORT_IRQ_CONNECT (1 << 6) /* port connect change status */ +#define PORT_IRQ_SG_DONE (1 << 5) /* descriptor processed */ +#define PORT_IRQ_UNK_FIS (1 << 4) /* unknown FIS rx'd */ +#define PORT_IRQ_SDB_FIS (1 << 3) /* Set Device Bits FIS rx'd */ +#define PORT_IRQ_DMAS_FIS (1 << 2) /* DMA Setup FIS rx'd */ +#define PORT_IRQ_PIOS_FIS (1 << 1) /* PIO Setup FIS rx'd */ +#define PORT_IRQ_D2H_REG_FIS (1 << 0) /* D2H Register FIS rx'd */ + +#define PORT_IRQ_FREEZE (PORT_IRQ_HBUS_ERR | PORT_IRQ_IF_ERR | \ + PORT_IRQ_CONNECT | PORT_IRQ_PHYRDY | \ + PORT_IRQ_UNK_FIS) +#define PORT_IRQ_ERROR (PORT_IRQ_FREEZE | PORT_IRQ_TF_ERR | \ + PORT_IRQ_HBUS_DATA_ERR) +#define DEF_PORT_IRQ (PORT_IRQ_ERROR | PORT_IRQ_SG_DONE | \ + PORT_IRQ_SDB_FIS | PORT_IRQ_DMAS_FIS | \ + PORT_IRQ_PIOS_FIS | PORT_IRQ_D2H_REG_FIS) + +/* PORT_CMD bits */ +#define PORT_CMD_ATAPI (1 << 24) /* Device is ATAPI */ +#define PORT_CMD_LIST_ON (1 << 15) /* cmd list DMA engine running */ +#define PORT_CMD_FIS_ON (1 << 14) /* FIS DMA engine running */ +#define PORT_CMD_FIS_RX (1 << 4) /* Enable FIS receive DMA engine */ +#define PORT_CMD_CLO (1 << 3) /* Command list override */ +#define PORT_CMD_POWER_ON (1 << 2) /* Power up device */ +#define PORT_CMD_SPIN_UP (1 << 1) /* Spin up device */ +#define PORT_CMD_START (1 << 0) /* Enable port DMA engine */ + +#define PORT_CMD_ICC_MASK (0xfU << 28) /* i/f ICC state mask */ +#define PORT_CMD_ICC_ACTIVE (0x1 << 28) /* Put i/f in active state */ +#define PORT_CMD_ICC_PARTIAL (0x2 << 28) /* Put i/f in partial state */ +#define PORT_CMD_ICC_SLUMBER (0x6 << 28) /* Put i/f in slumber state */ + +#define PORT_CMD_RO_MASK 0x007dffe0 /* Which CMD bits are read only? */ + +/* ap->flags bits */ +#define AHCI_FLAG_NO_NCQ (1 << 24) +#define AHCI_FLAG_IGN_IRQ_IF_ERR (1 << 25) /* ignore IRQ_IF_ERR */ +#define AHCI_FLAG_HONOR_PI (1 << 26) /* honor PORTS_IMPL */ +#define AHCI_FLAG_IGN_SERR_INTERNAL (1 << 27) /* ignore SERR_INTERNAL */ +#define AHCI_FLAG_32BIT_ONLY (1 << 28) /* force 32bit */ + +#define ATA_SRST (1 << 2) /* software reset */ + +#define STATE_RUN 0 +#define STATE_RESET 1 + +#define SATA_SCR_SSTATUS_DET_NODEV 0x0 +#define SATA_SCR_SSTATUS_DET_DEV_PRESENT_PHY_UP 0x3 + +#define SATA_SCR_SSTATUS_SPD_NODEV 0x00 +#define SATA_SCR_SSTATUS_SPD_GEN1 0x10 + +#define SATA_SCR_SSTATUS_IPM_NODEV 0x000 +#define SATA_SCR_SSTATUS_IPM_ACTIVE 0X100 + +#define AHCI_SCR_SCTL_DET 0xf + +#define SATA_FIS_TYPE_REGISTER_H2D 0x27 +#define SATA_FIS_REG_H2D_UPDATE_COMMAND_REGISTER 0x80 +#define SATA_FIS_TYPE_REGISTER_D2H 0x34 +#define SATA_FIS_TYPE_PIO_SETUP 0x5f +#define SATA_FIS_TYPE_SDB 0xA1 + +#define AHCI_CMD_HDR_CMD_FIS_LEN 0x1f +#define AHCI_CMD_HDR_PRDT_LEN 16 + +#define SATA_SIGNATURE_CDROM 0xeb140101 +#define SATA_SIGNATURE_DISK 0x00000101 + +#define AHCI_GENERIC_HOST_CONTROL_REGS_MAX_ADDR 0x20 + /* Shouldn't this be 0x2c? */ + +#define AHCI_PORT_REGS_START_ADDR 0x100 +#define AHCI_PORT_ADDR_OFFSET_MASK 0x7f +#define AHCI_PORT_ADDR_OFFSET_LEN 0x80 + +#define AHCI_NUM_COMMAND_SLOTS 31 +#define AHCI_SUPPORTED_SPEED 20 +#define AHCI_SUPPORTED_SPEED_GEN1 1 +#define AHCI_VERSION_1_0 0x10000 + +#define AHCI_PROGMODE_MAJOR_REV_1 1 + +#define AHCI_COMMAND_TABLE_ACMD 0x40 + +#define AHCI_PRDT_SIZE_MASK 0x3fffff + +#define IDE_FEATURE_DMA 1 + +#define READ_FPDMA_QUEUED 0x60 +#define WRITE_FPDMA_QUEUED 0x61 +#define NCQ_NON_DATA 0x63 +#define RECEIVE_FPDMA_QUEUED 0x65 +#define SEND_FPDMA_QUEUED 0x64 + +#define NCQ_FIS_FUA_MASK 0x80 +#define NCQ_FIS_RARC_MASK 0x01 + +#define RES_FIS_DSFIS 0x00 +#define RES_FIS_PSFIS 0x20 +#define RES_FIS_RFIS 0x40 +#define RES_FIS_SDBFIS 0x58 +#define RES_FIS_UFIS 0x60 + +#define SATA_CAP_SIZE 0x8 +#define SATA_CAP_REV 0x2 +#define SATA_CAP_BAR 0x4 + +typedef struct AHCIPortRegs { + uint32_t lst_addr; + uint32_t lst_addr_hi; + uint32_t fis_addr; + uint32_t fis_addr_hi; + uint32_t irq_stat; + uint32_t irq_mask; + uint32_t cmd; + uint32_t unused0; + uint32_t tfdata; + uint32_t sig; + uint32_t scr_stat; + uint32_t scr_ctl; + uint32_t scr_err; + uint32_t scr_act; + uint32_t cmd_issue; + uint32_t reserved; +} AHCIPortRegs; + +typedef struct AHCICmdHdr { + uint16_t opts; + uint16_t prdtl; + uint32_t status; + uint64_t tbl_addr; + uint32_t reserved[4]; +} QEMU_PACKED AHCICmdHdr; + +typedef struct AHCI_SG { + uint64_t addr; + uint32_t reserved; + uint32_t flags_size; +} QEMU_PACKED AHCI_SG; + +typedef struct NCQTransferState { + AHCIDevice *drive; + BlockAIOCB *aiocb; + AHCICmdHdr *cmdh; + QEMUSGList sglist; + BlockAcctCookie acct; + uint32_t sector_count; + uint64_t lba; + uint8_t tag; + uint8_t cmd; + uint8_t slot; + bool used; + bool halt; +} NCQTransferState; + +struct AHCIDevice { + IDEDMA dma; + IDEBus port; + int port_no; + uint32_t port_state; + uint32_t finished; + AHCIPortRegs port_regs; + struct AHCIState *hba; + QEMUBH *check_bh; + uint8_t *lst; + uint8_t *res_fis; + bool done_atapi_packet; + int32_t busy_slot; + bool init_d2h_sent; + AHCICmdHdr *cur_cmd; + NCQTransferState ncq_tfs[AHCI_MAX_CMDS]; +}; + +struct AHCIPCIState { + /*< private >*/ + PCIDevice parent_obj; + /*< public >*/ + + AHCIState ahci; +}; + +#define TYPE_ICH9_AHCI "ich9-ahci" + +#define ICH_AHCI(obj) \ + OBJECT_CHECK(AHCIPCIState, (obj), TYPE_ICH9_AHCI) + +extern const VMStateDescription vmstate_ahci; + +#define VMSTATE_AHCI(_field, _state) { \ + .name = (stringify(_field)), \ + .size = sizeof(AHCIState), \ + .vmsd = &vmstate_ahci, \ + .flags = VMS_STRUCT, \ + .offset = vmstate_offset_value(_state, _field, AHCIState), \ +} + +/** + * NCQFrame is the same as a Register H2D FIS (described in SATA 3.2), + * but some fields have been re-mapped and re-purposed, as seen in + * SATA 3.2 section 13.6.4.1 ("READ FPDMA QUEUED") + * + * cmd_fis[3], feature 7:0, becomes sector count 7:0. + * cmd_fis[7], device 7:0, uses bit 7 as the Force Unit Access bit. + * cmd_fis[11], feature 15:8, becomes sector count 15:8. + * cmd_fis[12], count 7:0, becomes the NCQ TAG (7:3) and RARC bit (0) + * cmd_fis[13], count 15:8, becomes the priority value (7:6) + * bytes 16-19 become an le32 "auxiliary" field. + */ +typedef struct NCQFrame { + uint8_t fis_type; + uint8_t c; + uint8_t command; + uint8_t sector_count_low; /* (feature 7:0) */ + uint8_t lba0; + uint8_t lba1; + uint8_t lba2; + uint8_t fua; /* (device 7:0) */ + uint8_t lba3; + uint8_t lba4; + uint8_t lba5; + uint8_t sector_count_high; /* (feature 15:8) */ + uint8_t tag; /* (count 0:7) */ + uint8_t prio; /* (count 15:8) */ + uint8_t icc; + uint8_t control; + uint8_t aux0; + uint8_t aux1; + uint8_t aux2; + uint8_t aux3; +} QEMU_PACKED NCQFrame; + +typedef struct SDBFIS { + uint8_t type; + uint8_t flags; + uint8_t status; + uint8_t error; + uint32_t payload; +} QEMU_PACKED SDBFIS; + +void ahci_realize(AHCIState *s, DeviceState *qdev, AddressSpace *as, int ports); +void ahci_init(AHCIState *s, DeviceState *qdev); +void ahci_uninit(AHCIState *s); + +void ahci_reset(AHCIState *s); + +#define TYPE_SYSBUS_AHCI "sysbus-ahci" +#define SYSBUS_AHCI(obj) OBJECT_CHECK(SysbusAHCIState, (obj), TYPE_SYSBUS_AHCI) + +#define TYPE_ALLWINNER_AHCI "allwinner-ahci" +#define ALLWINNER_AHCI(obj) OBJECT_CHECK(AllwinnerAHCIState, (obj), \ + TYPE_ALLWINNER_AHCI) + +#endif /* HW_IDE_AHCI_H */ diff --git a/hw/ide/ich.c b/hw/ide/ich.c index 989fca5e9f..9472a60cab 100644 --- a/hw/ide/ich.c +++ b/hw/ide/ich.c @@ -69,7 +69,7 @@ #include "sysemu/block-backend.h" #include "sysemu/dma.h" #include "hw/ide/pci.h" -#include "hw/ide/ahci.h" +#include "hw/ide/ahci_internal.h" #define ICH9_MSI_CAP_OFFSET 0x80 #define ICH9_SATA_CAP_OFFSET 0xA8 diff --git a/hw/ide/qdev.c b/hw/ide/qdev.c index 299e592fa2..cc2f5bd280 100644 --- a/hw/ide/qdev.c +++ b/hw/ide/qdev.c @@ -164,6 +164,7 @@ static int ide_dev_initfn(IDEDevice *dev, IDEDriveKind kind) IDEBus *bus = DO_UPCAST(IDEBus, qbus, dev->qdev.parent_bus); IDEState *s = bus->ifs + dev->unit; Error *err = NULL; + int ret; if (!dev->conf.blk) { if (kind != IDE_CD) { @@ -172,6 +173,8 @@ static int ide_dev_initfn(IDEDevice *dev, IDEDriveKind kind) } else { /* Anonymous BlockBackend for an empty drive */ dev->conf.blk = blk_new(0, BLK_PERM_ALL); + ret = blk_attach_dev(dev->conf.blk, &dev->qdev); + assert(ret == 0); } } diff --git a/hw/mips/boston.c b/hw/mips/boston.c index 7985c60dde..776ee283e1 100644 --- a/hw/mips/boston.c +++ b/hw/mips/boston.c @@ -538,8 +538,8 @@ static void boston_mach_init(MachineState *machine) ahci = pci_create_simple_multifunction(&PCI_BRIDGE(&pcie2->root)->sec_bus, PCI_DEVFN(0, 0), true, TYPE_ICH9_AHCI); - g_assert(ARRAY_SIZE(hd) == ICH_AHCI(ahci)->ahci.ports); - ide_drive_get(hd, ICH_AHCI(ahci)->ahci.ports); + g_assert(ARRAY_SIZE(hd) == ahci_get_num_ports(ahci)); + ide_drive_get(hd, ahci_get_num_ports(ahci)); ahci_ide_create_devs(ahci, hd); if (machine->firmware) { diff --git a/hw/scsi/scsi-disk.c b/hw/scsi/scsi-disk.c index a53f058621..5f1e5e8070 100644 --- a/hw/scsi/scsi-disk.c +++ b/hw/scsi/scsi-disk.c @@ -2384,9 +2384,14 @@ static void scsi_hd_realize(SCSIDevice *dev, Error **errp) static void scsi_cd_realize(SCSIDevice *dev, Error **errp) { SCSIDiskState *s = DO_UPCAST(SCSIDiskState, qdev, dev); + int ret; if (!dev->conf.blk) { + /* Anonymous BlockBackend for an empty drive. As we put it into + * dev->conf, qdev takes care of detaching on unplug. */ dev->conf.blk = blk_new(0, BLK_PERM_ALL); + ret = blk_attach_dev(dev->conf.blk, &dev->qdev); + assert(ret == 0); } s->qdev.blocksize = 2048; diff --git a/hw/xen/xen_pt_config_init.c b/hw/xen/xen_pt_config_init.c index 6f18366f67..1f04ec5eec 100644 --- a/hw/xen/xen_pt_config_init.c +++ b/hw/xen/xen_pt_config_init.c @@ -1535,6 +1535,7 @@ static XenPTRegInfo xen_pt_emu_reg_igd_opregion[] = { .offset = 0x0, .size = 4, .init_val = 0, + .emu_mask = 0xFFFFFFFF, .u.dw.read = xen_pt_intel_opregion_read, .u.dw.write = xen_pt_intel_opregion_write, }, diff --git a/hw/xen/xen_pt_msi.c b/hw/xen/xen_pt_msi.c index 62add0639f..ff9a79f5d2 100644 --- a/hw/xen/xen_pt_msi.c +++ b/hw/xen/xen_pt_msi.c @@ -535,7 +535,11 @@ int xen_pt_msix_init(XenPCIPassthroughState *s, uint32_t base) return -1; } - xen_host_pci_get_word(hd, base + PCI_MSIX_FLAGS, &control); + rc = xen_host_pci_get_word(hd, base + PCI_MSIX_FLAGS, &control); + if (rc) { + XEN_PT_ERR(d, "Failed to read PCI_MSIX_FLAGS field\n"); + return rc; + } total_entries = control & PCI_MSIX_FLAGS_QSIZE; total_entries += 1; @@ -554,7 +558,11 @@ int xen_pt_msix_init(XenPCIPassthroughState *s, uint32_t base) + XC_PAGE_SIZE - 1) & XC_PAGE_MASK); - xen_host_pci_get_long(hd, base + PCI_MSIX_TABLE, &table_off); + rc = xen_host_pci_get_long(hd, base + PCI_MSIX_TABLE, &table_off); + if (rc) { + XEN_PT_ERR(d, "Failed to read PCI_MSIX_TABLE field\n"); + goto error_out; + } bar_index = msix->bar_index = table_off & PCI_MSIX_FLAGS_BIRMASK; table_off = table_off & ~PCI_MSIX_FLAGS_BIRMASK; msix->table_base = s->real_device.io_regions[bar_index].base_addr; |