aboutsummaryrefslogtreecommitdiff
path: root/hw/ppc/spapr_iommu.c
diff options
context:
space:
mode:
Diffstat (limited to 'hw/ppc/spapr_iommu.c')
-rw-r--r--hw/ppc/spapr_iommu.c140
1 files changed, 113 insertions, 27 deletions
diff --git a/hw/ppc/spapr_iommu.c b/hw/ppc/spapr_iommu.c
index 96bb018..a3cc572 100644
--- a/hw/ppc/spapr_iommu.c
+++ b/hw/ppc/spapr_iommu.c
@@ -17,6 +17,7 @@
* License along with this library; if not, see <http://www.gnu.org/licenses/>.
*/
#include "qemu/osdep.h"
+#include "qemu/error-report.h"
#include "hw/hw.h"
#include "qemu/log.h"
#include "sysemu/kvm.h"
@@ -137,33 +138,92 @@ static IOMMUTLBEntry spapr_tce_translate_iommu(MemoryRegion *iommu, hwaddr addr,
return ret;
}
+static void spapr_tce_table_pre_save(void *opaque)
+{
+ sPAPRTCETable *tcet = SPAPR_TCE_TABLE(opaque);
+
+ tcet->mig_table = tcet->table;
+ tcet->mig_nb_table = tcet->nb_table;
+
+ trace_spapr_iommu_pre_save(tcet->liobn, tcet->mig_nb_table,
+ tcet->bus_offset, tcet->page_shift);
+}
+
static int spapr_tce_table_post_load(void *opaque, int version_id)
{
sPAPRTCETable *tcet = SPAPR_TCE_TABLE(opaque);
+ uint32_t old_nb_table = tcet->nb_table;
+ uint64_t old_bus_offset = tcet->bus_offset;
+ uint32_t old_page_shift = tcet->page_shift;
if (tcet->vdev) {
spapr_vio_set_bypass(tcet->vdev, tcet->bypass);
}
+ if (tcet->mig_nb_table != tcet->nb_table) {
+ spapr_tce_table_disable(tcet);
+ }
+
+ if (tcet->mig_nb_table) {
+ if (!tcet->nb_table) {
+ spapr_tce_table_enable(tcet, old_page_shift, old_bus_offset,
+ tcet->mig_nb_table);
+ }
+
+ memcpy(tcet->table, tcet->mig_table,
+ tcet->nb_table * sizeof(tcet->table[0]));
+
+ free(tcet->mig_table);
+ tcet->mig_table = NULL;
+ }
+
+ trace_spapr_iommu_post_load(tcet->liobn, old_nb_table, tcet->nb_table,
+ tcet->bus_offset, tcet->page_shift);
+
return 0;
}
+static bool spapr_tce_table_ex_needed(void *opaque)
+{
+ sPAPRTCETable *tcet = opaque;
+
+ return tcet->bus_offset || tcet->page_shift != 0xC;
+}
+
+static const VMStateDescription vmstate_spapr_tce_table_ex = {
+ .name = "spapr_iommu_ex",
+ .version_id = 1,
+ .minimum_version_id = 1,
+ .needed = spapr_tce_table_ex_needed,
+ .fields = (VMStateField[]) {
+ VMSTATE_UINT64(bus_offset, sPAPRTCETable),
+ VMSTATE_UINT32(page_shift, sPAPRTCETable),
+ VMSTATE_END_OF_LIST()
+ },
+};
+
static const VMStateDescription vmstate_spapr_tce_table = {
.name = "spapr_iommu",
.version_id = 2,
.minimum_version_id = 2,
+ .pre_save = spapr_tce_table_pre_save,
.post_load = spapr_tce_table_post_load,
.fields = (VMStateField []) {
/* Sanity check */
VMSTATE_UINT32_EQUAL(liobn, sPAPRTCETable),
- VMSTATE_UINT32_EQUAL(nb_table, sPAPRTCETable),
/* IOMMU state */
+ VMSTATE_UINT32(mig_nb_table, sPAPRTCETable),
VMSTATE_BOOL(bypass, sPAPRTCETable),
- VMSTATE_VARRAY_UINT32(table, sPAPRTCETable, nb_table, 0, vmstate_info_uint64, uint64_t),
+ VMSTATE_VARRAY_UINT32_ALLOC(mig_table, sPAPRTCETable, mig_nb_table, 0,
+ vmstate_info_uint64, uint64_t),
VMSTATE_END_OF_LIST()
},
+ .subsections = (const VMStateDescription*[]) {
+ &vmstate_spapr_tce_table_ex,
+ NULL
+ }
};
static MemoryRegionIOMMUOps spapr_iommu_ops = {
@@ -173,17 +233,16 @@ static MemoryRegionIOMMUOps spapr_iommu_ops = {
static int spapr_tce_table_realize(DeviceState *dev)
{
sPAPRTCETable *tcet = SPAPR_TCE_TABLE(dev);
+ Object *tcetobj = OBJECT(tcet);
+ char tmp[32];
tcet->fd = -1;
- tcet->table = spapr_tce_alloc_table(tcet->liobn,
- tcet->page_shift,
- tcet->nb_table,
- &tcet->fd,
- tcet->need_vfio);
+ tcet->need_vfio = false;
+ snprintf(tmp, sizeof(tmp), "tce-root-%x", tcet->liobn);
+ memory_region_init(&tcet->root, tcetobj, tmp, UINT64_MAX);
- memory_region_init_iommu(&tcet->iommu, OBJECT(dev), &spapr_iommu_ops,
- "iommu-spapr",
- (uint64_t)tcet->nb_table << tcet->page_shift);
+ snprintf(tmp, sizeof(tmp), "tce-iommu-%x", tcet->liobn);
+ memory_region_init_iommu(&tcet->iommu, tcetobj, &spapr_iommu_ops, tmp, 0);
QLIST_INSERT_HEAD(&spapr_tce_tables, tcet, list);
@@ -225,14 +284,10 @@ void spapr_tce_set_need_vfio(sPAPRTCETable *tcet, bool need_vfio)
tcet->table = newtable;
}
-sPAPRTCETable *spapr_tce_new_table(DeviceState *owner, uint32_t liobn,
- uint64_t bus_offset,
- uint32_t page_shift,
- uint32_t nb_table,
- bool need_vfio)
+sPAPRTCETable *spapr_tce_new_table(DeviceState *owner, uint32_t liobn)
{
sPAPRTCETable *tcet;
- char tmp[64];
+ char tmp[32];
if (spapr_tce_find_by_liobn(liobn)) {
fprintf(stderr, "Attempted to create TCE table with duplicate"
@@ -240,16 +295,8 @@ sPAPRTCETable *spapr_tce_new_table(DeviceState *owner, uint32_t liobn,
return NULL;
}
- if (!nb_table) {
- return NULL;
- }
-
tcet = SPAPR_TCE_TABLE(object_new(TYPE_SPAPR_TCE_TABLE));
tcet->liobn = liobn;
- tcet->bus_offset = bus_offset;
- tcet->page_shift = page_shift;
- tcet->nb_table = nb_table;
- tcet->need_vfio = need_vfio;
snprintf(tmp, sizeof(tmp), "tce-table-%x", liobn);
object_property_add_child(OBJECT(owner), tmp, OBJECT(tcet), NULL);
@@ -259,19 +306,58 @@ sPAPRTCETable *spapr_tce_new_table(DeviceState *owner, uint32_t liobn,
return tcet;
}
+void spapr_tce_table_enable(sPAPRTCETable *tcet,
+ uint32_t page_shift, uint64_t bus_offset,
+ uint32_t nb_table)
+{
+ if (tcet->nb_table) {
+ error_report("Warning: trying to enable already enabled TCE table");
+ return;
+ }
+
+ tcet->bus_offset = bus_offset;
+ tcet->page_shift = page_shift;
+ tcet->nb_table = nb_table;
+ tcet->table = spapr_tce_alloc_table(tcet->liobn,
+ tcet->page_shift,
+ tcet->nb_table,
+ &tcet->fd,
+ tcet->need_vfio);
+
+ memory_region_set_size(&tcet->iommu,
+ (uint64_t)tcet->nb_table << tcet->page_shift);
+ memory_region_add_subregion(&tcet->root, tcet->bus_offset, &tcet->iommu);
+}
+
+void spapr_tce_table_disable(sPAPRTCETable *tcet)
+{
+ if (!tcet->nb_table) {
+ return;
+ }
+
+ memory_region_del_subregion(&tcet->root, &tcet->iommu);
+ memory_region_set_size(&tcet->iommu, 0);
+
+ spapr_tce_free_table(tcet->table, tcet->fd, tcet->nb_table);
+ tcet->fd = -1;
+ tcet->table = NULL;
+ tcet->bus_offset = 0;
+ tcet->page_shift = 0;
+ tcet->nb_table = 0;
+}
+
static void spapr_tce_table_unrealize(DeviceState *dev, Error **errp)
{
sPAPRTCETable *tcet = SPAPR_TCE_TABLE(dev);
QLIST_REMOVE(tcet, list);
- spapr_tce_free_table(tcet->table, tcet->fd, tcet->nb_table);
- tcet->fd = -1;
+ spapr_tce_table_disable(tcet);
}
MemoryRegion *spapr_tce_get_iommu(sPAPRTCETable *tcet)
{
- return &tcet->iommu;
+ return &tcet->root;
}
static void spapr_tce_reset(DeviceState *dev)