153492b1dSGerald Schaefer /* 253492b1dSGerald Schaefer * IBM System z Huge TLB Page Support for Kernel. 353492b1dSGerald Schaefer * 4a53c8fabSHeiko Carstens * Copyright IBM Corp. 2007 553492b1dSGerald Schaefer * Author(s): Gerald Schaefer <gerald.schaefer@de.ibm.com> 653492b1dSGerald Schaefer */ 753492b1dSGerald Schaefer 853492b1dSGerald Schaefer #include <linux/mm.h> 953492b1dSGerald Schaefer #include <linux/hugetlb.h> 1053492b1dSGerald Schaefer 11e5098611SMartin Schwidefsky static inline pmd_t __pte_to_pmd(pte_t pte) 1253492b1dSGerald Schaefer { 13e5098611SMartin Schwidefsky pmd_t pmd; 1453492b1dSGerald Schaefer 15e5098611SMartin Schwidefsky /* 16e5098611SMartin Schwidefsky * Convert encoding pte bits pmd bits 17*a1c843b8SMartin Schwidefsky * lIR.uswrdy.p dy..R...I...wr 18*a1c843b8SMartin Schwidefsky * empty 010.000000.0 -> 00..0...1...00 19*a1c843b8SMartin Schwidefsky * prot-none, clean, old 111.000000.1 -> 00..1...1...00 20*a1c843b8SMartin Schwidefsky * prot-none, clean, young 111.000001.1 -> 01..1...1...00 21*a1c843b8SMartin Schwidefsky * prot-none, dirty, old 111.000010.1 -> 10..1...1...00 22*a1c843b8SMartin Schwidefsky * prot-none, dirty, young 111.000011.1 -> 11..1...1...00 23*a1c843b8SMartin Schwidefsky * read-only, clean, old 111.000100.1 -> 00..1...1...01 24*a1c843b8SMartin Schwidefsky * read-only, clean, young 101.000101.1 -> 01..1...0...01 25*a1c843b8SMartin Schwidefsky * read-only, dirty, old 111.000110.1 -> 10..1...1...01 26*a1c843b8SMartin Schwidefsky * read-only, dirty, young 101.000111.1 -> 11..1...0...01 27*a1c843b8SMartin Schwidefsky * read-write, clean, old 111.001100.1 -> 00..1...1...11 28*a1c843b8SMartin Schwidefsky * read-write, clean, young 101.001101.1 -> 01..1...0...11 29*a1c843b8SMartin Schwidefsky * read-write, dirty, old 110.001110.1 -> 10..0...1...11 30*a1c843b8SMartin Schwidefsky * read-write, dirty, young 100.001111.1 -> 11..0...0...11 31*a1c843b8SMartin Schwidefsky * HW-bits: R read-only, I invalid 32*a1c843b8SMartin Schwidefsky * SW-bits: p present, y young, d dirty, r read, w write, s special, 33*a1c843b8SMartin Schwidefsky * u unused, l large 34e5098611SMartin Schwidefsky */ 35e5098611SMartin Schwidefsky if (pte_present(pte)) { 36e5098611SMartin Schwidefsky pmd_val(pmd) = pte_val(pte) & PAGE_MASK; 37152125b7SMartin Schwidefsky pmd_val(pmd) |= (pte_val(pte) & _PAGE_READ) >> 4; 38152125b7SMartin Schwidefsky pmd_val(pmd) |= (pte_val(pte) & _PAGE_WRITE) >> 4; 39152125b7SMartin Schwidefsky pmd_val(pmd) |= (pte_val(pte) & _PAGE_INVALID) >> 5; 40152125b7SMartin Schwidefsky pmd_val(pmd) |= (pte_val(pte) & _PAGE_PROTECT); 41152125b7SMartin Schwidefsky pmd_val(pmd) |= (pte_val(pte) & _PAGE_DIRTY) << 10; 42152125b7SMartin Schwidefsky pmd_val(pmd) |= (pte_val(pte) & _PAGE_YOUNG) << 10; 43e5098611SMartin Schwidefsky } else 44e5098611SMartin Schwidefsky pmd_val(pmd) = _SEGMENT_ENTRY_INVALID; 45e5098611SMartin Schwidefsky return pmd; 4653492b1dSGerald Schaefer } 4753492b1dSGerald Schaefer 48e5098611SMartin Schwidefsky static inline pte_t __pmd_to_pte(pmd_t pmd) 49e5098611SMartin Schwidefsky { 50e5098611SMartin Schwidefsky pte_t pte; 51e5098611SMartin Schwidefsky 52e5098611SMartin Schwidefsky /* 53e5098611SMartin Schwidefsky * Convert encoding pmd bits pte bits 54*a1c843b8SMartin Schwidefsky * dy..R...I...wr lIR.uswrdy.p 55*a1c843b8SMartin Schwidefsky * empty 00..0...1...00 -> 010.000000.0 56*a1c843b8SMartin Schwidefsky * prot-none, clean, old 00..1...1...00 -> 111.000000.1 57*a1c843b8SMartin Schwidefsky * prot-none, clean, young 01..1...1...00 -> 111.000001.1 58*a1c843b8SMartin Schwidefsky * prot-none, dirty, old 10..1...1...00 -> 111.000010.1 59*a1c843b8SMartin Schwidefsky * prot-none, dirty, young 11..1...1...00 -> 111.000011.1 60*a1c843b8SMartin Schwidefsky * read-only, clean, old 00..1...1...01 -> 111.000100.1 61*a1c843b8SMartin Schwidefsky * read-only, clean, young 01..1...0...01 -> 101.000101.1 62*a1c843b8SMartin Schwidefsky * read-only, dirty, old 10..1...1...01 -> 111.000110.1 63*a1c843b8SMartin Schwidefsky * read-only, dirty, young 11..1...0...01 -> 101.000111.1 64*a1c843b8SMartin Schwidefsky * read-write, clean, old 00..1...1...11 -> 111.001100.1 65*a1c843b8SMartin Schwidefsky * read-write, clean, young 01..1...0...11 -> 101.001101.1 66*a1c843b8SMartin Schwidefsky * read-write, dirty, old 10..0...1...11 -> 110.001110.1 67*a1c843b8SMartin Schwidefsky * read-write, dirty, young 11..0...0...11 -> 100.001111.1 68*a1c843b8SMartin Schwidefsky * HW-bits: R read-only, I invalid 69*a1c843b8SMartin Schwidefsky * SW-bits: p present, y young, d dirty, r read, w write, s special, 70*a1c843b8SMartin Schwidefsky * u unused, l large 71e5098611SMartin Schwidefsky */ 72e5098611SMartin Schwidefsky if (pmd_present(pmd)) { 73152125b7SMartin Schwidefsky pte_val(pte) = pmd_val(pmd) & _SEGMENT_ENTRY_ORIGIN_LARGE; 74152125b7SMartin Schwidefsky pte_val(pte) |= _PAGE_LARGE | _PAGE_PRESENT; 75152125b7SMartin Schwidefsky pte_val(pte) |= (pmd_val(pmd) & _SEGMENT_ENTRY_READ) << 4; 76152125b7SMartin Schwidefsky pte_val(pte) |= (pmd_val(pmd) & _SEGMENT_ENTRY_WRITE) << 4; 77152125b7SMartin Schwidefsky pte_val(pte) |= (pmd_val(pmd) & _SEGMENT_ENTRY_INVALID) << 5; 78152125b7SMartin Schwidefsky pte_val(pte) |= (pmd_val(pmd) & _SEGMENT_ENTRY_PROTECT); 79b7d14f3aSMartin Schwidefsky pte_val(pte) |= (pmd_val(pmd) & _SEGMENT_ENTRY_DIRTY) >> 10; 80b7d14f3aSMartin Schwidefsky pte_val(pte) |= (pmd_val(pmd) & _SEGMENT_ENTRY_YOUNG) >> 10; 81e5098611SMartin Schwidefsky } else 82e5098611SMartin Schwidefsky pte_val(pte) = _PAGE_INVALID; 83e5098611SMartin Schwidefsky return pte; 84e5098611SMartin Schwidefsky } 85e5098611SMartin Schwidefsky 86e5098611SMartin Schwidefsky void set_huge_pte_at(struct mm_struct *mm, unsigned long addr, 87e5098611SMartin Schwidefsky pte_t *ptep, pte_t pte) 88e5098611SMartin Schwidefsky { 89e5098611SMartin Schwidefsky pmd_t pmd; 90e5098611SMartin Schwidefsky 91e5098611SMartin Schwidefsky pmd = __pte_to_pmd(pte); 92e5098611SMartin Schwidefsky if (!MACHINE_HAS_HPAGE) { 93152125b7SMartin Schwidefsky /* Emulated huge ptes loose the dirty and young bit */ 94e5098611SMartin Schwidefsky pmd_val(pmd) &= ~_SEGMENT_ENTRY_ORIGIN; 95e5098611SMartin Schwidefsky pmd_val(pmd) |= pte_page(pte)[1].index; 96e5098611SMartin Schwidefsky } else 976a5c1482SHeiko Carstens pmd_val(pmd) |= _SEGMENT_ENTRY_LARGE; 98e5098611SMartin Schwidefsky *(pmd_t *) ptep = pmd; 99e5098611SMartin Schwidefsky } 100e5098611SMartin Schwidefsky 101e5098611SMartin Schwidefsky pte_t huge_ptep_get(pte_t *ptep) 102e5098611SMartin Schwidefsky { 103e5098611SMartin Schwidefsky unsigned long origin; 104e5098611SMartin Schwidefsky pmd_t pmd; 105e5098611SMartin Schwidefsky 106e5098611SMartin Schwidefsky pmd = *(pmd_t *) ptep; 107e5098611SMartin Schwidefsky if (!MACHINE_HAS_HPAGE && pmd_present(pmd)) { 108e5098611SMartin Schwidefsky origin = pmd_val(pmd) & _SEGMENT_ENTRY_ORIGIN; 109e5098611SMartin Schwidefsky pmd_val(pmd) &= ~_SEGMENT_ENTRY_ORIGIN; 110e5098611SMartin Schwidefsky pmd_val(pmd) |= *(unsigned long *) origin; 111152125b7SMartin Schwidefsky /* Emulated huge ptes are young and dirty by definition */ 112152125b7SMartin Schwidefsky pmd_val(pmd) |= _SEGMENT_ENTRY_YOUNG | _SEGMENT_ENTRY_DIRTY; 113e5098611SMartin Schwidefsky } 114e5098611SMartin Schwidefsky return __pmd_to_pte(pmd); 115e5098611SMartin Schwidefsky } 116e5098611SMartin Schwidefsky 117e5098611SMartin Schwidefsky pte_t huge_ptep_get_and_clear(struct mm_struct *mm, 118e5098611SMartin Schwidefsky unsigned long addr, pte_t *ptep) 119e5098611SMartin Schwidefsky { 120e5098611SMartin Schwidefsky pmd_t *pmdp = (pmd_t *) ptep; 121e5098611SMartin Schwidefsky pte_t pte = huge_ptep_get(ptep); 122e5098611SMartin Schwidefsky 1231b948d6cSMartin Schwidefsky pmdp_flush_direct(mm, addr, pmdp); 124e5098611SMartin Schwidefsky pmd_val(*pmdp) = _SEGMENT_ENTRY_EMPTY; 125e5098611SMartin Schwidefsky return pte; 12653492b1dSGerald Schaefer } 12753492b1dSGerald Schaefer 12853492b1dSGerald Schaefer int arch_prepare_hugepage(struct page *page) 12953492b1dSGerald Schaefer { 13053492b1dSGerald Schaefer unsigned long addr = page_to_phys(page); 13153492b1dSGerald Schaefer pte_t pte; 13253492b1dSGerald Schaefer pte_t *ptep; 13353492b1dSGerald Schaefer int i; 13453492b1dSGerald Schaefer 13553492b1dSGerald Schaefer if (MACHINE_HAS_HPAGE) 13653492b1dSGerald Schaefer return 0; 13753492b1dSGerald Schaefer 138e5992f2eSMartin Schwidefsky ptep = (pte_t *) pte_alloc_one(&init_mm, addr); 13953492b1dSGerald Schaefer if (!ptep) 14053492b1dSGerald Schaefer return -ENOMEM; 14153492b1dSGerald Schaefer 142106c992aSGerald Schaefer pte_val(pte) = addr; 14353492b1dSGerald Schaefer for (i = 0; i < PTRS_PER_PTE; i++) { 14453492b1dSGerald Schaefer set_pte_at(&init_mm, addr + i * PAGE_SIZE, ptep + i, pte); 14553492b1dSGerald Schaefer pte_val(pte) += PAGE_SIZE; 14653492b1dSGerald Schaefer } 14753492b1dSGerald Schaefer page[1].index = (unsigned long) ptep; 14853492b1dSGerald Schaefer return 0; 14953492b1dSGerald Schaefer } 15053492b1dSGerald Schaefer 15153492b1dSGerald Schaefer void arch_release_hugepage(struct page *page) 15253492b1dSGerald Schaefer { 15353492b1dSGerald Schaefer pte_t *ptep; 15453492b1dSGerald Schaefer 15553492b1dSGerald Schaefer if (MACHINE_HAS_HPAGE) 15653492b1dSGerald Schaefer return; 15753492b1dSGerald Schaefer 15853492b1dSGerald Schaefer ptep = (pte_t *) page[1].index; 15953492b1dSGerald Schaefer if (!ptep) 16053492b1dSGerald Schaefer return; 161e5098611SMartin Schwidefsky clear_table((unsigned long *) ptep, _PAGE_INVALID, 162a686425bSGerald Schaefer PTRS_PER_PTE * sizeof(pte_t)); 16380217147SMartin Schwidefsky page_table_free(&init_mm, (unsigned long *) ptep); 16453492b1dSGerald Schaefer page[1].index = 0; 16553492b1dSGerald Schaefer } 16653492b1dSGerald Schaefer 167a5516438SAndi Kleen pte_t *huge_pte_alloc(struct mm_struct *mm, 168a5516438SAndi Kleen unsigned long addr, unsigned long sz) 16953492b1dSGerald Schaefer { 17053492b1dSGerald Schaefer pgd_t *pgdp; 17153492b1dSGerald Schaefer pud_t *pudp; 17253492b1dSGerald Schaefer pmd_t *pmdp = NULL; 17353492b1dSGerald Schaefer 17453492b1dSGerald Schaefer pgdp = pgd_offset(mm, addr); 17553492b1dSGerald Schaefer pudp = pud_alloc(mm, pgdp, addr); 17653492b1dSGerald Schaefer if (pudp) 17753492b1dSGerald Schaefer pmdp = pmd_alloc(mm, pudp, addr); 17853492b1dSGerald Schaefer return (pte_t *) pmdp; 17953492b1dSGerald Schaefer } 18053492b1dSGerald Schaefer 18153492b1dSGerald Schaefer pte_t *huge_pte_offset(struct mm_struct *mm, unsigned long addr) 18253492b1dSGerald Schaefer { 18353492b1dSGerald Schaefer pgd_t *pgdp; 18453492b1dSGerald Schaefer pud_t *pudp; 18553492b1dSGerald Schaefer pmd_t *pmdp = NULL; 18653492b1dSGerald Schaefer 18753492b1dSGerald Schaefer pgdp = pgd_offset(mm, addr); 18853492b1dSGerald Schaefer if (pgd_present(*pgdp)) { 18953492b1dSGerald Schaefer pudp = pud_offset(pgdp, addr); 19053492b1dSGerald Schaefer if (pud_present(*pudp)) 19153492b1dSGerald Schaefer pmdp = pmd_offset(pudp, addr); 19253492b1dSGerald Schaefer } 19353492b1dSGerald Schaefer return (pte_t *) pmdp; 19453492b1dSGerald Schaefer } 19553492b1dSGerald Schaefer 19653492b1dSGerald Schaefer int huge_pmd_unshare(struct mm_struct *mm, unsigned long *addr, pte_t *ptep) 19753492b1dSGerald Schaefer { 19853492b1dSGerald Schaefer return 0; 19953492b1dSGerald Schaefer } 20053492b1dSGerald Schaefer 20153492b1dSGerald Schaefer int pmd_huge(pmd_t pmd) 20253492b1dSGerald Schaefer { 20353492b1dSGerald Schaefer if (!MACHINE_HAS_HPAGE) 20453492b1dSGerald Schaefer return 0; 20553492b1dSGerald Schaefer 20653492b1dSGerald Schaefer return !!(pmd_val(pmd) & _SEGMENT_ENTRY_LARGE); 20753492b1dSGerald Schaefer } 20853492b1dSGerald Schaefer 209ceb86879SAndi Kleen int pud_huge(pud_t pud) 210ceb86879SAndi Kleen { 211ceb86879SAndi Kleen return 0; 212ceb86879SAndi Kleen } 213