diff options
Diffstat (limited to 'arch/s390/include/asm/pgtable.h')
-rw-r--r-- | arch/s390/include/asm/pgtable.h | 45 |
1 files changed, 32 insertions, 13 deletions
diff --git a/arch/s390/include/asm/pgtable.h b/arch/s390/include/asm/pgtable.h index 0f0de30e3e3f..e8b6e5b8932c 100644 --- a/arch/s390/include/asm/pgtable.h +++ b/arch/s390/include/asm/pgtable.h | |||
@@ -623,7 +623,7 @@ static inline pgste_t pgste_get_lock(pte_t *ptep) | |||
623 | " csg %0,%1,%2\n" | 623 | " csg %0,%1,%2\n" |
624 | " jl 0b\n" | 624 | " jl 0b\n" |
625 | : "=&d" (old), "=&d" (new), "=Q" (ptep[PTRS_PER_PTE]) | 625 | : "=&d" (old), "=&d" (new), "=Q" (ptep[PTRS_PER_PTE]) |
626 | : "Q" (ptep[PTRS_PER_PTE]) : "cc"); | 626 | : "Q" (ptep[PTRS_PER_PTE]) : "cc", "memory"); |
627 | #endif | 627 | #endif |
628 | return __pgste(new); | 628 | return __pgste(new); |
629 | } | 629 | } |
@@ -635,18 +635,26 @@ static inline void pgste_set_unlock(pte_t *ptep, pgste_t pgste) | |||
635 | " nihh %1,0xff7f\n" /* clear RCP_PCL_BIT */ | 635 | " nihh %1,0xff7f\n" /* clear RCP_PCL_BIT */ |
636 | " stg %1,%0\n" | 636 | " stg %1,%0\n" |
637 | : "=Q" (ptep[PTRS_PER_PTE]) | 637 | : "=Q" (ptep[PTRS_PER_PTE]) |
638 | : "d" (pgste_val(pgste)), "Q" (ptep[PTRS_PER_PTE]) : "cc"); | 638 | : "d" (pgste_val(pgste)), "Q" (ptep[PTRS_PER_PTE]) |
639 | : "cc", "memory"); | ||
639 | preempt_enable(); | 640 | preempt_enable(); |
640 | #endif | 641 | #endif |
641 | } | 642 | } |
642 | 643 | ||
644 | static inline void pgste_set(pte_t *ptep, pgste_t pgste) | ||
645 | { | ||
646 | #ifdef CONFIG_PGSTE | ||
647 | *(pgste_t *)(ptep + PTRS_PER_PTE) = pgste; | ||
648 | #endif | ||
649 | } | ||
650 | |||
643 | static inline pgste_t pgste_update_all(pte_t *ptep, pgste_t pgste) | 651 | static inline pgste_t pgste_update_all(pte_t *ptep, pgste_t pgste) |
644 | { | 652 | { |
645 | #ifdef CONFIG_PGSTE | 653 | #ifdef CONFIG_PGSTE |
646 | unsigned long address, bits; | 654 | unsigned long address, bits; |
647 | unsigned char skey; | 655 | unsigned char skey; |
648 | 656 | ||
649 | if (!pte_present(*ptep)) | 657 | if (pte_val(*ptep) & _PAGE_INVALID) |
650 | return pgste; | 658 | return pgste; |
651 | address = pte_val(*ptep) & PAGE_MASK; | 659 | address = pte_val(*ptep) & PAGE_MASK; |
652 | skey = page_get_storage_key(address); | 660 | skey = page_get_storage_key(address); |
@@ -680,7 +688,7 @@ static inline pgste_t pgste_update_young(pte_t *ptep, pgste_t pgste) | |||
680 | #ifdef CONFIG_PGSTE | 688 | #ifdef CONFIG_PGSTE |
681 | int young; | 689 | int young; |
682 | 690 | ||
683 | if (!pte_present(*ptep)) | 691 | if (pte_val(*ptep) & _PAGE_INVALID) |
684 | return pgste; | 692 | return pgste; |
685 | /* Get referenced bit from storage key */ | 693 | /* Get referenced bit from storage key */ |
686 | young = page_reset_referenced(pte_val(*ptep) & PAGE_MASK); | 694 | young = page_reset_referenced(pte_val(*ptep) & PAGE_MASK); |
@@ -704,17 +712,19 @@ static inline void pgste_set_key(pte_t *ptep, pgste_t pgste, pte_t entry) | |||
704 | { | 712 | { |
705 | #ifdef CONFIG_PGSTE | 713 | #ifdef CONFIG_PGSTE |
706 | unsigned long address; | 714 | unsigned long address; |
707 | unsigned long okey, nkey; | 715 | unsigned long nkey; |
708 | 716 | ||
709 | if (!pte_present(entry)) | 717 | if (pte_val(entry) & _PAGE_INVALID) |
710 | return; | 718 | return; |
719 | VM_BUG_ON(!(pte_val(*ptep) & _PAGE_INVALID)); | ||
711 | address = pte_val(entry) & PAGE_MASK; | 720 | address = pte_val(entry) & PAGE_MASK; |
712 | okey = nkey = page_get_storage_key(address); | 721 | /* |
713 | nkey &= ~(_PAGE_ACC_BITS | _PAGE_FP_BIT); | 722 | * Set page access key and fetch protection bit from pgste. |
714 | /* Set page access key and fetch protection bit from pgste */ | 723 | * The guest C/R information is still in the PGSTE, set real |
715 | nkey |= (pgste_val(pgste) & (RCP_ACC_BITS | RCP_FP_BIT)) >> 56; | 724 | * key C/R to 0. |
716 | if (okey != nkey) | 725 | */ |
717 | page_set_storage_key(address, nkey, 0); | 726 | nkey = (pgste_val(pgste) & (RCP_ACC_BITS | RCP_FP_BIT)) >> 56; |
727 | page_set_storage_key(address, nkey, 0); | ||
718 | #endif | 728 | #endif |
719 | } | 729 | } |
720 | 730 | ||
@@ -1098,6 +1108,11 @@ static inline pte_t ptep_modify_prot_start(struct mm_struct *mm, | |||
1098 | pte = *ptep; | 1108 | pte = *ptep; |
1099 | if (!mm_exclusive(mm)) | 1109 | if (!mm_exclusive(mm)) |
1100 | __ptep_ipte(address, ptep); | 1110 | __ptep_ipte(address, ptep); |
1111 | |||
1112 | if (mm_has_pgste(mm)) { | ||
1113 | pgste = pgste_update_all(&pte, pgste); | ||
1114 | pgste_set(ptep, pgste); | ||
1115 | } | ||
1101 | return pte; | 1116 | return pte; |
1102 | } | 1117 | } |
1103 | 1118 | ||
@@ -1105,9 +1120,13 @@ static inline void ptep_modify_prot_commit(struct mm_struct *mm, | |||
1105 | unsigned long address, | 1120 | unsigned long address, |
1106 | pte_t *ptep, pte_t pte) | 1121 | pte_t *ptep, pte_t pte) |
1107 | { | 1122 | { |
1123 | pgste_t pgste; | ||
1124 | |||
1108 | if (mm_has_pgste(mm)) { | 1125 | if (mm_has_pgste(mm)) { |
1126 | pgste = *(pgste_t *)(ptep + PTRS_PER_PTE); | ||
1127 | pgste_set_key(ptep, pgste, pte); | ||
1109 | pgste_set_pte(ptep, pte); | 1128 | pgste_set_pte(ptep, pte); |
1110 | pgste_set_unlock(ptep, *(pgste_t *)(ptep + PTRS_PER_PTE)); | 1129 | pgste_set_unlock(ptep, pgste); |
1111 | } else | 1130 | } else |
1112 | *ptep = pte; | 1131 | *ptep = pte; |
1113 | } | 1132 | } |