Virtual Command Support (VCS) - Virtual register intended to help support virtualization of the IOMMU. Unlike an SR-IOV device where an entire device is exposed to a guest, the new model creates device instances using PASID. This requires the PASID to be a flat global space which requires the guest and host PASIDs to be the same. Only virtual IOMMUs exposed to a guest would enumerate this capability. It provides an interface to for the host to control allocation of PASIDs in a guest OS.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
/* Virtual command interface for enlightened pasid management. */
#define VCMD_CMD_ALLOC 0x1
#define VCMD_CMD_FREE 0x2
#define VCMD_VRSP_IP 0x1
#define VCMD_VRSP_SC(e) (((e) & 0xff) >> 1)
#define VCMD_VRSP_SC_SUCCESS 0
#define VCMD_VRSP_SC_NO_PASID_AVAIL 16
#define VCMD_VRSP_SC_INVALID_PASID 16
#define VCMD_VRSP_RESULT_PASID(e) (((e) >> 16) & 0xfffff)
#define VCMD_CMD_OPERAND(e) ((e) << 16)

int vcmd_alloc_pasid(struct intel_iommu *iommu, u32 *pasid)
{
unsigned long flags;
u8 status_code;
int ret = 0;
u64 res;

raw_spin_lock_irqsave(&iommu->register_lock, flags);
dmar_writeq(iommu->reg + DMAR_VCMD_REG, VCMD_CMD_ALLOC);
IOMMU_WAIT_OP(iommu, DMAR_VCRSP_REG, dmar_readq,
!(res & VCMD_VRSP_IP), res);
raw_spin_unlock_irqrestore(&iommu->register_lock, flags);

status_code = VCMD_VRSP_SC(res);
switch (status_code) {
case VCMD_VRSP_SC_SUCCESS:
*pasid = VCMD_VRSP_RESULT_PASID(res);
break;
case VCMD_VRSP_SC_NO_PASID_AVAIL:
pr_info("IOMMU: %s: No PASID available\n", iommu->name);
ret = -ENOSPC;
break;
default:
ret = -ENODEV;
pr_warn("IOMMU: %s: Unexpected error code %d\n",
iommu->name, status_code);
}

return ret;
}

void vcmd_free_pasid(struct intel_iommu *iommu, u32 pasid)
{
unsigned long flags;
u8 status_code;
u64 res;

raw_spin_lock_irqsave(&iommu->register_lock, flags);
dmar_writeq(iommu->reg + DMAR_VCMD_REG,
VCMD_CMD_OPERAND(pasid) | VCMD_CMD_FREE);
IOMMU_WAIT_OP(iommu, DMAR_VCRSP_REG, dmar_readq,
!(res & VCMD_VRSP_IP), res);
raw_spin_unlock_irqrestore(&iommu->register_lock, flags);

status_code = VCMD_VRSP_SC(res);
switch (status_code) {
case VCMD_VRSP_SC_SUCCESS:
break;
case VCMD_VRSP_SC_INVALID_PASID:
pr_info("IOMMU: %s: Invalid PASID\n", iommu->name);
break;
default:
pr_warn("IOMMU: %s: Unexpected error code %d\n",
iommu->name, status_code);
}
}

流程主要分为如下两个步骤:

  1. guest分配pasid时,写vIOMMU的vcmd寄存器,此时会trap下来,host会将分配好的host paisd传给guest,这样guest与host的pasid就一样了。

  2. 当guest配置WQ Configuration register(MMIO寄存器)的PASID field时需要trap下来,hypervisor会检查guest的pasid与host的pasid是否一致,如果一致,那么hypervisor会将这个host PASID写入物理WQ Configuration register的PASID field。


参考资料:

  1. RECENT ENHANCEMENTS IN INTEL® VIRTUALIZATION TECHNOLOGY FOR DIRECTED I/O (INTEL® VT-D)
  2. Linux kernel v6.3
  3. Intel VT-d spec