|
4 | 4 | * Author: Jintack Lim <[email protected]>
|
5 | 5 | */
|
6 | 6 |
|
| 7 | +#include <linux/bitfield.h> |
7 | 8 | #include <linux/kvm.h>
|
8 | 9 | #include <linux/kvm_host.h>
|
9 | 10 |
|
@@ -420,12 +421,94 @@ static unsigned int ttl_to_size(u8 ttl)
|
420 | 421 | return max_size;
|
421 | 422 | }
|
422 | 423 |
|
| 424 | +/* |
| 425 | + * Compute the equivalent of the TTL field by parsing the shadow PT. The |
| 426 | + * granule size is extracted from the cached VTCR_EL2.TG0 while the level is |
| 427 | + * retrieved from first entry carrying the level as a tag. |
| 428 | + */ |
| 429 | +static u8 get_guest_mapping_ttl(struct kvm_s2_mmu *mmu, u64 addr) |
| 430 | +{ |
| 431 | + u64 tmp, sz = 0, vtcr = mmu->tlb_vtcr; |
| 432 | + kvm_pte_t pte; |
| 433 | + u8 ttl, level; |
| 434 | + |
| 435 | + lockdep_assert_held_write(&kvm_s2_mmu_to_kvm(mmu)->mmu_lock); |
| 436 | + |
| 437 | + switch (vtcr & VTCR_EL2_TG0_MASK) { |
| 438 | + case VTCR_EL2_TG0_4K: |
| 439 | + ttl = (TLBI_TTL_TG_4K << 2); |
| 440 | + break; |
| 441 | + case VTCR_EL2_TG0_16K: |
| 442 | + ttl = (TLBI_TTL_TG_16K << 2); |
| 443 | + break; |
| 444 | + case VTCR_EL2_TG0_64K: |
| 445 | + default: /* IMPDEF: treat any other value as 64k */ |
| 446 | + ttl = (TLBI_TTL_TG_64K << 2); |
| 447 | + break; |
| 448 | + } |
| 449 | + |
| 450 | + tmp = addr; |
| 451 | + |
| 452 | +again: |
| 453 | + /* Iteratively compute the block sizes for a particular granule size */ |
| 454 | + switch (vtcr & VTCR_EL2_TG0_MASK) { |
| 455 | + case VTCR_EL2_TG0_4K: |
| 456 | + if (sz < SZ_4K) sz = SZ_4K; |
| 457 | + else if (sz < SZ_2M) sz = SZ_2M; |
| 458 | + else if (sz < SZ_1G) sz = SZ_1G; |
| 459 | + else sz = 0; |
| 460 | + break; |
| 461 | + case VTCR_EL2_TG0_16K: |
| 462 | + if (sz < SZ_16K) sz = SZ_16K; |
| 463 | + else if (sz < SZ_32M) sz = SZ_32M; |
| 464 | + else sz = 0; |
| 465 | + break; |
| 466 | + case VTCR_EL2_TG0_64K: |
| 467 | + default: /* IMPDEF: treat any other value as 64k */ |
| 468 | + if (sz < SZ_64K) sz = SZ_64K; |
| 469 | + else if (sz < SZ_512M) sz = SZ_512M; |
| 470 | + else sz = 0; |
| 471 | + break; |
| 472 | + } |
| 473 | + |
| 474 | + if (sz == 0) |
| 475 | + return 0; |
| 476 | + |
| 477 | + tmp &= ~(sz - 1); |
| 478 | + if (kvm_pgtable_get_leaf(mmu->pgt, tmp, &pte, NULL)) |
| 479 | + goto again; |
| 480 | + if (!(pte & PTE_VALID)) |
| 481 | + goto again; |
| 482 | + level = FIELD_GET(KVM_NV_GUEST_MAP_SZ, pte); |
| 483 | + if (!level) |
| 484 | + goto again; |
| 485 | + |
| 486 | + ttl |= level; |
| 487 | + |
| 488 | + /* |
| 489 | + * We now have found some level information in the shadow S2. Check |
| 490 | + * that the resulting range is actually including the original IPA. |
| 491 | + */ |
| 492 | + sz = ttl_to_size(ttl); |
| 493 | + if (addr < (tmp + sz)) |
| 494 | + return ttl; |
| 495 | + |
| 496 | + return 0; |
| 497 | +} |
| 498 | + |
423 | 499 | unsigned long compute_tlb_inval_range(struct kvm_s2_mmu *mmu, u64 val)
|
424 | 500 | {
|
| 501 | + struct kvm *kvm = kvm_s2_mmu_to_kvm(mmu); |
425 | 502 | unsigned long max_size;
|
426 | 503 | u8 ttl;
|
427 | 504 |
|
428 |
| - ttl = FIELD_GET(GENMASK_ULL(47, 44), val); |
| 505 | + ttl = FIELD_GET(TLBI_TTL_MASK, val); |
| 506 | + |
| 507 | + if (!ttl || !kvm_has_feat(kvm, ID_AA64MMFR2_EL1, TTL, IMP)) { |
| 508 | + /* No TTL, check the shadow S2 for a hint */ |
| 509 | + u64 addr = (val & GENMASK_ULL(35, 0)) << 12; |
| 510 | + ttl = get_guest_mapping_ttl(mmu, addr); |
| 511 | + } |
429 | 512 |
|
430 | 513 | max_size = ttl_to_size(ttl);
|
431 | 514 |
|
|
0 commit comments