xref: /openbmc/linux/arch/s390/mm/hugetlbpage.c (revision a1c843b82541fdd4c4644607c942dabc7c7e6f6c)
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