diff options
| -rw-r--r-- | arch/x86/mm/pageattr.c | 50 |
1 files changed, 47 insertions, 3 deletions
diff --git a/arch/x86/mm/pageattr.c b/arch/x86/mm/pageattr.c index a718e0d23503..2713be4bca41 100644 --- a/arch/x86/mm/pageattr.c +++ b/arch/x86/mm/pageattr.c | |||
| @@ -445,6 +445,19 @@ try_preserve_large_page(pte_t *kpte, unsigned long address, | |||
| 445 | pgprot_val(req_prot) |= pgprot_val(cpa->mask_set); | 445 | pgprot_val(req_prot) |= pgprot_val(cpa->mask_set); |
| 446 | 446 | ||
| 447 | /* | 447 | /* |
| 448 | * Set the PSE and GLOBAL flags only if the PRESENT flag is | ||
| 449 | * set otherwise pmd_present/pmd_huge will return true even on | ||
| 450 | * a non present pmd. The canon_pgprot will clear _PAGE_GLOBAL | ||
| 451 | * for the ancient hardware that doesn't support it. | ||
| 452 | */ | ||
| 453 | if (pgprot_val(new_prot) & _PAGE_PRESENT) | ||
| 454 | pgprot_val(new_prot) |= _PAGE_PSE | _PAGE_GLOBAL; | ||
| 455 | else | ||
| 456 | pgprot_val(new_prot) &= ~(_PAGE_PSE | _PAGE_GLOBAL); | ||
| 457 | |||
| 458 | new_prot = canon_pgprot(new_prot); | ||
| 459 | |||
| 460 | /* | ||
| 448 | * old_pte points to the large page base address. So we need | 461 | * old_pte points to the large page base address. So we need |
| 449 | * to add the offset of the virtual address: | 462 | * to add the offset of the virtual address: |
| 450 | */ | 463 | */ |
| @@ -489,7 +502,7 @@ try_preserve_large_page(pte_t *kpte, unsigned long address, | |||
| 489 | * The address is aligned and the number of pages | 502 | * The address is aligned and the number of pages |
| 490 | * covers the full page. | 503 | * covers the full page. |
| 491 | */ | 504 | */ |
| 492 | new_pte = pfn_pte(pte_pfn(old_pte), canon_pgprot(new_prot)); | 505 | new_pte = pfn_pte(pte_pfn(old_pte), new_prot); |
| 493 | __set_pmd_pte(kpte, address, new_pte); | 506 | __set_pmd_pte(kpte, address, new_pte); |
| 494 | cpa->flags |= CPA_FLUSHTLB; | 507 | cpa->flags |= CPA_FLUSHTLB; |
| 495 | do_split = 0; | 508 | do_split = 0; |
| @@ -540,16 +553,35 @@ static int split_large_page(pte_t *kpte, unsigned long address) | |||
| 540 | #ifdef CONFIG_X86_64 | 553 | #ifdef CONFIG_X86_64 |
| 541 | if (level == PG_LEVEL_1G) { | 554 | if (level == PG_LEVEL_1G) { |
| 542 | pfninc = PMD_PAGE_SIZE >> PAGE_SHIFT; | 555 | pfninc = PMD_PAGE_SIZE >> PAGE_SHIFT; |
| 543 | pgprot_val(ref_prot) |= _PAGE_PSE; | 556 | /* |
| 557 | * Set the PSE flags only if the PRESENT flag is set | ||
| 558 | * otherwise pmd_present/pmd_huge will return true | ||
| 559 | * even on a non present pmd. | ||
| 560 | */ | ||
| 561 | if (pgprot_val(ref_prot) & _PAGE_PRESENT) | ||
| 562 | pgprot_val(ref_prot) |= _PAGE_PSE; | ||
| 563 | else | ||
| 564 | pgprot_val(ref_prot) &= ~_PAGE_PSE; | ||
| 544 | } | 565 | } |
| 545 | #endif | 566 | #endif |
| 546 | 567 | ||
| 547 | /* | 568 | /* |
| 569 | * Set the GLOBAL flags only if the PRESENT flag is set | ||
| 570 | * otherwise pmd/pte_present will return true even on a non | ||
| 571 | * present pmd/pte. The canon_pgprot will clear _PAGE_GLOBAL | ||
| 572 | * for the ancient hardware that doesn't support it. | ||
| 573 | */ | ||
| 574 | if (pgprot_val(ref_prot) & _PAGE_PRESENT) | ||
| 575 | pgprot_val(ref_prot) |= _PAGE_GLOBAL; | ||
| 576 | else | ||
| 577 | pgprot_val(ref_prot) &= ~_PAGE_GLOBAL; | ||
| 578 | |||
| 579 | /* | ||
| 548 | * Get the target pfn from the original entry: | 580 | * Get the target pfn from the original entry: |
| 549 | */ | 581 | */ |
| 550 | pfn = pte_pfn(*kpte); | 582 | pfn = pte_pfn(*kpte); |
| 551 | for (i = 0; i < PTRS_PER_PTE; i++, pfn += pfninc) | 583 | for (i = 0; i < PTRS_PER_PTE; i++, pfn += pfninc) |
| 552 | set_pte(&pbase[i], pfn_pte(pfn, ref_prot)); | 584 | set_pte(&pbase[i], pfn_pte(pfn, canon_pgprot(ref_prot))); |
| 553 | 585 | ||
| 554 | if (address >= (unsigned long)__va(0) && | 586 | if (address >= (unsigned long)__va(0) && |
| 555 | address < (unsigned long)__va(max_low_pfn_mapped << PAGE_SHIFT)) | 587 | address < (unsigned long)__va(max_low_pfn_mapped << PAGE_SHIFT)) |
| @@ -660,6 +692,18 @@ repeat: | |||
| 660 | new_prot = static_protections(new_prot, address, pfn); | 692 | new_prot = static_protections(new_prot, address, pfn); |
| 661 | 693 | ||
| 662 | /* | 694 | /* |
| 695 | * Set the GLOBAL flags only if the PRESENT flag is | ||
| 696 | * set otherwise pte_present will return true even on | ||
| 697 | * a non present pte. The canon_pgprot will clear | ||
| 698 | * _PAGE_GLOBAL for the ancient hardware that doesn't | ||
| 699 | * support it. | ||
| 700 | */ | ||
| 701 | if (pgprot_val(new_prot) & _PAGE_PRESENT) | ||
| 702 | pgprot_val(new_prot) |= _PAGE_GLOBAL; | ||
| 703 | else | ||
| 704 | pgprot_val(new_prot) &= ~_PAGE_GLOBAL; | ||
| 705 | |||
| 706 | /* | ||
| 663 | * We need to keep the pfn from the existing PTE, | 707 | * We need to keep the pfn from the existing PTE, |
| 664 | * after all we're only going to change it's attributes | 708 | * after all we're only going to change it's attributes |
| 665 | * not the memory it points to | 709 | * not the memory it points to |
