Moti Haimovski | d83fe66 | 2020-08-12 13:33:44 +0300 | [diff] [blame] | 1 | // SPDX-License-Identifier: GPL-2.0 |
| 2 | |
| 3 | /* |
| 4 | * Copyright 2016-2020 HabanaLabs, Ltd. |
| 5 | * All Rights Reserved. |
| 6 | */ |
| 7 | |
| 8 | #include <linux/slab.h> |
| 9 | |
Ofir Bitton | 8563e19 | 2020-12-28 14:36:47 +0200 | [diff] [blame] | 10 | #include "../habanalabs.h" |
Moti Haimovski | d83fe66 | 2020-08-12 13:33:44 +0300 | [diff] [blame] | 11 | |
Oded Gabbay | 9488307 | 2021-01-11 17:49:30 +0200 | [diff] [blame] | 12 | bool hl_is_dram_va(struct hl_device *hdev, u64 virt_addr) |
Moti Haimovski | d83fe66 | 2020-08-12 13:33:44 +0300 | [diff] [blame] | 13 | { |
| 14 | struct asic_fixed_properties *prop = &hdev->asic_prop; |
| 15 | |
| 16 | return hl_mem_area_inside_range(virt_addr, prop->dmmu.page_size, |
| 17 | prop->dmmu.start_addr, |
| 18 | prop->dmmu.end_addr); |
| 19 | } |
| 20 | |
| 21 | /** |
| 22 | * hl_mmu_init() - initialize the MMU module. |
| 23 | * @hdev: habanalabs device structure. |
| 24 | * |
Moti Haimovski | d83fe66 | 2020-08-12 13:33:44 +0300 | [diff] [blame] | 25 | * Return: 0 for success, non-zero for failure. |
| 26 | */ |
| 27 | int hl_mmu_init(struct hl_device *hdev) |
| 28 | { |
Moti Haimovski | ccf979e | 2020-10-05 17:59:29 +0300 | [diff] [blame] | 29 | int rc = -EOPNOTSUPP; |
Moti Haimovski | d83fe66 | 2020-08-12 13:33:44 +0300 | [diff] [blame] | 30 | |
Moti Haimovski | ccf979e | 2020-10-05 17:59:29 +0300 | [diff] [blame] | 31 | if (!hdev->mmu_enable) |
| 32 | return 0; |
| 33 | |
| 34 | if (hdev->mmu_func[MMU_DR_PGT].init != NULL) { |
| 35 | rc = hdev->mmu_func[MMU_DR_PGT].init(hdev); |
| 36 | if (rc) |
| 37 | return rc; |
| 38 | } |
| 39 | |
| 40 | if (hdev->mmu_func[MMU_HR_PGT].init != NULL) |
| 41 | rc = hdev->mmu_func[MMU_HR_PGT].init(hdev); |
| 42 | |
| 43 | return rc; |
Moti Haimovski | d83fe66 | 2020-08-12 13:33:44 +0300 | [diff] [blame] | 44 | } |
| 45 | |
| 46 | /** |
| 47 | * hl_mmu_fini() - release the MMU module. |
| 48 | * @hdev: habanalabs device structure. |
| 49 | * |
| 50 | * This function does the following: |
| 51 | * - Disable MMU in H/W. |
| 52 | * - Free the pgt_infos pool. |
| 53 | * |
| 54 | * All contexts should be freed before calling this function. |
| 55 | */ |
| 56 | void hl_mmu_fini(struct hl_device *hdev) |
| 57 | { |
Moti Haimovski | ccf979e | 2020-10-05 17:59:29 +0300 | [diff] [blame] | 58 | if (!hdev->mmu_enable) |
| 59 | return; |
| 60 | |
| 61 | if (hdev->mmu_func[MMU_DR_PGT].fini != NULL) |
| 62 | hdev->mmu_func[MMU_DR_PGT].fini(hdev); |
| 63 | |
| 64 | if (hdev->mmu_func[MMU_HR_PGT].fini != NULL) |
| 65 | hdev->mmu_func[MMU_HR_PGT].fini(hdev); |
Moti Haimovski | d83fe66 | 2020-08-12 13:33:44 +0300 | [diff] [blame] | 66 | } |
| 67 | |
| 68 | /** |
| 69 | * hl_mmu_ctx_init() - initialize a context for using the MMU module. |
| 70 | * @ctx: pointer to the context structure to initialize. |
| 71 | * |
| 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. |
| 75 | */ |
| 76 | int hl_mmu_ctx_init(struct hl_ctx *ctx) |
| 77 | { |
| 78 | struct hl_device *hdev = ctx->hdev; |
Moti Haimovski | ccf979e | 2020-10-05 17:59:29 +0300 | [diff] [blame] | 79 | int rc = -EOPNOTSUPP; |
Moti Haimovski | d83fe66 | 2020-08-12 13:33:44 +0300 | [diff] [blame] | 80 | |
Moti Haimovski | ccf979e | 2020-10-05 17:59:29 +0300 | [diff] [blame] | 81 | if (!hdev->mmu_enable) |
| 82 | return 0; |
Moti Haimovski | d83fe66 | 2020-08-12 13:33:44 +0300 | [diff] [blame] | 83 | |
Moti Haimovski | fe2bc2d2 | 2020-10-27 11:03:32 +0200 | [diff] [blame] | 84 | mutex_init(&ctx->mmu_lock); |
| 85 | |
Moti Haimovski | ccf979e | 2020-10-05 17:59:29 +0300 | [diff] [blame] | 86 | if (hdev->mmu_func[MMU_DR_PGT].ctx_init != NULL) { |
| 87 | rc = hdev->mmu_func[MMU_DR_PGT].ctx_init(ctx); |
| 88 | if (rc) |
| 89 | return rc; |
| 90 | } |
| 91 | |
| 92 | if (hdev->mmu_func[MMU_HR_PGT].ctx_init != NULL) |
| 93 | rc = hdev->mmu_func[MMU_HR_PGT].ctx_init(ctx); |
| 94 | |
| 95 | return rc; |
Moti Haimovski | d83fe66 | 2020-08-12 13:33:44 +0300 | [diff] [blame] | 96 | } |
| 97 | |
| 98 | /* |
| 99 | * hl_mmu_ctx_fini - disable a ctx from using the mmu module |
| 100 | * |
| 101 | * @ctx: pointer to the context structure |
| 102 | * |
| 103 | * This function does the following: |
| 104 | * - Free any pgts which were not freed yet |
| 105 | * - Free the mutex |
| 106 | * - Free DRAM default page mapping hops |
| 107 | */ |
| 108 | void hl_mmu_ctx_fini(struct hl_ctx *ctx) |
| 109 | { |
| 110 | struct hl_device *hdev = ctx->hdev; |
| 111 | |
Moti Haimovski | ccf979e | 2020-10-05 17:59:29 +0300 | [diff] [blame] | 112 | if (!hdev->mmu_enable) |
| 113 | return; |
| 114 | |
| 115 | if (hdev->mmu_func[MMU_DR_PGT].ctx_fini != NULL) |
| 116 | hdev->mmu_func[MMU_DR_PGT].ctx_fini(ctx); |
| 117 | |
| 118 | if (hdev->mmu_func[MMU_HR_PGT].ctx_fini != NULL) |
| 119 | hdev->mmu_func[MMU_HR_PGT].ctx_fini(ctx); |
Moti Haimovski | fe2bc2d2 | 2020-10-27 11:03:32 +0200 | [diff] [blame] | 120 | |
| 121 | mutex_destroy(&ctx->mmu_lock); |
Moti Haimovski | d83fe66 | 2020-08-12 13:33:44 +0300 | [diff] [blame] | 122 | } |
| 123 | |
| 124 | /* |
Ofir Bitton | 5c05487 | 2020-10-22 15:13:10 +0300 | [diff] [blame] | 125 | * hl_mmu_unmap_page - unmaps a virtual addr |
Moti Haimovski | d83fe66 | 2020-08-12 13:33:44 +0300 | [diff] [blame] | 126 | * |
| 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 |
| 131 | * |
| 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 |
| 136 | * |
| 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 |
| 141 | * |
| 142 | * For optimization reasons PCI flush may be requested once after unmapping of |
| 143 | * large area. |
| 144 | */ |
Ofir Bitton | 5c05487 | 2020-10-22 15:13:10 +0300 | [diff] [blame] | 145 | int hl_mmu_unmap_page(struct hl_ctx *ctx, u64 virt_addr, u32 page_size, |
Moti Haimovski | d83fe66 | 2020-08-12 13:33:44 +0300 | [diff] [blame] | 146 | bool flush_pte) |
| 147 | { |
| 148 | struct hl_device *hdev = ctx->hdev; |
| 149 | struct asic_fixed_properties *prop = &hdev->asic_prop; |
| 150 | struct hl_mmu_properties *mmu_prop; |
| 151 | u64 real_virt_addr; |
| 152 | u32 real_page_size, npages; |
Moti Haimovski | ccf979e | 2020-10-05 17:59:29 +0300 | [diff] [blame] | 153 | int i, rc = 0, pgt_residency; |
Moti Haimovski | d83fe66 | 2020-08-12 13:33:44 +0300 | [diff] [blame] | 154 | bool is_dram_addr; |
| 155 | |
| 156 | if (!hdev->mmu_enable) |
| 157 | return 0; |
| 158 | |
Oded Gabbay | 9488307 | 2021-01-11 17:49:30 +0200 | [diff] [blame] | 159 | is_dram_addr = hl_is_dram_va(hdev, virt_addr); |
Moti Haimovski | d83fe66 | 2020-08-12 13:33:44 +0300 | [diff] [blame] | 160 | |
| 161 | if (is_dram_addr) |
| 162 | mmu_prop = &prop->dmmu; |
| 163 | else if ((page_size % prop->pmmu_huge.page_size) == 0) |
| 164 | mmu_prop = &prop->pmmu_huge; |
| 165 | else |
| 166 | mmu_prop = &prop->pmmu; |
| 167 | |
Moti Haimovski | ccf979e | 2020-10-05 17:59:29 +0300 | [diff] [blame] | 168 | pgt_residency = mmu_prop->host_resident ? MMU_HR_PGT : MMU_DR_PGT; |
Moti Haimovski | d83fe66 | 2020-08-12 13:33:44 +0300 | [diff] [blame] | 169 | /* |
| 170 | * The H/W handles mapping of specific page sizes. Hence if the page |
| 171 | * size is bigger, we break it to sub-pages and unmap them separately. |
| 172 | */ |
| 173 | if ((page_size % mmu_prop->page_size) == 0) { |
| 174 | real_page_size = mmu_prop->page_size; |
| 175 | } else { |
Moti Haimovski | b19dc67 | 2020-11-18 20:15:29 +0200 | [diff] [blame] | 176 | /* |
| 177 | * MMU page size may differ from DRAM page size. |
| 178 | * In such case work with the DRAM page size and let the MMU |
| 179 | * scrambling routine to handle this mismatch when |
| 180 | * calculating the address to remove from the MMU page table |
| 181 | */ |
| 182 | if (is_dram_addr && ((page_size % prop->dram_page_size) == 0)) { |
| 183 | real_page_size = prop->dram_page_size; |
| 184 | } else { |
| 185 | dev_err(hdev->dev, |
| 186 | "page size of %u is not %uKB aligned, can't unmap\n", |
| 187 | page_size, mmu_prop->page_size >> 10); |
Moti Haimovski | d83fe66 | 2020-08-12 13:33:44 +0300 | [diff] [blame] | 188 | |
Moti Haimovski | b19dc67 | 2020-11-18 20:15:29 +0200 | [diff] [blame] | 189 | return -EFAULT; |
| 190 | } |
Moti Haimovski | d83fe66 | 2020-08-12 13:33:44 +0300 | [diff] [blame] | 191 | } |
| 192 | |
| 193 | npages = page_size / real_page_size; |
| 194 | real_virt_addr = virt_addr; |
| 195 | |
| 196 | for (i = 0 ; i < npages ; i++) { |
Moti Haimovski | ccf979e | 2020-10-05 17:59:29 +0300 | [diff] [blame] | 197 | rc = hdev->mmu_func[pgt_residency].unmap(ctx, |
| 198 | real_virt_addr, is_dram_addr); |
Moti Haimovski | d83fe66 | 2020-08-12 13:33:44 +0300 | [diff] [blame] | 199 | if (rc) |
| 200 | break; |
| 201 | |
| 202 | real_virt_addr += real_page_size; |
| 203 | } |
| 204 | |
| 205 | if (flush_pte) |
Moti Haimovski | ccf979e | 2020-10-05 17:59:29 +0300 | [diff] [blame] | 206 | hdev->mmu_func[pgt_residency].flush(ctx); |
Moti Haimovski | d83fe66 | 2020-08-12 13:33:44 +0300 | [diff] [blame] | 207 | |
| 208 | return rc; |
| 209 | } |
| 210 | |
| 211 | /* |
Ofir Bitton | 5c05487 | 2020-10-22 15:13:10 +0300 | [diff] [blame] | 212 | * hl_mmu_map_page - maps a virtual addr to physical addr |
Moti Haimovski | d83fe66 | 2020-08-12 13:33:44 +0300 | [diff] [blame] | 213 | * |
| 214 | * @ctx: pointer to the context structure |
| 215 | * @virt_addr: virt addr to map from |
| 216 | * @phys_addr: phys addr to map to |
| 217 | * @page_size: physical page size |
| 218 | * @flush_pte: whether to do a PCI flush |
| 219 | * |
| 220 | * This function does the following: |
| 221 | * - Check that the virt addr is not mapped |
| 222 | * - Allocate pgts as necessary in order to map the virt addr to the phys |
| 223 | * - Returns 0 on success, -EINVAL if addr is already mapped, or -ENOMEM. |
| 224 | * |
| 225 | * Because this function changes the page tables in the device and because it |
| 226 | * changes the MMU hash, it must be protected by a lock. |
| 227 | * However, because it maps only a single page, the lock should be implemented |
| 228 | * in a higher level in order to protect the entire mapping of the memory area |
| 229 | * |
| 230 | * For optimization reasons PCI flush may be requested once after mapping of |
| 231 | * large area. |
| 232 | */ |
Ofir Bitton | 5c05487 | 2020-10-22 15:13:10 +0300 | [diff] [blame] | 233 | int hl_mmu_map_page(struct hl_ctx *ctx, u64 virt_addr, u64 phys_addr, |
| 234 | u32 page_size, bool flush_pte) |
Moti Haimovski | d83fe66 | 2020-08-12 13:33:44 +0300 | [diff] [blame] | 235 | { |
| 236 | struct hl_device *hdev = ctx->hdev; |
| 237 | struct asic_fixed_properties *prop = &hdev->asic_prop; |
| 238 | struct hl_mmu_properties *mmu_prop; |
| 239 | u64 real_virt_addr, real_phys_addr; |
| 240 | u32 real_page_size, npages; |
Moti Haimovski | ccf979e | 2020-10-05 17:59:29 +0300 | [diff] [blame] | 241 | int i, rc, pgt_residency, mapped_cnt = 0; |
Moti Haimovski | d83fe66 | 2020-08-12 13:33:44 +0300 | [diff] [blame] | 242 | bool is_dram_addr; |
| 243 | |
Moti Haimovski | ccf979e | 2020-10-05 17:59:29 +0300 | [diff] [blame] | 244 | |
Moti Haimovski | d83fe66 | 2020-08-12 13:33:44 +0300 | [diff] [blame] | 245 | if (!hdev->mmu_enable) |
| 246 | return 0; |
| 247 | |
Oded Gabbay | 9488307 | 2021-01-11 17:49:30 +0200 | [diff] [blame] | 248 | is_dram_addr = hl_is_dram_va(hdev, virt_addr); |
Moti Haimovski | d83fe66 | 2020-08-12 13:33:44 +0300 | [diff] [blame] | 249 | |
| 250 | if (is_dram_addr) |
| 251 | mmu_prop = &prop->dmmu; |
| 252 | else if ((page_size % prop->pmmu_huge.page_size) == 0) |
| 253 | mmu_prop = &prop->pmmu_huge; |
| 254 | else |
| 255 | mmu_prop = &prop->pmmu; |
| 256 | |
Moti Haimovski | ccf979e | 2020-10-05 17:59:29 +0300 | [diff] [blame] | 257 | pgt_residency = mmu_prop->host_resident ? MMU_HR_PGT : MMU_DR_PGT; |
| 258 | |
Moti Haimovski | d83fe66 | 2020-08-12 13:33:44 +0300 | [diff] [blame] | 259 | /* |
| 260 | * The H/W handles mapping of specific page sizes. Hence if the page |
| 261 | * size is bigger, we break it to sub-pages and map them separately. |
| 262 | */ |
| 263 | if ((page_size % mmu_prop->page_size) == 0) { |
| 264 | real_page_size = mmu_prop->page_size; |
Moti Haimovski | b19dc67 | 2020-11-18 20:15:29 +0200 | [diff] [blame] | 265 | } else if (is_dram_addr && ((page_size % prop->dram_page_size) == 0) && |
| 266 | (prop->dram_page_size < mmu_prop->page_size)) { |
| 267 | /* |
| 268 | * MMU page size may differ from DRAM page size. |
| 269 | * In such case work with the DRAM page size and let the MMU |
| 270 | * scrambling routine handle this mismatch when calculating |
| 271 | * the address to place in the MMU page table. (in that case |
| 272 | * also make sure that the dram_page_size smaller than the |
| 273 | * mmu page size) |
| 274 | */ |
| 275 | real_page_size = prop->dram_page_size; |
Moti Haimovski | d83fe66 | 2020-08-12 13:33:44 +0300 | [diff] [blame] | 276 | } else { |
| 277 | dev_err(hdev->dev, |
Moti Haimovski | a6722d6 | 2020-10-05 19:33:10 +0300 | [diff] [blame] | 278 | "page size of %u is not %uKB aligned, can't map\n", |
Moti Haimovski | d83fe66 | 2020-08-12 13:33:44 +0300 | [diff] [blame] | 279 | page_size, mmu_prop->page_size >> 10); |
| 280 | |
| 281 | return -EFAULT; |
| 282 | } |
| 283 | |
Moti Haimovski | b19dc67 | 2020-11-18 20:15:29 +0200 | [diff] [blame] | 284 | /* |
| 285 | * Verify that the phys and virt addresses are aligned with the |
| 286 | * MMU page size (in dram this means checking the address and MMU |
| 287 | * after scrambling) |
| 288 | */ |
| 289 | if ((is_dram_addr && |
farah kassabri | 89473a1 | 2021-01-12 17:24:00 +0200 | [diff] [blame] | 290 | ((hdev->asic_funcs->scramble_addr(hdev, phys_addr) & |
Moti Haimovski | b19dc67 | 2020-11-18 20:15:29 +0200 | [diff] [blame] | 291 | (mmu_prop->page_size - 1)) || |
farah kassabri | 89473a1 | 2021-01-12 17:24:00 +0200 | [diff] [blame] | 292 | (hdev->asic_funcs->scramble_addr(hdev, virt_addr) & |
Moti Haimovski | b19dc67 | 2020-11-18 20:15:29 +0200 | [diff] [blame] | 293 | (mmu_prop->page_size - 1)))) || |
| 294 | (!is_dram_addr && ((phys_addr & (real_page_size - 1)) || |
| 295 | (virt_addr & (real_page_size - 1))))) |
Alon Mizrahi | 75d9a2a | 2020-12-03 17:32:19 +0200 | [diff] [blame] | 296 | dev_crit(hdev->dev, |
Moti Haimovski | b19dc67 | 2020-11-18 20:15:29 +0200 | [diff] [blame] | 297 | "Mapping address 0x%llx with virtual address 0x%llx and page size of 0x%x is erroneous! Addresses must be divisible by page size", |
| 298 | phys_addr, virt_addr, real_page_size); |
Moti Haimovski | d83fe66 | 2020-08-12 13:33:44 +0300 | [diff] [blame] | 299 | |
| 300 | npages = page_size / real_page_size; |
| 301 | real_virt_addr = virt_addr; |
| 302 | real_phys_addr = phys_addr; |
| 303 | |
| 304 | for (i = 0 ; i < npages ; i++) { |
Moti Haimovski | ccf979e | 2020-10-05 17:59:29 +0300 | [diff] [blame] | 305 | rc = hdev->mmu_func[pgt_residency].map(ctx, |
| 306 | real_virt_addr, real_phys_addr, |
| 307 | real_page_size, is_dram_addr); |
Moti Haimovski | d83fe66 | 2020-08-12 13:33:44 +0300 | [diff] [blame] | 308 | if (rc) |
| 309 | goto err; |
| 310 | |
| 311 | real_virt_addr += real_page_size; |
| 312 | real_phys_addr += real_page_size; |
| 313 | mapped_cnt++; |
| 314 | } |
| 315 | |
| 316 | if (flush_pte) |
Moti Haimovski | ccf979e | 2020-10-05 17:59:29 +0300 | [diff] [blame] | 317 | hdev->mmu_func[pgt_residency].flush(ctx); |
Moti Haimovski | d83fe66 | 2020-08-12 13:33:44 +0300 | [diff] [blame] | 318 | |
| 319 | return 0; |
| 320 | |
| 321 | err: |
| 322 | real_virt_addr = virt_addr; |
| 323 | for (i = 0 ; i < mapped_cnt ; i++) { |
Moti Haimovski | ccf979e | 2020-10-05 17:59:29 +0300 | [diff] [blame] | 324 | if (hdev->mmu_func[pgt_residency].unmap(ctx, |
| 325 | real_virt_addr, is_dram_addr)) |
Moti Haimovski | d83fe66 | 2020-08-12 13:33:44 +0300 | [diff] [blame] | 326 | dev_warn_ratelimited(hdev->dev, |
| 327 | "failed to unmap va: 0x%llx\n", real_virt_addr); |
| 328 | |
| 329 | real_virt_addr += real_page_size; |
| 330 | } |
| 331 | |
Moti Haimovski | ccf979e | 2020-10-05 17:59:29 +0300 | [diff] [blame] | 332 | hdev->mmu_func[pgt_residency].flush(ctx); |
Moti Haimovski | d83fe66 | 2020-08-12 13:33:44 +0300 | [diff] [blame] | 333 | |
| 334 | return rc; |
| 335 | } |
| 336 | |
| 337 | /* |
Ofir Bitton | 5c05487 | 2020-10-22 15:13:10 +0300 | [diff] [blame] | 338 | * hl_mmu_map_contiguous - implements a wrapper for hl_mmu_map_page |
| 339 | * for mapping contiguous physical memory |
| 340 | * |
| 341 | * @ctx: pointer to the context structure |
| 342 | * @virt_addr: virt addr to map from |
| 343 | * @phys_addr: phys addr to map to |
| 344 | * @size: size to map |
| 345 | * |
| 346 | */ |
| 347 | int hl_mmu_map_contiguous(struct hl_ctx *ctx, u64 virt_addr, |
| 348 | u64 phys_addr, u32 size) |
| 349 | { |
| 350 | struct hl_device *hdev = ctx->hdev; |
| 351 | struct asic_fixed_properties *prop = &hdev->asic_prop; |
| 352 | u64 curr_va, curr_pa; |
| 353 | u32 page_size; |
| 354 | bool flush_pte; |
| 355 | int rc = 0, off; |
| 356 | |
| 357 | if (hl_mem_area_inside_range(virt_addr, size, |
| 358 | prop->dmmu.start_addr, prop->dmmu.end_addr)) |
| 359 | page_size = prop->dmmu.page_size; |
| 360 | else if (hl_mem_area_inside_range(virt_addr, size, |
| 361 | prop->pmmu.start_addr, prop->pmmu.end_addr)) |
| 362 | page_size = prop->pmmu.page_size; |
| 363 | else if (hl_mem_area_inside_range(virt_addr, size, |
| 364 | prop->pmmu_huge.start_addr, prop->pmmu_huge.end_addr)) |
| 365 | page_size = prop->pmmu_huge.page_size; |
| 366 | else |
| 367 | return -EINVAL; |
| 368 | |
| 369 | for (off = 0 ; off < size ; off += page_size) { |
| 370 | curr_va = virt_addr + off; |
| 371 | curr_pa = phys_addr + off; |
| 372 | flush_pte = (off + page_size) >= size; |
| 373 | rc = hl_mmu_map_page(ctx, curr_va, curr_pa, page_size, |
| 374 | flush_pte); |
| 375 | if (rc) { |
| 376 | dev_err(hdev->dev, |
| 377 | "Map failed for va 0x%llx to pa 0x%llx\n", |
| 378 | curr_va, curr_pa); |
| 379 | goto unmap; |
| 380 | } |
| 381 | } |
| 382 | |
| 383 | return rc; |
| 384 | |
| 385 | unmap: |
| 386 | for (; off >= 0 ; off -= page_size) { |
| 387 | curr_va = virt_addr + off; |
| 388 | flush_pte = (off - (s32) page_size) < 0; |
| 389 | if (hl_mmu_unmap_page(ctx, curr_va, page_size, flush_pte)) |
| 390 | dev_warn_ratelimited(hdev->dev, |
| 391 | "failed to unmap va 0x%llx\n", curr_va); |
| 392 | } |
| 393 | |
| 394 | return rc; |
| 395 | } |
| 396 | |
| 397 | /* |
| 398 | * hl_mmu_unmap_contiguous - implements a wrapper for hl_mmu_unmap_page |
| 399 | * for unmapping contiguous physical memory |
| 400 | * |
| 401 | * @ctx: pointer to the context structure |
| 402 | * @virt_addr: virt addr to unmap |
| 403 | * @size: size to unmap |
| 404 | * |
| 405 | */ |
| 406 | int hl_mmu_unmap_contiguous(struct hl_ctx *ctx, u64 virt_addr, u32 size) |
| 407 | { |
| 408 | struct hl_device *hdev = ctx->hdev; |
| 409 | struct asic_fixed_properties *prop = &hdev->asic_prop; |
| 410 | u64 curr_va; |
| 411 | u32 page_size; |
| 412 | bool flush_pte; |
| 413 | int rc = 0, off; |
| 414 | |
| 415 | if (hl_mem_area_inside_range(virt_addr, size, |
| 416 | prop->dmmu.start_addr, prop->dmmu.end_addr)) |
| 417 | page_size = prop->dmmu.page_size; |
| 418 | else if (hl_mem_area_inside_range(virt_addr, size, |
| 419 | prop->pmmu.start_addr, prop->pmmu.end_addr)) |
| 420 | page_size = prop->pmmu.page_size; |
| 421 | else if (hl_mem_area_inside_range(virt_addr, size, |
| 422 | prop->pmmu_huge.start_addr, prop->pmmu_huge.end_addr)) |
| 423 | page_size = prop->pmmu_huge.page_size; |
| 424 | else |
| 425 | return -EINVAL; |
| 426 | |
| 427 | for (off = 0 ; off < size ; off += page_size) { |
| 428 | curr_va = virt_addr + off; |
| 429 | flush_pte = (off + page_size) >= size; |
| 430 | rc = hl_mmu_unmap_page(ctx, curr_va, page_size, flush_pte); |
| 431 | if (rc) |
| 432 | dev_warn_ratelimited(hdev->dev, |
| 433 | "Unmap failed for va 0x%llx\n", curr_va); |
| 434 | } |
| 435 | |
| 436 | return rc; |
| 437 | } |
| 438 | |
| 439 | /* |
Moti Haimovski | d83fe66 | 2020-08-12 13:33:44 +0300 | [diff] [blame] | 440 | * hl_mmu_swap_out - marks all mapping of the given ctx as swapped out |
| 441 | * |
| 442 | * @ctx: pointer to the context structure |
| 443 | * |
| 444 | */ |
| 445 | void hl_mmu_swap_out(struct hl_ctx *ctx) |
| 446 | { |
| 447 | struct hl_device *hdev = ctx->hdev; |
| 448 | |
Moti Haimovski | ccf979e | 2020-10-05 17:59:29 +0300 | [diff] [blame] | 449 | if (!hdev->mmu_enable) |
| 450 | return; |
| 451 | |
| 452 | if (hdev->mmu_func[MMU_DR_PGT].swap_out != NULL) |
| 453 | hdev->mmu_func[MMU_DR_PGT].swap_out(ctx); |
| 454 | |
| 455 | if (hdev->mmu_func[MMU_HR_PGT].swap_out != NULL) |
| 456 | hdev->mmu_func[MMU_HR_PGT].swap_out(ctx); |
Moti Haimovski | d83fe66 | 2020-08-12 13:33:44 +0300 | [diff] [blame] | 457 | } |
| 458 | |
| 459 | /* |
| 460 | * hl_mmu_swap_in - marks all mapping of the given ctx as swapped in |
| 461 | * |
| 462 | * @ctx: pointer to the context structure |
| 463 | * |
| 464 | */ |
| 465 | void hl_mmu_swap_in(struct hl_ctx *ctx) |
| 466 | { |
| 467 | struct hl_device *hdev = ctx->hdev; |
| 468 | |
Moti Haimovski | ccf979e | 2020-10-05 17:59:29 +0300 | [diff] [blame] | 469 | if (!hdev->mmu_enable) |
| 470 | return; |
| 471 | |
| 472 | if (hdev->mmu_func[MMU_DR_PGT].swap_in != NULL) |
| 473 | hdev->mmu_func[MMU_DR_PGT].swap_in(ctx); |
| 474 | |
| 475 | if (hdev->mmu_func[MMU_HR_PGT].swap_in != NULL) |
| 476 | hdev->mmu_func[MMU_HR_PGT].swap_in(ctx); |
Moti Haimovski | d83fe66 | 2020-08-12 13:33:44 +0300 | [diff] [blame] | 477 | } |
| 478 | |
farah kassabri | 89473a1 | 2021-01-12 17:24:00 +0200 | [diff] [blame] | 479 | static void hl_mmu_pa_page_with_offset(struct hl_ctx *ctx, u64 virt_addr, |
| 480 | struct hl_mmu_hop_info *hops, |
| 481 | u64 *phys_addr) |
| 482 | { |
| 483 | struct hl_device *hdev = ctx->hdev; |
| 484 | struct asic_fixed_properties *prop = &hdev->asic_prop; |
| 485 | u64 offset_mask, addr_mask, hop_shift, tmp_phys_addr; |
| 486 | u32 hop0_shift_off; |
| 487 | void *p; |
| 488 | |
| 489 | /* last hop holds the phys address and flags */ |
| 490 | if (hops->unscrambled_paddr) |
| 491 | tmp_phys_addr = hops->unscrambled_paddr; |
| 492 | else |
| 493 | tmp_phys_addr = hops->hop_info[hops->used_hops - 1].hop_pte_val; |
| 494 | |
| 495 | if (hops->range_type == HL_VA_RANGE_TYPE_HOST_HUGE) |
| 496 | p = &prop->pmmu_huge; |
| 497 | else if (hops->range_type == HL_VA_RANGE_TYPE_HOST) |
| 498 | p = &prop->pmmu; |
| 499 | else /* HL_VA_RANGE_TYPE_DRAM */ |
| 500 | p = &prop->dmmu; |
| 501 | |
farah kassabri | 15097e9 | 2021-02-28 11:06:14 +0200 | [diff] [blame] | 502 | if ((hops->range_type == HL_VA_RANGE_TYPE_DRAM) && |
| 503 | !is_power_of_2(prop->dram_page_size)) { |
Yuri Nudelman | dd08335 | 2021-09-30 11:18:01 +0300 | [diff] [blame] | 504 | u64 dram_page_size, dram_base, abs_phys_addr, abs_virt_addr, |
| 505 | page_id, page_start; |
| 506 | u32 page_off; |
farah kassabri | 15097e9 | 2021-02-28 11:06:14 +0200 | [diff] [blame] | 507 | |
Ohad Sharabi | 0f37510 | 2021-06-03 00:24:32 +0300 | [diff] [blame] | 508 | /* |
Yuri Nudelman | dd08335 | 2021-09-30 11:18:01 +0300 | [diff] [blame] | 509 | * Bit arithmetics cannot be used for non power of two page |
| 510 | * sizes. In addition, since bit arithmetics is not used, |
| 511 | * we cannot ignore dram base. All that shall be considerd. |
Ohad Sharabi | 0f37510 | 2021-06-03 00:24:32 +0300 | [diff] [blame] | 512 | */ |
Yuri Nudelman | dd08335 | 2021-09-30 11:18:01 +0300 | [diff] [blame] | 513 | |
| 514 | dram_page_size = prop->dram_page_size; |
| 515 | dram_base = prop->dram_base_address; |
| 516 | abs_phys_addr = tmp_phys_addr - dram_base; |
| 517 | abs_virt_addr = virt_addr - dram_base; |
| 518 | page_id = DIV_ROUND_DOWN_ULL(abs_phys_addr, dram_page_size); |
| 519 | page_start = page_id * dram_page_size; |
| 520 | div_u64_rem(abs_virt_addr, dram_page_size, &page_off); |
| 521 | |
| 522 | *phys_addr = page_start + page_off + dram_base; |
farah kassabri | 15097e9 | 2021-02-28 11:06:14 +0200 | [diff] [blame] | 523 | } else { |
| 524 | /* |
| 525 | * find the correct hop shift field in hl_mmu_properties |
| 526 | * structure in order to determine the right masks |
| 527 | * for the page offset. |
| 528 | */ |
| 529 | hop0_shift_off = offsetof(struct hl_mmu_properties, hop0_shift); |
| 530 | p = (char *)p + hop0_shift_off; |
| 531 | p = (char *)p + ((hops->used_hops - 1) * sizeof(u64)); |
| 532 | hop_shift = *(u64 *)p; |
| 533 | offset_mask = (1ull << hop_shift) - 1; |
| 534 | addr_mask = ~(offset_mask); |
| 535 | *phys_addr = (tmp_phys_addr & addr_mask) | |
| 536 | (virt_addr & offset_mask); |
| 537 | } |
farah kassabri | 89473a1 | 2021-01-12 17:24:00 +0200 | [diff] [blame] | 538 | } |
| 539 | |
Moti Haimovski | 00e1b59 | 2020-10-27 10:55:42 +0200 | [diff] [blame] | 540 | int hl_mmu_va_to_pa(struct hl_ctx *ctx, u64 virt_addr, u64 *phys_addr) |
| 541 | { |
| 542 | struct hl_mmu_hop_info hops; |
Moti Haimovski | 00e1b59 | 2020-10-27 10:55:42 +0200 | [diff] [blame] | 543 | int rc; |
| 544 | |
Ofir Bitton | a5778d1 | 2021-02-24 11:51:40 +0200 | [diff] [blame] | 545 | memset(&hops, 0, sizeof(hops)); |
| 546 | |
Moti Haimovski | 00e1b59 | 2020-10-27 10:55:42 +0200 | [diff] [blame] | 547 | rc = hl_mmu_get_tlb_info(ctx, virt_addr, &hops); |
| 548 | if (rc) |
| 549 | return rc; |
| 550 | |
farah kassabri | 89473a1 | 2021-01-12 17:24:00 +0200 | [diff] [blame] | 551 | hl_mmu_pa_page_with_offset(ctx, virt_addr, &hops, phys_addr); |
Moti Haimovski | 00e1b59 | 2020-10-27 10:55:42 +0200 | [diff] [blame] | 552 | |
| 553 | return 0; |
| 554 | } |
| 555 | |
| 556 | int hl_mmu_get_tlb_info(struct hl_ctx *ctx, u64 virt_addr, |
| 557 | struct hl_mmu_hop_info *hops) |
| 558 | { |
| 559 | struct hl_device *hdev = ctx->hdev; |
| 560 | struct asic_fixed_properties *prop = &hdev->asic_prop; |
| 561 | struct hl_mmu_properties *mmu_prop; |
| 562 | int rc; |
| 563 | bool is_dram_addr; |
| 564 | |
| 565 | if (!hdev->mmu_enable) |
| 566 | return -EOPNOTSUPP; |
| 567 | |
Moti Haimovski | b19dc67 | 2020-11-18 20:15:29 +0200 | [diff] [blame] | 568 | hops->scrambled_vaddr = virt_addr; /* assume no scrambling */ |
| 569 | |
Moti Haimovski | 00e1b59 | 2020-10-27 10:55:42 +0200 | [diff] [blame] | 570 | is_dram_addr = hl_mem_area_inside_range(virt_addr, prop->dmmu.page_size, |
| 571 | prop->dmmu.start_addr, |
| 572 | prop->dmmu.end_addr); |
| 573 | |
| 574 | /* host-residency is the same in PMMU and HPMMU, use one of them */ |
| 575 | mmu_prop = is_dram_addr ? &prop->dmmu : &prop->pmmu; |
| 576 | |
| 577 | mutex_lock(&ctx->mmu_lock); |
| 578 | |
| 579 | if (mmu_prop->host_resident) |
| 580 | rc = hdev->mmu_func[MMU_HR_PGT].get_tlb_info(ctx, |
| 581 | virt_addr, hops); |
| 582 | else |
| 583 | rc = hdev->mmu_func[MMU_DR_PGT].get_tlb_info(ctx, |
| 584 | virt_addr, hops); |
| 585 | |
| 586 | mutex_unlock(&ctx->mmu_lock); |
| 587 | |
farah kassabri | 89473a1 | 2021-01-12 17:24:00 +0200 | [diff] [blame] | 588 | /* add page offset to physical address */ |
| 589 | if (hops->unscrambled_paddr) |
| 590 | hl_mmu_pa_page_with_offset(ctx, virt_addr, hops, |
| 591 | &hops->unscrambled_paddr); |
| 592 | |
Moti Haimovski | 00e1b59 | 2020-10-27 10:55:42 +0200 | [diff] [blame] | 593 | return rc; |
| 594 | } |
| 595 | |
Moti Haimovski | d83fe66 | 2020-08-12 13:33:44 +0300 | [diff] [blame] | 596 | int hl_mmu_if_set_funcs(struct hl_device *hdev) |
| 597 | { |
| 598 | if (!hdev->mmu_enable) |
| 599 | return 0; |
| 600 | |
| 601 | switch (hdev->asic_type) { |
| 602 | case ASIC_GOYA: |
| 603 | case ASIC_GAUDI: |
Ofir Bitton | e5042a6 | 2021-04-01 13:43:40 +0300 | [diff] [blame] | 604 | case ASIC_GAUDI_SEC: |
Moti Haimovski | ccf979e | 2020-10-05 17:59:29 +0300 | [diff] [blame] | 605 | hl_mmu_v1_set_funcs(hdev, &hdev->mmu_func[MMU_DR_PGT]); |
Moti Haimovski | d83fe66 | 2020-08-12 13:33:44 +0300 | [diff] [blame] | 606 | break; |
| 607 | default: |
| 608 | dev_err(hdev->dev, "Unrecognized ASIC type %d\n", |
| 609 | hdev->asic_type); |
| 610 | return -EOPNOTSUPP; |
| 611 | } |
| 612 | |
| 613 | return 0; |
| 614 | } |
Moti Haimovski | b19dc67 | 2020-11-18 20:15:29 +0200 | [diff] [blame] | 615 | |
| 616 | /** |
farah kassabri | 89473a1 | 2021-01-12 17:24:00 +0200 | [diff] [blame] | 617 | * hl_mmu_scramble_addr() - The generic mmu address scrambling routine. |
Moti Haimovski | b19dc67 | 2020-11-18 20:15:29 +0200 | [diff] [blame] | 618 | * @hdev: pointer to device data. |
farah kassabri | 89473a1 | 2021-01-12 17:24:00 +0200 | [diff] [blame] | 619 | * @addr: The address to scramble. |
Moti Haimovski | b19dc67 | 2020-11-18 20:15:29 +0200 | [diff] [blame] | 620 | * |
farah kassabri | 89473a1 | 2021-01-12 17:24:00 +0200 | [diff] [blame] | 621 | * Return: The scrambled address. |
Moti Haimovski | b19dc67 | 2020-11-18 20:15:29 +0200 | [diff] [blame] | 622 | */ |
farah kassabri | 89473a1 | 2021-01-12 17:24:00 +0200 | [diff] [blame] | 623 | u64 hl_mmu_scramble_addr(struct hl_device *hdev, u64 addr) |
Moti Haimovski | b19dc67 | 2020-11-18 20:15:29 +0200 | [diff] [blame] | 624 | { |
farah kassabri | 89473a1 | 2021-01-12 17:24:00 +0200 | [diff] [blame] | 625 | return addr; |
| 626 | } |
| 627 | |
| 628 | /** |
| 629 | * hl_mmu_descramble_addr() - The generic mmu address descrambling |
| 630 | * routine. |
| 631 | * @hdev: pointer to device data. |
| 632 | * @addr: The address to descramble. |
| 633 | * |
| 634 | * Return: The un-scrambled address. |
| 635 | */ |
| 636 | u64 hl_mmu_descramble_addr(struct hl_device *hdev, u64 addr) |
| 637 | { |
| 638 | return addr; |
Moti Haimovski | b19dc67 | 2020-11-18 20:15:29 +0200 | [diff] [blame] | 639 | } |