static uint64_t *spapr_tce_alloc_table(uint32_t liobn,
uint32_t page_shift,
+ uint64_t bus_offset,
uint32_t nb_table,
int *fd,
bool need_vfio)
{
uint64_t *table = NULL;
- uint64_t window_size = (uint64_t)nb_table << page_shift;
- if (kvm_enabled() && !(window_size >> 32)) {
- table = kvmppc_create_spapr_tce(liobn, window_size, fd, need_vfio);
+ if (kvm_enabled()) {
+ table = kvmppc_create_spapr_tce(liobn, page_shift, bus_offset, nb_table,
+ fd, need_vfio);
}
if (!table) {
}
/* Called from RCU critical section */
-static IOMMUTLBEntry spapr_tce_translate_iommu(MemoryRegion *iommu, hwaddr addr,
- bool is_write)
+static IOMMUTLBEntry spapr_tce_translate_iommu(IOMMUMemoryRegion *iommu,
+ hwaddr addr,
+ IOMMUAccessFlags flag)
{
sPAPRTCETable *tcet = container_of(iommu, sPAPRTCETable, iommu);
uint64_t tce;
return ret;
}
-static void spapr_tce_table_pre_save(void *opaque)
+static int spapr_tce_table_pre_save(void *opaque)
{
sPAPRTCETable *tcet = SPAPR_TCE_TABLE(opaque);
trace_spapr_iommu_pre_save(tcet->liobn, tcet->mig_nb_table,
tcet->bus_offset, tcet->page_shift);
+
+ return 0;
}
-static uint64_t spapr_tce_get_min_page_size(MemoryRegion *iommu)
+static uint64_t spapr_tce_get_min_page_size(IOMMUMemoryRegion *iommu)
{
sPAPRTCETable *tcet = container_of(iommu, sPAPRTCETable, iommu);
return 1ULL << tcet->page_shift;
}
-static void spapr_tce_notify_started(MemoryRegion *iommu)
+static int spapr_tce_get_attr(IOMMUMemoryRegion *iommu,
+ enum IOMMUMemoryRegionAttr attr, void *data)
{
- spapr_tce_set_need_vfio(container_of(iommu, sPAPRTCETable, iommu), true);
+ sPAPRTCETable *tcet = container_of(iommu, sPAPRTCETable, iommu);
+
+ if (attr == IOMMU_ATTR_SPAPR_TCE_FD && kvmppc_has_cap_spapr_vfio()) {
+ *(int *) data = tcet->fd;
+ return 0;
+ }
+
+ return -EINVAL;
}
-static void spapr_tce_notify_stopped(MemoryRegion *iommu)
+static void spapr_tce_notify_flag_changed(IOMMUMemoryRegion *iommu,
+ IOMMUNotifierFlag old,
+ IOMMUNotifierFlag new)
{
- spapr_tce_set_need_vfio(container_of(iommu, sPAPRTCETable, iommu), false);
+ struct sPAPRTCETable *tbl = container_of(iommu, sPAPRTCETable, iommu);
+
+ if (old == IOMMU_NOTIFIER_NONE && new != IOMMU_NOTIFIER_NONE) {
+ spapr_tce_set_need_vfio(tbl, true);
+ } else if (old != IOMMU_NOTIFIER_NONE && new == IOMMU_NOTIFIER_NONE) {
+ spapr_tce_set_need_vfio(tbl, false);
+ }
}
static int spapr_tce_table_post_load(void *opaque, int version_id)
.post_load = spapr_tce_table_post_load,
.fields = (VMStateField []) {
/* Sanity check */
- VMSTATE_UINT32_EQUAL(liobn, sPAPRTCETable),
+ VMSTATE_UINT32_EQUAL(liobn, sPAPRTCETable, NULL),
/* IOMMU state */
VMSTATE_UINT32(mig_nb_table, sPAPRTCETable),
}
};
-static MemoryRegionIOMMUOps spapr_iommu_ops = {
- .translate = spapr_tce_translate_iommu,
- .get_min_page_size = spapr_tce_get_min_page_size,
- .notify_started = spapr_tce_notify_started,
- .notify_stopped = spapr_tce_notify_stopped,
-};
-
-static int spapr_tce_table_realize(DeviceState *dev)
+static void spapr_tce_table_realize(DeviceState *dev, Error **errp)
{
sPAPRTCETable *tcet = SPAPR_TCE_TABLE(dev);
Object *tcetobj = OBJECT(tcet);
- char tmp[32];
+ gchar *tmp;
tcet->fd = -1;
tcet->need_vfio = false;
- snprintf(tmp, sizeof(tmp), "tce-root-%x", tcet->liobn);
+ tmp = g_strdup_printf("tce-root-%x", tcet->liobn);
memory_region_init(&tcet->root, tcetobj, tmp, UINT64_MAX);
+ g_free(tmp);
- snprintf(tmp, sizeof(tmp), "tce-iommu-%x", tcet->liobn);
- memory_region_init_iommu(&tcet->iommu, tcetobj, &spapr_iommu_ops, tmp, 0);
+ tmp = g_strdup_printf("tce-iommu-%x", tcet->liobn);
+ memory_region_init_iommu(&tcet->iommu, sizeof(tcet->iommu),
+ TYPE_SPAPR_IOMMU_MEMORY_REGION,
+ tcetobj, tmp, 0);
+ g_free(tmp);
QLIST_INSERT_HEAD(&spapr_tce_tables, tcet, list);
vmstate_register(DEVICE(tcet), tcet->liobn, &vmstate_spapr_tce_table,
tcet);
-
- return 0;
}
void spapr_tce_set_need_vfio(sPAPRTCETable *tcet, bool need_vfio)
{
size_t table_size = tcet->nb_table * sizeof(uint64_t);
- void *newtable;
-
- if (need_vfio == tcet->need_vfio) {
- /* Nothing to do */
- return;
- }
+ uint64_t *oldtable;
+ int newfd = -1;
- if (!need_vfio) {
- /* FIXME: We don't support transition back to KVM accelerated
- * TCEs yet */
- return;
- }
+ g_assert(need_vfio != tcet->need_vfio);
- tcet->need_vfio = true;
+ tcet->need_vfio = need_vfio;
- if (tcet->fd < 0) {
- /* Table is already in userspace, nothing to be do */
+ if (!need_vfio || (tcet->fd != -1 && kvmppc_has_cap_spapr_vfio())) {
return;
}
- newtable = g_malloc(table_size);
- memcpy(newtable, tcet->table, table_size);
+ oldtable = tcet->table;
+
+ tcet->table = spapr_tce_alloc_table(tcet->liobn,
+ tcet->page_shift,
+ tcet->bus_offset,
+ tcet->nb_table,
+ &newfd,
+ need_vfio);
+ memcpy(tcet->table, oldtable, table_size);
- kvmppc_remove_spapr_tce(tcet->table, tcet->fd, tcet->nb_table);
+ spapr_tce_free_table(oldtable, tcet->fd, tcet->nb_table);
- tcet->fd = -1;
- tcet->table = newtable;
+ tcet->fd = newfd;
}
sPAPRTCETable *spapr_tce_new_table(DeviceState *owner, uint32_t liobn)
{
sPAPRTCETable *tcet;
- char tmp[32];
+ gchar *tmp;
if (spapr_tce_find_by_liobn(liobn)) {
error_report("Attempted to create TCE table with duplicate"
tcet = SPAPR_TCE_TABLE(object_new(TYPE_SPAPR_TCE_TABLE));
tcet->liobn = liobn;
- snprintf(tmp, sizeof(tmp), "tce-table-%x", liobn);
+ tmp = g_strdup_printf("tce-table-%x", liobn);
object_property_add_child(OBJECT(owner), tmp, OBJECT(tcet), NULL);
+ g_free(tmp);
+ object_unref(OBJECT(tcet));
object_property_set_bool(OBJECT(tcet), true, "realized", NULL);
uint32_t nb_table)
{
if (tcet->nb_table) {
- error_report("Warning: trying to enable already enabled TCE table");
+ warn_report("trying to enable already enabled TCE table");
return;
}
tcet->nb_table = nb_table;
tcet->table = spapr_tce_alloc_table(tcet->liobn,
tcet->page_shift,
+ tcet->bus_offset,
tcet->nb_table,
&tcet->fd,
tcet->need_vfio);
- memory_region_set_size(&tcet->iommu,
+ memory_region_set_size(MEMORY_REGION(&tcet->iommu),
(uint64_t)tcet->nb_table << tcet->page_shift);
- memory_region_add_subregion(&tcet->root, tcet->bus_offset, &tcet->iommu);
+ memory_region_add_subregion(&tcet->root, tcet->bus_offset,
+ MEMORY_REGION(&tcet->iommu));
}
void spapr_tce_table_disable(sPAPRTCETable *tcet)
return;
}
- memory_region_del_subregion(&tcet->root, &tcet->iommu);
- memory_region_set_size(&tcet->iommu, 0);
+ memory_region_del_subregion(&tcet->root, MEMORY_REGION(&tcet->iommu));
+ memory_region_set_size(MEMORY_REGION(&tcet->iommu), 0);
spapr_tce_free_table(tcet->table, tcet->fd, tcet->nb_table);
tcet->fd = -1;
{
sPAPRTCETable *tcet = SPAPR_TCE_TABLE(dev);
+ vmstate_unregister(DEVICE(tcet), &vmstate_spapr_tce_table, tcet);
+
QLIST_REMOVE(tcet, list);
spapr_tce_table_disable(tcet);
static void spapr_tce_table_class_init(ObjectClass *klass, void *data)
{
DeviceClass *dc = DEVICE_CLASS(klass);
- dc->init = spapr_tce_table_realize;
+ dc->realize = spapr_tce_table_realize;
dc->reset = spapr_tce_reset;
dc->unrealize = spapr_tce_table_unrealize;
+ /* Reason: This is just an internal device for handling the hypercalls */
+ dc->user_creatable = false;
QLIST_INIT(&spapr_tce_tables);
.class_init = spapr_tce_table_class_init,
};
+static void spapr_iommu_memory_region_class_init(ObjectClass *klass, void *data)
+{
+ IOMMUMemoryRegionClass *imrc = IOMMU_MEMORY_REGION_CLASS(klass);
+
+ imrc->translate = spapr_tce_translate_iommu;
+ imrc->get_min_page_size = spapr_tce_get_min_page_size;
+ imrc->notify_flag_changed = spapr_tce_notify_flag_changed;
+ imrc->get_attr = spapr_tce_get_attr;
+}
+
+static const TypeInfo spapr_iommu_memory_region_info = {
+ .parent = TYPE_IOMMU_MEMORY_REGION,
+ .name = TYPE_SPAPR_IOMMU_MEMORY_REGION,
+ .class_init = spapr_iommu_memory_region_class_init,
+};
+
static void register_types(void)
{
type_register_static(&spapr_tce_table_info);
+ type_register_static(&spapr_iommu_memory_region_info);
}
type_init(register_types);