in vfio_iommu_spapr_tce.c [644:707]
static long tce_iommu_create_window(struct tce_container *container,
__u32 page_shift, __u64 window_size, __u32 levels,
__u64 *start_addr)
{
struct tce_iommu_group *tcegrp;
struct iommu_table_group *table_group;
struct iommu_table *tbl = NULL;
long ret, num;
num = tce_iommu_find_free_table(container);
if (num < 0)
return num;
/* Get the first group for ops::create_table */
tcegrp = list_first_entry(&container->group_list,
struct tce_iommu_group, next);
table_group = iommu_group_get_iommudata(tcegrp->grp);
if (!table_group)
return -EFAULT;
if (!(table_group->pgsizes & (1ULL << page_shift)))
return -EINVAL;
if (!table_group->ops->set_window || !table_group->ops->unset_window ||
!table_group->ops->get_table_size ||
!table_group->ops->create_table)
return -EPERM;
/* Create TCE table */
ret = tce_iommu_create_table(container, table_group, num,
page_shift, window_size, levels, &tbl);
if (ret)
return ret;
BUG_ON(!tbl->it_ops->free);
/*
* Program the table to every group.
* Groups have been tested for compatibility at the attach time.
*/
list_for_each_entry(tcegrp, &container->group_list, next) {
table_group = iommu_group_get_iommudata(tcegrp->grp);
ret = table_group->ops->set_window(table_group, num, tbl);
if (ret)
goto unset_exit;
}
container->tables[num] = tbl;
/* Return start address assigned by platform in create_table() */
*start_addr = tbl->it_offset << tbl->it_page_shift;
return 0;
unset_exit:
list_for_each_entry(tcegrp, &container->group_list, next) {
table_group = iommu_group_get_iommudata(tcegrp->grp);
table_group->ops->unset_window(table_group, num);
}
tce_iommu_free_table(container, tbl);
return ret;
}