1 // SPDX-License-Identifier: GPL-2.0
4 * Copyright 2016-2020 HabanaLabs, Ltd.
8 #include <linux/slab.h>
10 #include "habanalabs.h"
12 static bool is_dram_va(struct hl_device
*hdev
, u64 virt_addr
)
14 struct asic_fixed_properties
*prop
= &hdev
->asic_prop
;
16 return hl_mem_area_inside_range(virt_addr
, prop
->dmmu
.page_size
,
17 prop
->dmmu
.start_addr
,
22 * hl_mmu_init() - initialize the MMU module.
23 * @hdev: habanalabs device structure.
25 * Return: 0 for success, non-zero for failure.
27 int hl_mmu_init(struct hl_device
*hdev
)
31 if (!hdev
->mmu_enable
)
34 if (hdev
->mmu_func
[MMU_DR_PGT
].init
!= NULL
) {
35 rc
= hdev
->mmu_func
[MMU_DR_PGT
].init(hdev
);
40 if (hdev
->mmu_func
[MMU_HR_PGT
].init
!= NULL
)
41 rc
= hdev
->mmu_func
[MMU_HR_PGT
].init(hdev
);
47 * hl_mmu_fini() - release the MMU module.
48 * @hdev: habanalabs device structure.
50 * This function does the following:
51 * - Disable MMU in H/W.
52 * - Free the pgt_infos pool.
54 * All contexts should be freed before calling this function.
56 void hl_mmu_fini(struct hl_device
*hdev
)
58 if (!hdev
->mmu_enable
)
61 if (hdev
->mmu_func
[MMU_DR_PGT
].fini
!= NULL
)
62 hdev
->mmu_func
[MMU_DR_PGT
].fini(hdev
);
64 if (hdev
->mmu_func
[MMU_HR_PGT
].fini
!= NULL
)
65 hdev
->mmu_func
[MMU_HR_PGT
].fini(hdev
);
69 * hl_mmu_ctx_init() - initialize a context for using the MMU module.
70 * @ctx: pointer to the context structure to initialize.
72 * Initialize a mutex to protect the concurrent mapping flow, a hash to hold all
73 * page tables hops related to this context.
74 * Return: 0 on success, non-zero otherwise.
76 int hl_mmu_ctx_init(struct hl_ctx
*ctx
)
78 struct hl_device
*hdev
= ctx
->hdev
;
81 if (!hdev
->mmu_enable
)
84 mutex_init(&ctx
->mmu_lock
);
86 if (hdev
->mmu_func
[MMU_DR_PGT
].ctx_init
!= NULL
) {
87 rc
= hdev
->mmu_func
[MMU_DR_PGT
].ctx_init(ctx
);
92 if (hdev
->mmu_func
[MMU_HR_PGT
].ctx_init
!= NULL
)
93 rc
= hdev
->mmu_func
[MMU_HR_PGT
].ctx_init(ctx
);
99 * hl_mmu_ctx_fini - disable a ctx from using the mmu module
101 * @ctx: pointer to the context structure
103 * This function does the following:
104 * - Free any pgts which were not freed yet
106 * - Free DRAM default page mapping hops
108 void hl_mmu_ctx_fini(struct hl_ctx
*ctx
)
110 struct hl_device
*hdev
= ctx
->hdev
;
112 if (!hdev
->mmu_enable
)
115 if (hdev
->mmu_func
[MMU_DR_PGT
].ctx_fini
!= NULL
)
116 hdev
->mmu_func
[MMU_DR_PGT
].ctx_fini(ctx
);
118 if (hdev
->mmu_func
[MMU_HR_PGT
].ctx_fini
!= NULL
)
119 hdev
->mmu_func
[MMU_HR_PGT
].ctx_fini(ctx
);
121 mutex_destroy(&ctx
->mmu_lock
);
125 * hl_mmu_unmap_page - unmaps a virtual addr
127 * @ctx: pointer to the context structure
128 * @virt_addr: virt addr to map from
129 * @page_size: size of the page to unmap
130 * @flush_pte: whether to do a PCI flush
132 * This function does the following:
133 * - Check that the virt addr is mapped
134 * - Unmap the virt addr and frees pgts if possible
135 * - Returns 0 on success, -EINVAL if the given addr is not mapped
137 * Because this function changes the page tables in the device and because it
138 * changes the MMU hash, it must be protected by a lock.
139 * However, because it maps only a single page, the lock should be implemented
140 * in a higher level in order to protect the entire mapping of the memory area
142 * For optimization reasons PCI flush may be requested once after unmapping of
145 int hl_mmu_unmap_page(struct hl_ctx
*ctx
, u64 virt_addr
, u32 page_size
,
148 struct hl_device
*hdev
= ctx
->hdev
;
149 struct asic_fixed_properties
*prop
= &hdev
->asic_prop
;
150 struct hl_mmu_properties
*mmu_prop
;
152 u32 real_page_size
, npages
;
153 int i
, rc
= 0, pgt_residency
;
156 if (!hdev
->mmu_enable
)
159 is_dram_addr
= is_dram_va(hdev
, virt_addr
);
162 mmu_prop
= &prop
->dmmu
;
163 else if ((page_size
% prop
->pmmu_huge
.page_size
) == 0)
164 mmu_prop
= &prop
->pmmu_huge
;
166 mmu_prop
= &prop
->pmmu
;
168 pgt_residency
= mmu_prop
->host_resident
? MMU_HR_PGT
: MMU_DR_PGT
;
171 * The H/W handles mapping of specific page sizes. Hence if the page
172 * size is bigger, we break it to sub-pages and unmap them separately.
174 if ((page_size
% mmu_prop
->page_size
) == 0) {
175 real_page_size
= mmu_prop
->page_size
;
178 "page size of %u is not %uKB aligned, can't unmap\n",
179 page_size
, mmu_prop
->page_size
>> 10);
184 npages
= page_size
/ real_page_size
;
185 real_virt_addr
= virt_addr
;
187 for (i
= 0 ; i
< npages
; i
++) {
188 rc
= hdev
->mmu_func
[pgt_residency
].unmap(ctx
,
189 real_virt_addr
, is_dram_addr
);
193 real_virt_addr
+= real_page_size
;
197 hdev
->mmu_func
[pgt_residency
].flush(ctx
);
203 * hl_mmu_map_page - maps a virtual addr to physical addr
205 * @ctx: pointer to the context structure
206 * @virt_addr: virt addr to map from
207 * @phys_addr: phys addr to map to
208 * @page_size: physical page size
209 * @flush_pte: whether to do a PCI flush
211 * This function does the following:
212 * - Check that the virt addr is not mapped
213 * - Allocate pgts as necessary in order to map the virt addr to the phys
214 * - Returns 0 on success, -EINVAL if addr is already mapped, or -ENOMEM.
216 * Because this function changes the page tables in the device and because it
217 * changes the MMU hash, it must be protected by a lock.
218 * However, because it maps only a single page, the lock should be implemented
219 * in a higher level in order to protect the entire mapping of the memory area
221 * For optimization reasons PCI flush may be requested once after mapping of
224 int hl_mmu_map_page(struct hl_ctx
*ctx
, u64 virt_addr
, u64 phys_addr
,
225 u32 page_size
, bool flush_pte
)
227 struct hl_device
*hdev
= ctx
->hdev
;
228 struct asic_fixed_properties
*prop
= &hdev
->asic_prop
;
229 struct hl_mmu_properties
*mmu_prop
;
230 u64 real_virt_addr
, real_phys_addr
;
231 u32 real_page_size
, npages
;
232 int i
, rc
, pgt_residency
, mapped_cnt
= 0;
236 if (!hdev
->mmu_enable
)
239 is_dram_addr
= is_dram_va(hdev
, virt_addr
);
242 mmu_prop
= &prop
->dmmu
;
243 else if ((page_size
% prop
->pmmu_huge
.page_size
) == 0)
244 mmu_prop
= &prop
->pmmu_huge
;
246 mmu_prop
= &prop
->pmmu
;
248 pgt_residency
= mmu_prop
->host_resident
? MMU_HR_PGT
: MMU_DR_PGT
;
251 * The H/W handles mapping of specific page sizes. Hence if the page
252 * size is bigger, we break it to sub-pages and map them separately.
254 if ((page_size
% mmu_prop
->page_size
) == 0) {
255 real_page_size
= mmu_prop
->page_size
;
258 "page size of %u is not %uKB aligned, can't map\n",
259 page_size
, mmu_prop
->page_size
>> 10);
264 WARN_ONCE((phys_addr
& (real_page_size
- 1)),
265 "Mapping 0x%llx with page size of 0x%x is erroneous! Address must be divisible by page size",
266 phys_addr
, real_page_size
);
268 npages
= page_size
/ real_page_size
;
269 real_virt_addr
= virt_addr
;
270 real_phys_addr
= phys_addr
;
272 for (i
= 0 ; i
< npages
; i
++) {
273 rc
= hdev
->mmu_func
[pgt_residency
].map(ctx
,
274 real_virt_addr
, real_phys_addr
,
275 real_page_size
, is_dram_addr
);
279 real_virt_addr
+= real_page_size
;
280 real_phys_addr
+= real_page_size
;
285 hdev
->mmu_func
[pgt_residency
].flush(ctx
);
290 real_virt_addr
= virt_addr
;
291 for (i
= 0 ; i
< mapped_cnt
; i
++) {
292 if (hdev
->mmu_func
[pgt_residency
].unmap(ctx
,
293 real_virt_addr
, is_dram_addr
))
294 dev_warn_ratelimited(hdev
->dev
,
295 "failed to unmap va: 0x%llx\n", real_virt_addr
);
297 real_virt_addr
+= real_page_size
;
300 hdev
->mmu_func
[pgt_residency
].flush(ctx
);
306 * hl_mmu_map_contiguous - implements a wrapper for hl_mmu_map_page
307 * for mapping contiguous physical memory
309 * @ctx: pointer to the context structure
310 * @virt_addr: virt addr to map from
311 * @phys_addr: phys addr to map to
315 int hl_mmu_map_contiguous(struct hl_ctx
*ctx
, u64 virt_addr
,
316 u64 phys_addr
, u32 size
)
318 struct hl_device
*hdev
= ctx
->hdev
;
319 struct asic_fixed_properties
*prop
= &hdev
->asic_prop
;
320 u64 curr_va
, curr_pa
;
325 if (hl_mem_area_inside_range(virt_addr
, size
,
326 prop
->dmmu
.start_addr
, prop
->dmmu
.end_addr
))
327 page_size
= prop
->dmmu
.page_size
;
328 else if (hl_mem_area_inside_range(virt_addr
, size
,
329 prop
->pmmu
.start_addr
, prop
->pmmu
.end_addr
))
330 page_size
= prop
->pmmu
.page_size
;
331 else if (hl_mem_area_inside_range(virt_addr
, size
,
332 prop
->pmmu_huge
.start_addr
, prop
->pmmu_huge
.end_addr
))
333 page_size
= prop
->pmmu_huge
.page_size
;
337 for (off
= 0 ; off
< size
; off
+= page_size
) {
338 curr_va
= virt_addr
+ off
;
339 curr_pa
= phys_addr
+ off
;
340 flush_pte
= (off
+ page_size
) >= size
;
341 rc
= hl_mmu_map_page(ctx
, curr_va
, curr_pa
, page_size
,
345 "Map failed for va 0x%llx to pa 0x%llx\n",
354 for (; off
>= 0 ; off
-= page_size
) {
355 curr_va
= virt_addr
+ off
;
356 flush_pte
= (off
- (s32
) page_size
) < 0;
357 if (hl_mmu_unmap_page(ctx
, curr_va
, page_size
, flush_pte
))
358 dev_warn_ratelimited(hdev
->dev
,
359 "failed to unmap va 0x%llx\n", curr_va
);
366 * hl_mmu_unmap_contiguous - implements a wrapper for hl_mmu_unmap_page
367 * for unmapping contiguous physical memory
369 * @ctx: pointer to the context structure
370 * @virt_addr: virt addr to unmap
371 * @size: size to unmap
374 int hl_mmu_unmap_contiguous(struct hl_ctx
*ctx
, u64 virt_addr
, u32 size
)
376 struct hl_device
*hdev
= ctx
->hdev
;
377 struct asic_fixed_properties
*prop
= &hdev
->asic_prop
;
383 if (hl_mem_area_inside_range(virt_addr
, size
,
384 prop
->dmmu
.start_addr
, prop
->dmmu
.end_addr
))
385 page_size
= prop
->dmmu
.page_size
;
386 else if (hl_mem_area_inside_range(virt_addr
, size
,
387 prop
->pmmu
.start_addr
, prop
->pmmu
.end_addr
))
388 page_size
= prop
->pmmu
.page_size
;
389 else if (hl_mem_area_inside_range(virt_addr
, size
,
390 prop
->pmmu_huge
.start_addr
, prop
->pmmu_huge
.end_addr
))
391 page_size
= prop
->pmmu_huge
.page_size
;
395 for (off
= 0 ; off
< size
; off
+= page_size
) {
396 curr_va
= virt_addr
+ off
;
397 flush_pte
= (off
+ page_size
) >= size
;
398 rc
= hl_mmu_unmap_page(ctx
, curr_va
, page_size
, flush_pte
);
400 dev_warn_ratelimited(hdev
->dev
,
401 "Unmap failed for va 0x%llx\n", curr_va
);
408 * hl_mmu_swap_out - marks all mapping of the given ctx as swapped out
410 * @ctx: pointer to the context structure
413 void hl_mmu_swap_out(struct hl_ctx
*ctx
)
415 struct hl_device
*hdev
= ctx
->hdev
;
417 if (!hdev
->mmu_enable
)
420 if (hdev
->mmu_func
[MMU_DR_PGT
].swap_out
!= NULL
)
421 hdev
->mmu_func
[MMU_DR_PGT
].swap_out(ctx
);
423 if (hdev
->mmu_func
[MMU_HR_PGT
].swap_out
!= NULL
)
424 hdev
->mmu_func
[MMU_HR_PGT
].swap_out(ctx
);
428 * hl_mmu_swap_in - marks all mapping of the given ctx as swapped in
430 * @ctx: pointer to the context structure
433 void hl_mmu_swap_in(struct hl_ctx
*ctx
)
435 struct hl_device
*hdev
= ctx
->hdev
;
437 if (!hdev
->mmu_enable
)
440 if (hdev
->mmu_func
[MMU_DR_PGT
].swap_in
!= NULL
)
441 hdev
->mmu_func
[MMU_DR_PGT
].swap_in(ctx
);
443 if (hdev
->mmu_func
[MMU_HR_PGT
].swap_in
!= NULL
)
444 hdev
->mmu_func
[MMU_HR_PGT
].swap_in(ctx
);
447 int hl_mmu_va_to_pa(struct hl_ctx
*ctx
, u64 virt_addr
, u64
*phys_addr
)
449 struct hl_mmu_hop_info hops
;
453 rc
= hl_mmu_get_tlb_info(ctx
, virt_addr
, &hops
);
457 /* last hop holds the phys address and flags */
458 tmp_addr
= hops
.hop_info
[hops
.used_hops
- 1].hop_pte_val
;
459 *phys_addr
= (tmp_addr
& HOP_PHYS_ADDR_MASK
) | (virt_addr
& FLAGS_MASK
);
464 int hl_mmu_get_tlb_info(struct hl_ctx
*ctx
, u64 virt_addr
,
465 struct hl_mmu_hop_info
*hops
)
467 struct hl_device
*hdev
= ctx
->hdev
;
468 struct asic_fixed_properties
*prop
= &hdev
->asic_prop
;
469 struct hl_mmu_properties
*mmu_prop
;
473 if (!hdev
->mmu_enable
)
476 is_dram_addr
= hl_mem_area_inside_range(virt_addr
, prop
->dmmu
.page_size
,
477 prop
->dmmu
.start_addr
,
478 prop
->dmmu
.end_addr
);
480 /* host-residency is the same in PMMU and HPMMU, use one of them */
481 mmu_prop
= is_dram_addr
? &prop
->dmmu
: &prop
->pmmu
;
483 mutex_lock(&ctx
->mmu_lock
);
485 if (mmu_prop
->host_resident
)
486 rc
= hdev
->mmu_func
[MMU_HR_PGT
].get_tlb_info(ctx
,
489 rc
= hdev
->mmu_func
[MMU_DR_PGT
].get_tlb_info(ctx
,
492 mutex_unlock(&ctx
->mmu_lock
);
497 int hl_mmu_if_set_funcs(struct hl_device
*hdev
)
499 if (!hdev
->mmu_enable
)
502 switch (hdev
->asic_type
) {
505 hl_mmu_v1_set_funcs(hdev
, &hdev
->mmu_func
[MMU_DR_PGT
]);
508 dev_err(hdev
->dev
, "Unrecognized ASIC type %d\n",