xref: /linux/drivers/accel/amdxdna/amdxdna_iommu.c (revision 0fc8f6200d2313278fbf4539bbab74677c685531)
1*ece3e898SLizhi Hou // SPDX-License-Identifier: GPL-2.0
2*ece3e898SLizhi Hou /*
3*ece3e898SLizhi Hou  * Copyright (C) 2025, Advanced Micro Devices, Inc.
4*ece3e898SLizhi Hou  */
5*ece3e898SLizhi Hou 
6*ece3e898SLizhi Hou #include <drm/amdxdna_accel.h>
7*ece3e898SLizhi Hou #include <linux/iommu.h>
8*ece3e898SLizhi Hou #include <linux/iova.h>
9*ece3e898SLizhi Hou 
10*ece3e898SLizhi Hou #include "amdxdna_gem.h"
11*ece3e898SLizhi Hou #include "amdxdna_pci_drv.h"
12*ece3e898SLizhi Hou 
13*ece3e898SLizhi Hou static bool force_iova;
14*ece3e898SLizhi Hou module_param(force_iova, bool, 0600);
15*ece3e898SLizhi Hou MODULE_PARM_DESC(force_iova, "Force use IOVA (Default false)");
16*ece3e898SLizhi Hou 
17*ece3e898SLizhi Hou static struct iova *amdxdna_iommu_alloc_iova(struct amdxdna_dev *xdna,
18*ece3e898SLizhi Hou 					     size_t size,
19*ece3e898SLizhi Hou 					     dma_addr_t *dma_addr,
20*ece3e898SLizhi Hou 					     bool size_aligned)
21*ece3e898SLizhi Hou {
22*ece3e898SLizhi Hou 	unsigned long shift, end;
23*ece3e898SLizhi Hou 	struct iova *iova;
24*ece3e898SLizhi Hou 
25*ece3e898SLizhi Hou 	end = xdna->domain->geometry.aperture_end;
26*ece3e898SLizhi Hou 	shift = iova_shift(&xdna->iovad);
27*ece3e898SLizhi Hou 	size = iova_align(&xdna->iovad, size);
28*ece3e898SLizhi Hou 
29*ece3e898SLizhi Hou 	iova = alloc_iova(&xdna->iovad, size >> shift, end >> shift, size_aligned);
30*ece3e898SLizhi Hou 	if (!iova)
31*ece3e898SLizhi Hou 		return ERR_PTR(-ENOMEM);
32*ece3e898SLizhi Hou 
33*ece3e898SLizhi Hou 	*dma_addr = iova_dma_addr(&xdna->iovad, iova);
34*ece3e898SLizhi Hou 
35*ece3e898SLizhi Hou 	return iova;
36*ece3e898SLizhi Hou }
37*ece3e898SLizhi Hou 
38*ece3e898SLizhi Hou int amdxdna_iommu_map_bo(struct amdxdna_dev *xdna, struct amdxdna_gem_obj *abo)
39*ece3e898SLizhi Hou {
40*ece3e898SLizhi Hou 	struct sg_table *sgt;
41*ece3e898SLizhi Hou 	dma_addr_t dma_addr;
42*ece3e898SLizhi Hou 	struct iova *iova;
43*ece3e898SLizhi Hou 	size_t size;
44*ece3e898SLizhi Hou 
45*ece3e898SLizhi Hou 	if (abo->type != AMDXDNA_BO_DEV_HEAP && abo->type != AMDXDNA_BO_SHMEM)
46*ece3e898SLizhi Hou 		return 0;
47*ece3e898SLizhi Hou 
48*ece3e898SLizhi Hou 	sgt = drm_gem_shmem_get_pages_sgt(&abo->base);
49*ece3e898SLizhi Hou 	if (IS_ERR(sgt)) {
50*ece3e898SLizhi Hou 		XDNA_ERR(xdna, "Get sgt failed, ret %ld", PTR_ERR(sgt));
51*ece3e898SLizhi Hou 		return PTR_ERR(sgt);
52*ece3e898SLizhi Hou 	}
53*ece3e898SLizhi Hou 
54*ece3e898SLizhi Hou 	if (!sgt->orig_nents || !sg_page(sgt->sgl)) {
55*ece3e898SLizhi Hou 		XDNA_ERR(xdna, "sgl is zero length or not page backed");
56*ece3e898SLizhi Hou 		return -EOPNOTSUPP;
57*ece3e898SLizhi Hou 	}
58*ece3e898SLizhi Hou 
59*ece3e898SLizhi Hou 	iova = amdxdna_iommu_alloc_iova(xdna, abo->mem.size, &dma_addr,
60*ece3e898SLizhi Hou 					(abo->type == AMDXDNA_BO_DEV_HEAP));
61*ece3e898SLizhi Hou 	if (IS_ERR(iova)) {
62*ece3e898SLizhi Hou 		XDNA_ERR(xdna, "Alloc iova failed, ret %ld", PTR_ERR(iova));
63*ece3e898SLizhi Hou 		return PTR_ERR(iova);
64*ece3e898SLizhi Hou 	}
65*ece3e898SLizhi Hou 
66*ece3e898SLizhi Hou 	size = iommu_map_sgtable(xdna->domain, dma_addr, sgt,
67*ece3e898SLizhi Hou 				 IOMMU_READ | IOMMU_WRITE);
68*ece3e898SLizhi Hou 	if (size < abo->mem.size) {
69*ece3e898SLizhi Hou 		__free_iova(&xdna->iovad, iova);
70*ece3e898SLizhi Hou 		return -ENXIO;
71*ece3e898SLizhi Hou 	}
72*ece3e898SLizhi Hou 
73*ece3e898SLizhi Hou 	abo->mem.dma_addr = dma_addr;
74*ece3e898SLizhi Hou 
75*ece3e898SLizhi Hou 	return 0;
76*ece3e898SLizhi Hou }
77*ece3e898SLizhi Hou 
78*ece3e898SLizhi Hou void amdxdna_iommu_unmap_bo(struct amdxdna_dev *xdna, struct amdxdna_gem_obj *abo)
79*ece3e898SLizhi Hou {
80*ece3e898SLizhi Hou 	size_t size;
81*ece3e898SLizhi Hou 
82*ece3e898SLizhi Hou 	if (abo->mem.dma_addr == AMDXDNA_INVALID_ADDR)
83*ece3e898SLizhi Hou 		return;
84*ece3e898SLizhi Hou 
85*ece3e898SLizhi Hou 	size = iova_align(&xdna->iovad, abo->mem.size);
86*ece3e898SLizhi Hou 	iommu_unmap(xdna->domain, abo->mem.dma_addr, size);
87*ece3e898SLizhi Hou 	free_iova(&xdna->iovad, iova_pfn(&xdna->iovad, abo->mem.dma_addr));
88*ece3e898SLizhi Hou 	abo->mem.dma_addr = AMDXDNA_INVALID_ADDR;
89*ece3e898SLizhi Hou }
90*ece3e898SLizhi Hou 
91*ece3e898SLizhi Hou void *amdxdna_iommu_alloc(struct amdxdna_dev *xdna, size_t size, dma_addr_t *dma_addr)
92*ece3e898SLizhi Hou {
93*ece3e898SLizhi Hou 	struct iova *iova;
94*ece3e898SLizhi Hou 	void *cpu_addr;
95*ece3e898SLizhi Hou 	int ret;
96*ece3e898SLizhi Hou 
97*ece3e898SLizhi Hou 	iova = amdxdna_iommu_alloc_iova(xdna, size, dma_addr, true);
98*ece3e898SLizhi Hou 	if (IS_ERR(iova)) {
99*ece3e898SLizhi Hou 		XDNA_ERR(xdna, "Alloc iova failed, ret %ld", PTR_ERR(iova));
100*ece3e898SLizhi Hou 		return iova;
101*ece3e898SLizhi Hou 	}
102*ece3e898SLizhi Hou 
103*ece3e898SLizhi Hou 	cpu_addr = (void *)__get_free_pages(GFP_KERNEL, get_order(size));
104*ece3e898SLizhi Hou 	if (!cpu_addr) {
105*ece3e898SLizhi Hou 		ret = -ENOMEM;
106*ece3e898SLizhi Hou 		goto free_iova;
107*ece3e898SLizhi Hou 	}
108*ece3e898SLizhi Hou 
109*ece3e898SLizhi Hou 	ret = iommu_map(xdna->domain, *dma_addr, virt_to_phys(cpu_addr),
110*ece3e898SLizhi Hou 			iova_align(&xdna->iovad, size),
111*ece3e898SLizhi Hou 			IOMMU_READ | IOMMU_WRITE, GFP_KERNEL);
112*ece3e898SLizhi Hou 	if (ret)
113*ece3e898SLizhi Hou 		goto free_iova;
114*ece3e898SLizhi Hou 
115*ece3e898SLizhi Hou 	return cpu_addr;
116*ece3e898SLizhi Hou 
117*ece3e898SLizhi Hou free_iova:
118*ece3e898SLizhi Hou 	__free_iova(&xdna->iovad, iova);
119*ece3e898SLizhi Hou 	return ERR_PTR(ret);
120*ece3e898SLizhi Hou }
121*ece3e898SLizhi Hou 
122*ece3e898SLizhi Hou void amdxdna_iommu_free(struct amdxdna_dev *xdna, size_t size,
123*ece3e898SLizhi Hou 			void *cpu_addr, dma_addr_t dma_addr)
124*ece3e898SLizhi Hou {
125*ece3e898SLizhi Hou 	iommu_unmap(xdna->domain, dma_addr, iova_align(&xdna->iovad, size));
126*ece3e898SLizhi Hou 	free_iova(&xdna->iovad, iova_pfn(&xdna->iovad, dma_addr));
127*ece3e898SLizhi Hou 	free_pages((unsigned long)cpu_addr, get_order(size));
128*ece3e898SLizhi Hou }
129*ece3e898SLizhi Hou 
130*ece3e898SLizhi Hou int amdxdna_iommu_init(struct amdxdna_dev *xdna)
131*ece3e898SLizhi Hou {
132*ece3e898SLizhi Hou 	unsigned long order;
133*ece3e898SLizhi Hou 	int ret;
134*ece3e898SLizhi Hou 
135*ece3e898SLizhi Hou 	xdna->group = iommu_group_get(xdna->ddev.dev);
136*ece3e898SLizhi Hou 	if (!xdna->group || !force_iova)
137*ece3e898SLizhi Hou 		return 0;
138*ece3e898SLizhi Hou 
139*ece3e898SLizhi Hou 	XDNA_WARN(xdna, "Enabled force_iova mode.");
140*ece3e898SLizhi Hou 	xdna->domain = iommu_paging_domain_alloc_flags(xdna->ddev.dev,
141*ece3e898SLizhi Hou 						       IOMMU_HWPT_ALLOC_PASID);
142*ece3e898SLizhi Hou 	if (IS_ERR(xdna->domain)) {
143*ece3e898SLizhi Hou 		XDNA_ERR(xdna, "Failed to alloc iommu domain");
144*ece3e898SLizhi Hou 		ret = PTR_ERR(xdna->domain);
145*ece3e898SLizhi Hou 		goto put_group;
146*ece3e898SLizhi Hou 	}
147*ece3e898SLizhi Hou 
148*ece3e898SLizhi Hou 	ret = iova_cache_get();
149*ece3e898SLizhi Hou 	if (ret)
150*ece3e898SLizhi Hou 		goto free_domain;
151*ece3e898SLizhi Hou 
152*ece3e898SLizhi Hou 	order = __ffs(xdna->domain->pgsize_bitmap);
153*ece3e898SLizhi Hou 	init_iova_domain(&xdna->iovad, 1UL << order, 0);
154*ece3e898SLizhi Hou 
155*ece3e898SLizhi Hou 	ret = iommu_attach_group(xdna->domain, xdna->group);
156*ece3e898SLizhi Hou 	if (ret)
157*ece3e898SLizhi Hou 		goto put_iova;
158*ece3e898SLizhi Hou 
159*ece3e898SLizhi Hou 	return 0;
160*ece3e898SLizhi Hou 
161*ece3e898SLizhi Hou put_iova:
162*ece3e898SLizhi Hou 	put_iova_domain(&xdna->iovad);
163*ece3e898SLizhi Hou 	iova_cache_put();
164*ece3e898SLizhi Hou free_domain:
165*ece3e898SLizhi Hou 	iommu_domain_free(xdna->domain);
166*ece3e898SLizhi Hou put_group:
167*ece3e898SLizhi Hou 	iommu_group_put(xdna->group);
168*ece3e898SLizhi Hou 	xdna->domain = NULL;
169*ece3e898SLizhi Hou 
170*ece3e898SLizhi Hou 	return ret;
171*ece3e898SLizhi Hou }
172*ece3e898SLizhi Hou 
173*ece3e898SLizhi Hou void amdxdna_iommu_fini(struct amdxdna_dev *xdna)
174*ece3e898SLizhi Hou {
175*ece3e898SLizhi Hou 	if (xdna->domain) {
176*ece3e898SLizhi Hou 		iommu_detach_group(xdna->domain, xdna->group);
177*ece3e898SLizhi Hou 		put_iova_domain(&xdna->iovad);
178*ece3e898SLizhi Hou 		iova_cache_put();
179*ece3e898SLizhi Hou 		iommu_domain_free(xdna->domain);
180*ece3e898SLizhi Hou 	}
181*ece3e898SLizhi Hou 
182*ece3e898SLizhi Hou 	if (xdna->group)
183*ece3e898SLizhi Hou 		iommu_group_put(xdna->group);
184*ece3e898SLizhi Hou }
185