SPAPR_TCE_RW = 3,
};
-QLIST_HEAD(spapr_tce_tables, sPAPRTCETable) spapr_tce_tables;
+#define IOMMU_PAGE_SIZE(shift) (1ULL << (shift))
+#define IOMMU_PAGE_MASK(shift) (~(IOMMU_PAGE_SIZE(shift) - 1))
+
+static QLIST_HEAD(spapr_tce_tables, sPAPRTCETable) spapr_tce_tables;
static sPAPRTCETable *spapr_tce_find_by_liobn(uint32_t liobn)
{
if (tcet->bypass) {
ret.perm = IOMMU_RW;
- } else if (addr < tcet->window_size) {
+ } else if ((addr >> tcet->page_shift) < tcet->nb_table) {
/* Check if we are in bound */
- tce = tcet->table[addr >> SPAPR_TCE_PAGE_SHIFT];
- ret.iova = addr & ~SPAPR_TCE_PAGE_MASK;
- ret.translated_addr = tce & ~SPAPR_TCE_PAGE_MASK;
- ret.addr_mask = SPAPR_TCE_PAGE_MASK;
- ret.perm = tce;
+ hwaddr page_mask = IOMMU_PAGE_MASK(tcet->page_shift);
+
+ tce = tcet->table[addr >> tcet->page_shift];
+ ret.iova = addr & page_mask;
+ ret.translated_addr = tce & page_mask;
+ ret.addr_mask = ~page_mask;
+ ret.perm = tce & IOMMU_RW;
}
trace_spapr_iommu_xlate(tcet->liobn, addr, ret.iova, ret.perm,
ret.addr_mask);
return ret;
}
-static int spapr_tce_table_pre_load(void *opaque)
-{
- sPAPRTCETable *tcet = SPAPR_TCE_TABLE(opaque);
-
- tcet->nb_table = tcet->window_size >> SPAPR_TCE_PAGE_SHIFT;
-
- return 0;
-}
-
static const VMStateDescription vmstate_spapr_tce_table = {
.name = "spapr_iommu",
- .version_id = 1,
- .minimum_version_id = 1,
- .minimum_version_id_old = 1,
- .pre_load = spapr_tce_table_pre_load,
+ .version_id = 2,
+ .minimum_version_id = 2,
.fields = (VMStateField []) {
/* Sanity check */
VMSTATE_UINT32_EQUAL(liobn, sPAPRTCETable),
- VMSTATE_UINT32_EQUAL(window_size, sPAPRTCETable),
+ VMSTATE_UINT32_EQUAL(nb_table, sPAPRTCETable),
/* IOMMU state */
VMSTATE_BOOL(bypass, sPAPRTCETable),
if (kvm_enabled()) {
tcet->table = kvmppc_create_spapr_tce(tcet->liobn,
- tcet->window_size,
- &tcet->fd);
+ tcet->nb_table <<
+ tcet->page_shift,
+ &tcet->fd,
+ tcet->vfio_accel);
}
if (!tcet->table) {
- size_t table_size = (tcet->window_size >> SPAPR_TCE_PAGE_SHIFT)
- * sizeof(uint64_t);
+ size_t table_size = tcet->nb_table * sizeof(uint64_t);
tcet->table = g_malloc0(table_size);
}
- tcet->nb_table = tcet->window_size >> SPAPR_TCE_PAGE_SHIFT;
trace_spapr_iommu_new_table(tcet->liobn, tcet, tcet->table, tcet->fd);
memory_region_init_iommu(&tcet->iommu, OBJECT(dev), &spapr_iommu_ops,
- "iommu-spapr", UINT64_MAX);
+ "iommu-spapr", ram_size);
QLIST_INSERT_HEAD(&spapr_tce_tables, tcet, list);
+ vmstate_register(DEVICE(tcet), tcet->liobn, &vmstate_spapr_tce_table,
+ tcet);
+
return 0;
}
-sPAPRTCETable *spapr_tce_new_table(DeviceState *owner, uint32_t liobn, size_t window_size)
+sPAPRTCETable *spapr_tce_new_table(DeviceState *owner, uint32_t liobn,
+ uint64_t bus_offset,
+ uint32_t page_shift,
+ uint32_t nb_table,
+ bool vfio_accel)
{
sPAPRTCETable *tcet;
return NULL;
}
- if (!window_size) {
+ if (!nb_table) {
return NULL;
}
tcet = SPAPR_TCE_TABLE(object_new(TYPE_SPAPR_TCE_TABLE));
tcet->liobn = liobn;
- tcet->window_size = window_size;
+ tcet->bus_offset = bus_offset;
+ tcet->page_shift = page_shift;
+ tcet->nb_table = nb_table;
+ tcet->vfio_accel = vfio_accel;
object_property_add_child(OBJECT(owner), "tce-table", OBJECT(tcet), NULL);
- qdev_init_nofail(DEVICE(tcet));
+ object_property_set_bool(OBJECT(tcet), true, "realized", NULL);
return tcet;
}
if (!kvm_enabled() ||
(kvmppc_remove_spapr_tce(tcet->table, tcet->fd,
- tcet->window_size) != 0)) {
+ tcet->nb_table) != 0)) {
g_free(tcet->table);
}
}
static void spapr_tce_reset(DeviceState *dev)
{
sPAPRTCETable *tcet = SPAPR_TCE_TABLE(dev);
- size_t table_size = (tcet->window_size >> SPAPR_TCE_PAGE_SHIFT)
- * sizeof(uint64_t);
+ size_t table_size = tcet->nb_table * sizeof(uint64_t);
tcet->bypass = false;
memset(tcet->table, 0, table_size);
target_ulong tce)
{
IOMMUTLBEntry entry;
+ hwaddr page_mask = IOMMU_PAGE_MASK(tcet->page_shift);
+ unsigned long index = (ioba - tcet->bus_offset) >> tcet->page_shift;
- if (ioba >= tcet->window_size) {
+ if (index >= tcet->nb_table) {
hcall_dprintf("spapr_vio_put_tce on out-of-bounds IOBA 0x"
TARGET_FMT_lx "\n", ioba);
return H_PARAMETER;
}
- tcet->table[ioba >> SPAPR_TCE_PAGE_SHIFT] = tce;
+ tcet->table[index] = tce;
entry.target_as = &address_space_memory,
- entry.iova = ioba & ~SPAPR_TCE_PAGE_MASK;
- entry.translated_addr = tce & ~SPAPR_TCE_PAGE_MASK;
- entry.addr_mask = SPAPR_TCE_PAGE_MASK;
- entry.perm = tce;
+ entry.iova = ioba & page_mask;
+ entry.translated_addr = tce & page_mask;
+ entry.addr_mask = ~page_mask;
+ entry.perm = tce & IOMMU_RW;
memory_region_notify_iommu(&tcet->iommu, entry);
return H_SUCCESS;
}
+static target_ulong h_put_tce_indirect(PowerPCCPU *cpu,
+ sPAPREnvironment *spapr,
+ target_ulong opcode, target_ulong *args)
+{
+ int i;
+ target_ulong liobn = args[0];
+ target_ulong ioba = args[1];
+ target_ulong ioba1 = ioba;
+ target_ulong tce_list = args[2];
+ target_ulong npages = args[3];
+ target_ulong ret = H_PARAMETER;
+ sPAPRTCETable *tcet = spapr_tce_find_by_liobn(liobn);
+ CPUState *cs = CPU(cpu);
+ hwaddr page_mask, page_size;
+
+ if (!tcet) {
+ return H_PARAMETER;
+ }
+
+ if ((npages > 512) || (tce_list & SPAPR_TCE_PAGE_MASK)) {
+ return H_PARAMETER;
+ }
+
+ page_mask = IOMMU_PAGE_MASK(tcet->page_shift);
+ page_size = IOMMU_PAGE_SIZE(tcet->page_shift);
+ ioba &= page_mask;
+
+ for (i = 0; i < npages; ++i, ioba += page_size) {
+ target_ulong off = (tce_list & ~SPAPR_TCE_RW) +
+ i * sizeof(target_ulong);
+ target_ulong tce = ldq_phys(cs->as, off);
+
+ ret = put_tce_emu(tcet, ioba, tce);
+ if (ret) {
+ break;
+ }
+ }
+
+ /* Trace last successful or the first problematic entry */
+ i = i ? (i - 1) : 0;
+ trace_spapr_iommu_indirect(liobn, ioba1, tce_list, i,
+ ldq_phys(cs->as,
+ tce_list + i * sizeof(target_ulong)),
+ ret);
+
+ return ret;
+}
+
+static target_ulong h_stuff_tce(PowerPCCPU *cpu, sPAPREnvironment *spapr,
+ target_ulong opcode, target_ulong *args)
+{
+ int i;
+ target_ulong liobn = args[0];
+ target_ulong ioba = args[1];
+ target_ulong tce_value = args[2];
+ target_ulong npages = args[3];
+ target_ulong ret = H_PARAMETER;
+ sPAPRTCETable *tcet = spapr_tce_find_by_liobn(liobn);
+ hwaddr page_mask, page_size;
+
+ if (!tcet) {
+ return H_PARAMETER;
+ }
+
+ if (npages > tcet->nb_table) {
+ return H_PARAMETER;
+ }
+
+ page_mask = IOMMU_PAGE_MASK(tcet->page_shift);
+ page_size = IOMMU_PAGE_SIZE(tcet->page_shift);
+ ioba &= page_mask;
+
+ for (i = 0; i < npages; ++i, ioba += page_size) {
+ ret = put_tce_emu(tcet, ioba, tce_value);
+ if (ret) {
+ break;
+ }
+ }
+ trace_spapr_iommu_stuff(liobn, ioba, tce_value, npages, ret);
+
+ return ret;
+}
+
static target_ulong h_put_tce(PowerPCCPU *cpu, sPAPREnvironment *spapr,
target_ulong opcode, target_ulong *args)
{
target_ulong ret = H_PARAMETER;
sPAPRTCETable *tcet = spapr_tce_find_by_liobn(liobn);
- ioba &= ~(SPAPR_TCE_PAGE_SIZE - 1);
-
if (tcet) {
+ hwaddr page_mask = IOMMU_PAGE_MASK(tcet->page_shift);
+
+ ioba &= page_mask;
+
ret = put_tce_emu(tcet, ioba, tce);
}
trace_spapr_iommu_put(liobn, ioba, tce, ret);
static target_ulong get_tce_emu(sPAPRTCETable *tcet, target_ulong ioba,
target_ulong *tce)
{
- if (ioba >= tcet->window_size) {
+ unsigned long index = (ioba - tcet->bus_offset) >> tcet->page_shift;
+
+ if (index >= tcet->nb_table) {
hcall_dprintf("spapr_iommu_get_tce on out-of-bounds IOBA 0x"
TARGET_FMT_lx "\n", ioba);
return H_PARAMETER;
}
- *tce = tcet->table[ioba >> SPAPR_TCE_PAGE_SHIFT];
+ *tce = tcet->table[index];
return H_SUCCESS;
}
target_ulong ret = H_PARAMETER;
sPAPRTCETable *tcet = spapr_tce_find_by_liobn(liobn);
- ioba &= ~(SPAPR_TCE_PAGE_SIZE - 1);
-
if (tcet) {
+ hwaddr page_mask = IOMMU_PAGE_MASK(tcet->page_shift);
+
+ ioba &= page_mask;
+
ret = get_tce_emu(tcet, ioba, &tce);
if (!ret) {
args[0] = tce;
}
return spapr_dma_dt(fdt, node_off, propname,
- tcet->liobn, 0, tcet->window_size);
+ tcet->liobn, 0, tcet->nb_table << tcet->page_shift);
}
static void spapr_tce_table_class_init(ObjectClass *klass, void *data)
{
DeviceClass *dc = DEVICE_CLASS(klass);
- dc->vmsd = &vmstate_spapr_tce_table;
dc->init = spapr_tce_table_realize;
dc->reset = spapr_tce_reset;
/* hcall-tce */
spapr_register_hypercall(H_PUT_TCE, h_put_tce);
spapr_register_hypercall(H_GET_TCE, h_get_tce);
+ spapr_register_hypercall(H_PUT_TCE_INDIRECT, h_put_tce_indirect);
+ spapr_register_hypercall(H_STUFF_TCE, h_stuff_tce);
}
static TypeInfo spapr_tce_table_info = {