aboutsummaryrefslogtreecommitdiff
path: root/hw
diff options
context:
space:
mode:
Diffstat (limited to 'hw')
-rw-r--r--hw/core/qdev-properties.c32
-rw-r--r--hw/i386/pc_q35.c4
-rw-r--r--hw/i386/xen/xen-hvm.c105
-rw-r--r--hw/i386/xen/xen-mapcache.c121
-rw-r--r--hw/i386/xen/xen_platform.c47
-rw-r--r--hw/ide/ahci.c10
-rw-r--r--hw/ide/ahci_internal.h359
-rw-r--r--hw/ide/ich.c2
-rw-r--r--hw/ide/qdev.c3
-rw-r--r--hw/mips/boston.c4
-rw-r--r--hw/scsi/scsi-disk.c5
-rw-r--r--hw/xen/xen_pt_config_init.c1
-rw-r--r--hw/xen/xen_pt_msi.c12
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;