259
vtd_flush_host_piotlb_locked, &piotlb_info);
260
}
261
262
+static void vtd_accel_fill_pc(VTDHostIOMMUDevice *vtd_hiod, uint32_t pasid,
263
+ VTDPASIDEntry *pe)
264
+{
265
+ VTDAccelPASIDCacheEntry *vtd_pce;
266
+
267
+ QLIST_FOREACH(vtd_pce, &vtd_hiod->pasid_cache_list, next) {
268
+ if (vtd_pce->pasid == pasid) {
269
+ if (vtd_pasid_entry_compare(pe, &vtd_pce->pasid_entry)) {
270
+ vtd_pce->pasid_entry = *pe;
271
+ }
272
+ return;
273
+ }
274
+ }
275
+
276
+ vtd_pce = g_malloc0(sizeof(VTDAccelPASIDCacheEntry));
277
+ vtd_pce->vtd_hiod = vtd_hiod;
278
+ vtd_pce->pasid = pasid;
279
+ vtd_pce->pasid_entry = *pe;
280
+ QLIST_INSERT_HEAD(&vtd_hiod->pasid_cache_list, vtd_pce, next);
281
+}
282
+
283
+/*
284
+ * This function walks over PASID range within [start, end) in a single
285
+ * PASID table for entries matching @info type/did, then create
286
+ * VTDAccelPASIDCacheEntry if not exist yet.
287
+ */
288
+static void vtd_sm_pasid_table_walk_one(VTDHostIOMMUDevice *vtd_hiod,
289
+ dma_addr_t pt_base, int start, int end,
290
+ VTDPASIDCacheInfo *info)
291
+{
292
+ IntelIOMMUState *s = vtd_hiod->iommu_state;
293
+ VTDPASIDEntry pe;
294
+ int pasid;
295
+
296
+ for (pasid = start; pasid < end; pasid++) {
297
+ if (vtd_get_pe_in_pasid_leaf_table(s, pasid, pt_base, &pe) ||
298
+ !vtd_pe_present(&pe)) {
299
+ continue;
300
+ }
301
+
302
+ if ((info->type == VTD_INV_DESC_PASIDC_G_DSI ||
303
+ info->type == VTD_INV_DESC_PASIDC_G_PASID_SI) &&
304
+ (info->did != VTD_SM_PASID_ENTRY_DID(&pe))) {
305
+ /*
306
+ * VTD_PASID_CACHE_DOMSI and VTD_PASID_CACHE_PASIDSI
307
+ * requires domain id check. If domain id check fail,
308
+ * go to next pasid.
309
+ */
310
+ continue;
311
+ }
312
+
313
+ vtd_accel_fill_pc(vtd_hiod, pasid, &pe);
314
+ }
315
+}
316
+
317
+/*
318
+ * In VT-d scalable mode translation, PASID dir + PASID table is used.
319
+ * This function aims at looping over a range of PASIDs in the given
320
+ * two level table to identify the pasid config in guest.
321
+ */
322
+static void vtd_sm_pasid_table_walk(VTDHostIOMMUDevice *vtd_hiod,
323
+ dma_addr_t pdt_base, int start, int end,
324
+ VTDPASIDCacheInfo *info)
325
+{
326
+ VTDPASIDDirEntry pdire;
327
+ int pasid = start;
328
+ int pasid_next;
329
+ dma_addr_t pt_base;
330
+
331
+ while (pasid < end) {
332
+ pasid_next = (pasid + VTD_PASID_TABLE_ENTRY_NUM) &
333
+ ~(VTD_PASID_TABLE_ENTRY_NUM - 1);
334
+ pasid_next = pasid_next < end ? pasid_next : end;
335
+
336
+ if (!vtd_get_pdire_from_pdir_table(pdt_base, pasid, &pdire)
337
+ && vtd_pdire_present(&pdire)) {
338
+ pt_base = pdire.val & VTD_PASID_TABLE_BASE_ADDR_MASK;
339
+ vtd_sm_pasid_table_walk_one(vtd_hiod, pt_base, pasid, pasid_next,
340
+ info);
341
+ }
342
+ pasid = pasid_next;
343
+ }
344
+}
345
+
346
+static void vtd_accel_replay_pasid_bind_for_dev(VTDHostIOMMUDevice *vtd_hiod,
347
+ int start, int end,
348
+ VTDPASIDCacheInfo *pc_info)
349
+{
350
+ IntelIOMMUState *s = vtd_hiod->iommu_state;
351
+ VTDContextEntry ce;
352
+ int dev_max_pasid = 1 << vtd_hiod->hiod->caps.max_pasid_log2;
353
+
354
+ if (!vtd_dev_to_context_entry(s, pci_bus_num(vtd_hiod->bus),
355
+ vtd_hiod->devfn, &ce)) {
356
+ VTDPASIDCacheInfo walk_info = *pc_info;
357
+ uint32_t ce_max_pasid = vtd_sm_ce_get_pdt_entry_num(&ce) *
358
+ VTD_PASID_TABLE_ENTRY_NUM;
359
+
360
+ end = MIN(end, MIN(dev_max_pasid, ce_max_pasid));
361
+
362
+ vtd_sm_pasid_table_walk(vtd_hiod, VTD_CE_GET_PASID_DIR_TABLE(&ce),
363
+ start, end, &walk_info);
364
+ }
365
+}
366
+
367
+/*
368
+ * This function replays the guest pasid bindings by walking the two level
369
+ * guest PASID table. For each valid pasid entry, it creates an entry
370
+ * VTDAccelPASIDCacheEntry dynamically if not exist yet. This entry holds
371
+ * info specific to a pasid
372
+ */
373
+void vtd_accel_pasid_cache_sync(IntelIOMMUState *s, VTDPASIDCacheInfo *pc_info)
374
+{
375
+ int start = IOMMU_NO_PASID, end = 1 << s->pasid;
376
+ VTDHostIOMMUDevice *vtd_hiod;
377
+ GHashTableIter hiod_it;
378
+
379
+ if (!s->fsts) {
380
+ return;
381
+ }
382
+
383
+ switch (pc_info->type) {
384
+ case VTD_INV_DESC_PASIDC_G_PASID_SI:
385
+ start = pc_info->pasid;
386
+ end = pc_info->pasid + 1;
387
+ /* fall through */
388
+ case VTD_INV_DESC_PASIDC_G_DSI:
389
+ /*
390
+ * loop all assigned devices, do domain id check in
391
+ * vtd_sm_pasid_table_walk_one() after get pasid entry.
392
+ */
393
+ break;
394
+ case VTD_INV_DESC_PASIDC_G_GLOBAL:
395
+ /* loop all assigned devices */
396
+ break;
397
+ default:
398
+ g_assert_not_reached();
399
+ }
400
+
401
+ /*
402
+ * Loop all the vtd_hiod instances to sync the "pasid cache" per the
403
+ * guest pasid configuration.
404
+ *
405
+ * VTD translation callback never accesses vtd_hiod and its corresponding
406
+ * cached pasid entry, so no iommu lock needed here.
407
+ */
408
+ g_hash_table_iter_init(&hiod_it, s->vtd_host_iommu_dev);
409
+ while (g_hash_table_iter_next(&hiod_it, NULL, (void **)&vtd_hiod)) {
410
+ if (!object_dynamic_cast(OBJECT(vtd_hiod->hiod),
411
+ TYPE_HOST_IOMMU_DEVICE_IOMMUFD)) {
412
+ continue;
413
+ }
414
+ vtd_accel_replay_pasid_bind_for_dev(vtd_hiod, start, end, pc_info);
415
+ }
416
+}
417
+
418
static uint64_t vtd_get_host_iommu_quirks(uint32_t type,
419
void *caps, uint32_t size)
420
{