Skip to content

Commit 1e8be08

Browse files
committed
iommu/arm-smmu-v3: Support IOMMU_DOMAIN_NESTED
For SMMUv3 a IOMMU_DOMAIN_NESTED is composed of a S2 iommu_domain acting as the parent and a user provided STE fragment that defines the CD table and related data with addresses translated by the S2 iommu_domain. The kernel only permits userspace to control certain allowed bits of the STE that are safe for user/guest control. IOTLB maintenance is a bit subtle here, the S1 implicitly includes the S2 translation, but there is no way of knowing which S1 entries refer to a range of S2. For the IOTLB we follow ARM's guidance and issue a CMDQ_OP_TLBI_NH_ALL to flush all ASIDs from the VMID after flushing the S2 on any change to the S2. The IOMMU_DOMAIN_NESTED can only be created from inside a VIOMMU as the invalidation path relies on the VIOMMU to translate virtual stream ID used in the invalidation commands for the CD table and ATS. Link: https://patch.msgid.link/r/9-v4-9e99b76f3518+3a8-smmuv3_nesting_jgg@nvidia.com Reviewed-by: Nicolin Chen <nicolinc@nvidia.com> Reviewed-by: Kevin Tian <kevin.tian@intel.com> Reviewed-by: Jerry Snitselaar <jsnitsel@redhat.com> Reviewed-by: Donald Dutile <ddutile@redhat.com> Signed-off-by: Nicolin Chen <nicolinc@nvidia.com> Tested-by: Nicolin Chen <nicolinc@nvidia.com> Signed-off-by: Jason Gunthorpe <jgg@nvidia.com>
1 parent 69d9b31 commit 1e8be08

File tree

4 files changed

+225
-1
lines changed

4 files changed

+225
-1
lines changed

drivers/iommu/arm/arm-smmu-v3/arm-smmu-v3-iommufd.c

Lines changed: 163 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,170 @@ void *arm_smmu_hw_info(struct device *dev, u32 *length, u32 *type)
3030
return info;
3131
}
3232

33+
static void arm_smmu_make_nested_cd_table_ste(
34+
struct arm_smmu_ste *target, struct arm_smmu_master *master,
35+
struct arm_smmu_nested_domain *nested_domain, bool ats_enabled)
36+
{
37+
arm_smmu_make_s2_domain_ste(
38+
target, master, nested_domain->vsmmu->s2_parent, ats_enabled);
39+
40+
target->data[0] = cpu_to_le64(STRTAB_STE_0_V |
41+
FIELD_PREP(STRTAB_STE_0_CFG,
42+
STRTAB_STE_0_CFG_NESTED));
43+
target->data[0] |= nested_domain->ste[0] &
44+
~cpu_to_le64(STRTAB_STE_0_CFG);
45+
target->data[1] |= nested_domain->ste[1];
46+
}
47+
48+
/*
49+
* Create a physical STE from the virtual STE that userspace provided when it
50+
* created the nested domain. Using the vSTE userspace can request:
51+
* - Non-valid STE
52+
* - Abort STE
53+
* - Bypass STE (install the S2, no CD table)
54+
* - CD table STE (install the S2 and the userspace CD table)
55+
*/
56+
static void arm_smmu_make_nested_domain_ste(
57+
struct arm_smmu_ste *target, struct arm_smmu_master *master,
58+
struct arm_smmu_nested_domain *nested_domain, bool ats_enabled)
59+
{
60+
unsigned int cfg =
61+
FIELD_GET(STRTAB_STE_0_CFG, le64_to_cpu(nested_domain->ste[0]));
62+
63+
/*
64+
* Userspace can request a non-valid STE through the nesting interface.
65+
* We relay that into an abort physical STE with the intention that
66+
* C_BAD_STE for this SID can be generated to userspace.
67+
*/
68+
if (!(nested_domain->ste[0] & cpu_to_le64(STRTAB_STE_0_V)))
69+
cfg = STRTAB_STE_0_CFG_ABORT;
70+
71+
switch (cfg) {
72+
case STRTAB_STE_0_CFG_S1_TRANS:
73+
arm_smmu_make_nested_cd_table_ste(target, master, nested_domain,
74+
ats_enabled);
75+
break;
76+
case STRTAB_STE_0_CFG_BYPASS:
77+
arm_smmu_make_s2_domain_ste(target, master,
78+
nested_domain->vsmmu->s2_parent,
79+
ats_enabled);
80+
break;
81+
case STRTAB_STE_0_CFG_ABORT:
82+
default:
83+
arm_smmu_make_abort_ste(target);
84+
break;
85+
}
86+
}
87+
88+
static int arm_smmu_attach_dev_nested(struct iommu_domain *domain,
89+
struct device *dev)
90+
{
91+
struct arm_smmu_nested_domain *nested_domain =
92+
to_smmu_nested_domain(domain);
93+
struct arm_smmu_master *master = dev_iommu_priv_get(dev);
94+
struct arm_smmu_attach_state state = {
95+
.master = master,
96+
.old_domain = iommu_get_domain_for_dev(dev),
97+
.ssid = IOMMU_NO_PASID,
98+
/* Currently invalidation of ATC is not supported */
99+
.disable_ats = true,
100+
};
101+
struct arm_smmu_ste ste;
102+
int ret;
103+
104+
if (nested_domain->vsmmu->smmu != master->smmu)
105+
return -EINVAL;
106+
if (arm_smmu_ssids_in_use(&master->cd_table))
107+
return -EBUSY;
108+
109+
mutex_lock(&arm_smmu_asid_lock);
110+
ret = arm_smmu_attach_prepare(&state, domain);
111+
if (ret) {
112+
mutex_unlock(&arm_smmu_asid_lock);
113+
return ret;
114+
}
115+
116+
arm_smmu_make_nested_domain_ste(&ste, master, nested_domain,
117+
state.ats_enabled);
118+
arm_smmu_install_ste_for_dev(master, &ste);
119+
arm_smmu_attach_commit(&state);
120+
mutex_unlock(&arm_smmu_asid_lock);
121+
return 0;
122+
}
123+
124+
static void arm_smmu_domain_nested_free(struct iommu_domain *domain)
125+
{
126+
kfree(to_smmu_nested_domain(domain));
127+
}
128+
129+
static const struct iommu_domain_ops arm_smmu_nested_ops = {
130+
.attach_dev = arm_smmu_attach_dev_nested,
131+
.free = arm_smmu_domain_nested_free,
132+
};
133+
134+
static int arm_smmu_validate_vste(struct iommu_hwpt_arm_smmuv3 *arg)
135+
{
136+
unsigned int cfg;
137+
138+
if (!(arg->ste[0] & cpu_to_le64(STRTAB_STE_0_V))) {
139+
memset(arg->ste, 0, sizeof(arg->ste));
140+
return 0;
141+
}
142+
143+
/* EIO is reserved for invalid STE data. */
144+
if ((arg->ste[0] & ~STRTAB_STE_0_NESTING_ALLOWED) ||
145+
(arg->ste[1] & ~STRTAB_STE_1_NESTING_ALLOWED))
146+
return -EIO;
147+
148+
cfg = FIELD_GET(STRTAB_STE_0_CFG, le64_to_cpu(arg->ste[0]));
149+
if (cfg != STRTAB_STE_0_CFG_ABORT && cfg != STRTAB_STE_0_CFG_BYPASS &&
150+
cfg != STRTAB_STE_0_CFG_S1_TRANS)
151+
return -EIO;
152+
return 0;
153+
}
154+
155+
static struct iommu_domain *
156+
arm_vsmmu_alloc_domain_nested(struct iommufd_viommu *viommu, u32 flags,
157+
const struct iommu_user_data *user_data)
158+
{
159+
struct arm_vsmmu *vsmmu = container_of(viommu, struct arm_vsmmu, core);
160+
const u32 SUPPORTED_FLAGS = IOMMU_HWPT_FAULT_ID_VALID;
161+
struct arm_smmu_nested_domain *nested_domain;
162+
struct iommu_hwpt_arm_smmuv3 arg;
163+
int ret;
164+
165+
/*
166+
* Faults delivered to the nested domain are faults that originated by
167+
* the S1 in the domain. The core code will match all PASIDs when
168+
* delivering the fault due to user_pasid_table
169+
*/
170+
if (flags & ~SUPPORTED_FLAGS)
171+
return ERR_PTR(-EOPNOTSUPP);
172+
173+
ret = iommu_copy_struct_from_user(&arg, user_data,
174+
IOMMU_HWPT_DATA_ARM_SMMUV3, ste);
175+
if (ret)
176+
return ERR_PTR(ret);
177+
178+
ret = arm_smmu_validate_vste(&arg);
179+
if (ret)
180+
return ERR_PTR(ret);
181+
182+
nested_domain = kzalloc(sizeof(*nested_domain), GFP_KERNEL_ACCOUNT);
183+
if (!nested_domain)
184+
return ERR_PTR(-ENOMEM);
185+
186+
nested_domain->domain.type = IOMMU_DOMAIN_NESTED;
187+
nested_domain->domain.ops = &arm_smmu_nested_ops;
188+
nested_domain->vsmmu = vsmmu;
189+
nested_domain->ste[0] = arg.ste[0];
190+
nested_domain->ste[1] = arg.ste[1] & ~cpu_to_le64(STRTAB_STE_1_EATS);
191+
192+
return &nested_domain->domain;
193+
}
194+
33195
static const struct iommufd_viommu_ops arm_vsmmu_ops = {
196+
.alloc_domain_nested = arm_vsmmu_alloc_domain_nested,
34197
};
35198

36199
struct iommufd_viommu *arm_vsmmu_alloc(struct device *dev,

drivers/iommu/arm/arm-smmu-v3/arm-smmu-v3.c

Lines changed: 16 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -295,6 +295,7 @@ static int arm_smmu_cmdq_build_cmd(u64 *cmd, struct arm_smmu_cmdq_ent *ent)
295295
case CMDQ_OP_TLBI_NH_ASID:
296296
cmd[0] |= FIELD_PREP(CMDQ_TLBI_0_ASID, ent->tlbi.asid);
297297
fallthrough;
298+
case CMDQ_OP_TLBI_NH_ALL:
298299
case CMDQ_OP_TLBI_S12_VMALL:
299300
cmd[0] |= FIELD_PREP(CMDQ_TLBI_0_VMID, ent->tlbi.vmid);
300301
break;
@@ -2230,6 +2231,15 @@ static void arm_smmu_tlb_inv_range_domain(unsigned long iova, size_t size,
22302231
}
22312232
__arm_smmu_tlb_inv_range(&cmd, iova, size, granule, smmu_domain);
22322233

2234+
if (smmu_domain->nest_parent) {
2235+
/*
2236+
* When the S2 domain changes all the nested S1 ASIDs have to be
2237+
* flushed too.
2238+
*/
2239+
cmd.opcode = CMDQ_OP_TLBI_NH_ALL;
2240+
arm_smmu_cmdq_issue_cmd_with_sync(smmu_domain->smmu, &cmd);
2241+
}
2242+
22332243
/*
22342244
* Unfortunately, this can't be leaf-only since we may have
22352245
* zapped an entire table.
@@ -2644,6 +2654,8 @@ to_smmu_domain_devices(struct iommu_domain *domain)
26442654
if ((domain->type & __IOMMU_DOMAIN_PAGING) ||
26452655
domain->type == IOMMU_DOMAIN_SVA)
26462656
return to_smmu_domain(domain);
2657+
if (domain->type == IOMMU_DOMAIN_NESTED)
2658+
return to_smmu_nested_domain(domain)->vsmmu->s2_parent;
26472659
return NULL;
26482660
}
26492661

@@ -2716,7 +2728,8 @@ int arm_smmu_attach_prepare(struct arm_smmu_attach_state *state,
27162728
* enabled if we have arm_smmu_domain, those always have page
27172729
* tables.
27182730
*/
2719-
state->ats_enabled = arm_smmu_ats_supported(master);
2731+
state->ats_enabled = !state->disable_ats &&
2732+
arm_smmu_ats_supported(master);
27202733
}
27212734

27222735
if (smmu_domain) {
@@ -3122,6 +3135,7 @@ arm_smmu_domain_alloc_user(struct device *dev, u32 flags,
31223135
goto err_free;
31233136
}
31243137
smmu_domain->stage = ARM_SMMU_DOMAIN_S2;
3138+
smmu_domain->nest_parent = true;
31253139
}
31263140

31273141
smmu_domain->domain.type = IOMMU_DOMAIN_UNMANAGED;
@@ -3518,6 +3532,7 @@ static struct iommu_ops arm_smmu_ops = {
35183532
.page_response = arm_smmu_page_response,
35193533
.def_domain_type = arm_smmu_def_domain_type,
35203534
.viommu_alloc = arm_vsmmu_alloc,
3535+
.user_pasid_table = 1,
35213536
.pgsize_bitmap = -1UL, /* Restricted during device attach */
35223537
.owner = THIS_MODULE,
35233538
.default_domain_ops = &(const struct iommu_domain_ops) {

drivers/iommu/arm/arm-smmu-v3/arm-smmu-v3.h

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -244,6 +244,7 @@ static inline u32 arm_smmu_strtab_l2_idx(u32 sid)
244244
#define STRTAB_STE_0_CFG_BYPASS 4
245245
#define STRTAB_STE_0_CFG_S1_TRANS 5
246246
#define STRTAB_STE_0_CFG_S2_TRANS 6
247+
#define STRTAB_STE_0_CFG_NESTED 7
247248

248249
#define STRTAB_STE_0_S1FMT GENMASK_ULL(5, 4)
249250
#define STRTAB_STE_0_S1FMT_LINEAR 0
@@ -295,6 +296,15 @@ static inline u32 arm_smmu_strtab_l2_idx(u32 sid)
295296

296297
#define STRTAB_STE_3_S2TTB_MASK GENMASK_ULL(51, 4)
297298

299+
/* These bits can be controlled by userspace for STRTAB_STE_0_CFG_NESTED */
300+
#define STRTAB_STE_0_NESTING_ALLOWED \
301+
cpu_to_le64(STRTAB_STE_0_V | STRTAB_STE_0_CFG | STRTAB_STE_0_S1FMT | \
302+
STRTAB_STE_0_S1CTXPTR_MASK | STRTAB_STE_0_S1CDMAX)
303+
#define STRTAB_STE_1_NESTING_ALLOWED \
304+
cpu_to_le64(STRTAB_STE_1_S1DSS | STRTAB_STE_1_S1CIR | \
305+
STRTAB_STE_1_S1COR | STRTAB_STE_1_S1CSH | \
306+
STRTAB_STE_1_S1STALLD)
307+
298308
/*
299309
* Context descriptors.
300310
*
@@ -514,6 +524,7 @@ struct arm_smmu_cmdq_ent {
514524
};
515525
} cfgi;
516526

527+
#define CMDQ_OP_TLBI_NH_ALL 0x10
517528
#define CMDQ_OP_TLBI_NH_ASID 0x11
518529
#define CMDQ_OP_TLBI_NH_VA 0x12
519530
#define CMDQ_OP_TLBI_EL2_ALL 0x20
@@ -815,10 +826,18 @@ struct arm_smmu_domain {
815826
struct list_head devices;
816827
spinlock_t devices_lock;
817828
bool enforce_cache_coherency : 1;
829+
bool nest_parent : 1;
818830

819831
struct mmu_notifier mmu_notifier;
820832
};
821833

834+
struct arm_smmu_nested_domain {
835+
struct iommu_domain domain;
836+
struct arm_vsmmu *vsmmu;
837+
838+
__le64 ste[2];
839+
};
840+
822841
/* The following are exposed for testing purposes. */
823842
struct arm_smmu_entry_writer_ops;
824843
struct arm_smmu_entry_writer {
@@ -863,6 +882,12 @@ static inline struct arm_smmu_domain *to_smmu_domain(struct iommu_domain *dom)
863882
return container_of(dom, struct arm_smmu_domain, domain);
864883
}
865884

885+
static inline struct arm_smmu_nested_domain *
886+
to_smmu_nested_domain(struct iommu_domain *dom)
887+
{
888+
return container_of(dom, struct arm_smmu_nested_domain, domain);
889+
}
890+
866891
extern struct xarray arm_smmu_asid_xa;
867892
extern struct mutex arm_smmu_asid_lock;
868893

@@ -909,6 +934,7 @@ struct arm_smmu_attach_state {
909934
struct iommu_domain *old_domain;
910935
struct arm_smmu_master *master;
911936
bool cd_needs_ats;
937+
bool disable_ats;
912938
ioasid_t ssid;
913939
/* Resulting state */
914940
bool ats_enabled;

include/uapi/linux/iommufd.h

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -421,6 +421,26 @@ struct iommu_hwpt_vtd_s1 {
421421
__u32 __reserved;
422422
};
423423

424+
/**
425+
* struct iommu_hwpt_arm_smmuv3 - ARM SMMUv3 nested STE
426+
* (IOMMU_HWPT_DATA_ARM_SMMUV3)
427+
*
428+
* @ste: The first two double words of the user space Stream Table Entry for
429+
* the translation. Must be little-endian.
430+
* Allowed fields: (Refer to "5.2 Stream Table Entry" in SMMUv3 HW Spec)
431+
* - word-0: V, Cfg, S1Fmt, S1ContextPtr, S1CDMax
432+
* - word-1: S1DSS, S1CIR, S1COR, S1CSH, S1STALLD
433+
*
434+
* -EIO will be returned if @ste is not legal or contains any non-allowed field.
435+
* Cfg can be used to select a S1, Bypass or Abort configuration. A Bypass
436+
* nested domain will translate the same as the nesting parent. The S1 will
437+
* install a Context Descriptor Table pointing at userspace memory translated
438+
* by the nesting parent.
439+
*/
440+
struct iommu_hwpt_arm_smmuv3 {
441+
__aligned_le64 ste[2];
442+
};
443+
424444
/**
425445
* enum iommu_hwpt_data_type - IOMMU HWPT Data Type
426446
* @IOMMU_HWPT_DATA_NONE: no data

0 commit comments

Comments
 (0)