16c9f99dfSJanosch Frank /* SPDX-License-Identifier: GPL-2.0-only */
2c08c320bSDavid Hildenbrand /*
3c08c320bSDavid Hildenbrand * s390x page table definitions and functions
4c08c320bSDavid Hildenbrand *
5c08c320bSDavid Hildenbrand * Copyright (c) 2017 Red Hat Inc
6c08c320bSDavid Hildenbrand *
7c08c320bSDavid Hildenbrand * Authors:
8c08c320bSDavid Hildenbrand * David Hildenbrand <david@redhat.com>
9c08c320bSDavid Hildenbrand */
10c08c320bSDavid Hildenbrand #ifndef _ASMS390X_PGTABLE_H_
11c08c320bSDavid Hildenbrand #define _ASMS390X_PGTABLE_H_
12c08c320bSDavid Hildenbrand
13c08c320bSDavid Hildenbrand #include <asm/page.h>
14c08c320bSDavid Hildenbrand #include <alloc_page.h>
15c08c320bSDavid Hildenbrand
16c08c320bSDavid Hildenbrand #define ASCE_ORIGIN 0xfffffffffffff000UL
17c08c320bSDavid Hildenbrand #define ASCE_G 0x0000000000000200UL
18c08c320bSDavid Hildenbrand #define ASCE_P 0x0000000000000100UL
19c08c320bSDavid Hildenbrand #define ASCE_S 0x0000000000000080UL
20c08c320bSDavid Hildenbrand #define ASCE_X 0x0000000000000040UL
21c08c320bSDavid Hildenbrand #define ASCE_R 0x0000000000000020UL
22c08c320bSDavid Hildenbrand #define ASCE_DT 0x000000000000000cUL
23c08c320bSDavid Hildenbrand #define ASCE_TL 0x0000000000000003UL
24c08c320bSDavid Hildenbrand
25c08c320bSDavid Hildenbrand #define ASCE_DT_REGION1 0x000000000000000cUL
26c08c320bSDavid Hildenbrand #define ASCE_DT_REGION2 0x0000000000000008UL
27c08c320bSDavid Hildenbrand #define ASCE_DT_REGION3 0x0000000000000004UL
28c08c320bSDavid Hildenbrand #define ASCE_DT_SEGMENT 0x0000000000000000UL
29c08c320bSDavid Hildenbrand
30c08c320bSDavid Hildenbrand #define REGION_TABLE_ORDER 2
31c08c320bSDavid Hildenbrand #define REGION_TABLE_ENTRIES 2048
32c08c320bSDavid Hildenbrand #define REGION_TABLE_LENGTH 3
33c08c320bSDavid Hildenbrand
34c08c320bSDavid Hildenbrand #define REGION1_SHIFT 53
35c08c320bSDavid Hildenbrand #define REGION2_SHIFT 42
36c08c320bSDavid Hildenbrand #define REGION3_SHIFT 31
37c08c320bSDavid Hildenbrand
38c08c320bSDavid Hildenbrand #define REGION_ENTRY_ORIGIN 0xfffffffffffff000UL
39c08c320bSDavid Hildenbrand #define REGION_ENTRY_P 0x0000000000000200UL
40c08c320bSDavid Hildenbrand #define REGION_ENTRY_TF 0x00000000000000c0UL
41c08c320bSDavid Hildenbrand #define REGION_ENTRY_I 0x0000000000000020UL
42c08c320bSDavid Hildenbrand #define REGION_ENTRY_TT 0x000000000000000cUL
43c08c320bSDavid Hildenbrand #define REGION_ENTRY_TL 0x0000000000000003UL
44c08c320bSDavid Hildenbrand
45c08c320bSDavid Hildenbrand #define REGION_ENTRY_TT_REGION1 0x000000000000000cUL
46c08c320bSDavid Hildenbrand #define REGION_ENTRY_TT_REGION2 0x0000000000000008UL
47c08c320bSDavid Hildenbrand #define REGION_ENTRY_TT_REGION3 0x0000000000000004UL
48c08c320bSDavid Hildenbrand
49c08c320bSDavid Hildenbrand #define REGION3_ENTRY_RFAA 0xffffffff80000000UL
50c08c320bSDavid Hildenbrand #define REGION3_ENTRY_AV 0x0000000000010000UL
51c08c320bSDavid Hildenbrand #define REGION3_ENTRY_ACC 0x000000000000f000UL
52c08c320bSDavid Hildenbrand #define REGION3_ENTRY_F 0x0000000000000800UL
53c08c320bSDavid Hildenbrand #define REGION3_ENTRY_FC 0x0000000000000400UL
54c08c320bSDavid Hildenbrand #define REGION3_ENTRY_IEP 0x0000000000000100UL
55c08c320bSDavid Hildenbrand #define REGION3_ENTRY_CR 0x0000000000000010UL
56c08c320bSDavid Hildenbrand
57c08c320bSDavid Hildenbrand #define SEGMENT_TABLE_ORDER 2
58c08c320bSDavid Hildenbrand #define SEGMENT_TABLE_ENTRIES 2048
59c08c320bSDavid Hildenbrand #define SEGMENT_TABLE_LENGTH 3
60c08c320bSDavid Hildenbrand #define SEGMENT_SHIFT 20
61c08c320bSDavid Hildenbrand
62c08c320bSDavid Hildenbrand #define SEGMENT_ENTRY_ORIGIN 0xfffffffffffff800UL
63149c39d6SClaudio Imbrenda #define SEGMENT_ENTRY_SFAA 0xfffffffffff00000UL
64c08c320bSDavid Hildenbrand #define SEGMENT_ENTRY_AV 0x0000000000010000UL
65c08c320bSDavid Hildenbrand #define SEGMENT_ENTRY_ACC 0x000000000000f000UL
66c08c320bSDavid Hildenbrand #define SEGMENT_ENTRY_F 0x0000000000000800UL
67c08c320bSDavid Hildenbrand #define SEGMENT_ENTRY_FC 0x0000000000000400UL
68c08c320bSDavid Hildenbrand #define SEGMENT_ENTRY_P 0x0000000000000200UL
69c08c320bSDavid Hildenbrand #define SEGMENT_ENTRY_IEP 0x0000000000000100UL
70c08c320bSDavid Hildenbrand #define SEGMENT_ENTRY_I 0x0000000000000020UL
71c08c320bSDavid Hildenbrand #define SEGMENT_ENTRY_CS 0x0000000000000010UL
72c08c320bSDavid Hildenbrand #define SEGMENT_ENTRY_TT 0x000000000000000cUL
73c08c320bSDavid Hildenbrand
74c08c320bSDavid Hildenbrand #define SEGMENT_ENTRY_TT_REGION1 0x000000000000000cUL
75c08c320bSDavid Hildenbrand #define SEGMENT_ENTRY_TT_REGION2 0x0000000000000008UL
76c08c320bSDavid Hildenbrand #define SEGMENT_ENTRY_TT_REGION3 0x0000000000000004UL
77c08c320bSDavid Hildenbrand #define SEGMENT_ENTRY_TT_SEGMENT 0x0000000000000000UL
78c08c320bSDavid Hildenbrand
79c08c320bSDavid Hildenbrand #define PAGE_TABLE_ORDER 0
80c08c320bSDavid Hildenbrand #define PAGE_TABLE_ENTRIES 256
81c08c320bSDavid Hildenbrand
82c08c320bSDavid Hildenbrand #define PAGE_ENTRY_I 0x0000000000000400UL
83c08c320bSDavid Hildenbrand #define PAGE_ENTRY_P 0x0000000000000200UL
84c08c320bSDavid Hildenbrand #define PAGE_ENTRY_IEP 0x0000000000000100UL
85c08c320bSDavid Hildenbrand
86c08c320bSDavid Hildenbrand #define PTRS_PER_PGD REGION_TABLE_ENTRIES
87c08c320bSDavid Hildenbrand #define PTRS_PER_P4D REGION_TABLE_ENTRIES
88c08c320bSDavid Hildenbrand #define PTRS_PER_PUD REGION_TABLE_ENTRIES
89c08c320bSDavid Hildenbrand #define PTRS_PER_PMD SEGMENT_TABLE_ENTRIES
90c08c320bSDavid Hildenbrand #define PTRS_PER_PTE PAGE_TABLE_ENTRIES
91c08c320bSDavid Hildenbrand
92c08c320bSDavid Hildenbrand #define PGDIR_SHIFT REGION1_SHIFT
93c08c320bSDavid Hildenbrand #define P4D_SHIFT REGION2_SHIFT
94c08c320bSDavid Hildenbrand #define PUD_SHIFT REGION3_SHIFT
95c08c320bSDavid Hildenbrand #define PMD_SHIFT SEGMENT_SHIFT
96c08c320bSDavid Hildenbrand
97c08c320bSDavid Hildenbrand #define pgd_none(entry) (pgd_val(entry) & REGION_ENTRY_I)
98c08c320bSDavid Hildenbrand #define p4d_none(entry) (p4d_val(entry) & REGION_ENTRY_I)
99c08c320bSDavid Hildenbrand #define pud_none(entry) (pud_val(entry) & REGION_ENTRY_I)
100c08c320bSDavid Hildenbrand #define pmd_none(entry) (pmd_val(entry) & SEGMENT_ENTRY_I)
101c08c320bSDavid Hildenbrand #define pte_none(entry) (pte_val(entry) & PAGE_ENTRY_I)
102c08c320bSDavid Hildenbrand
103*81c4c141SClaudio Imbrenda #define pud_huge(entry) (pud_val(entry) & REGION3_ENTRY_FC)
104*81c4c141SClaudio Imbrenda #define pmd_large(entry) (pmd_val(entry) & SEGMENT_ENTRY_FC)
105*81c4c141SClaudio Imbrenda
106c08c320bSDavid Hildenbrand #define pgd_addr(entry) __va(pgd_val(entry) & REGION_ENTRY_ORIGIN)
107c08c320bSDavid Hildenbrand #define p4d_addr(entry) __va(p4d_val(entry) & REGION_ENTRY_ORIGIN)
108c08c320bSDavid Hildenbrand #define pud_addr(entry) __va(pud_val(entry) & REGION_ENTRY_ORIGIN)
109c08c320bSDavid Hildenbrand #define pmd_addr(entry) __va(pmd_val(entry) & SEGMENT_ENTRY_ORIGIN)
110c08c320bSDavid Hildenbrand #define pte_addr(entry) __va(pte_val(entry) & PAGE_MASK)
111c08c320bSDavid Hildenbrand
112c08c320bSDavid Hildenbrand #define pgd_index(addr) (((addr) >> PGDIR_SHIFT) & (PTRS_PER_PGD - 1))
113c08c320bSDavid Hildenbrand #define p4d_index(addr) (((addr) >> P4D_SHIFT) & (PTRS_PER_P4D - 1))
114c08c320bSDavid Hildenbrand #define pud_index(addr) (((addr) >> PUD_SHIFT) & (PTRS_PER_PUD - 1))
115c08c320bSDavid Hildenbrand #define pmd_index(addr) (((addr) >> PMD_SHIFT) & (PTRS_PER_PMD - 1))
116c08c320bSDavid Hildenbrand #define pte_index(addr) (((addr) >> PAGE_SHIFT) & (PTRS_PER_PTE - 1))
117c08c320bSDavid Hildenbrand
118c08c320bSDavid Hildenbrand #define pgd_offset(table, addr) ((pgd_t *)(table) + pgd_index(addr))
119c08c320bSDavid Hildenbrand #define p4d_offset(pgd, addr) ((p4d_t *)pgd_addr(*(pgd)) + p4d_index(addr))
120c08c320bSDavid Hildenbrand #define pud_offset(p4d, addr) ((pud_t *)p4d_addr(*(p4d)) + pud_index(addr))
121c08c320bSDavid Hildenbrand #define pmd_offset(pud, addr) ((pmd_t *)pud_addr(*(pud)) + pmd_index(addr))
122c08c320bSDavid Hildenbrand #define pte_offset(pmd, addr) ((pte_t *)pmd_addr(*(pmd)) + pte_index(addr))
123c08c320bSDavid Hildenbrand
pgd_alloc_one(void)124c08c320bSDavid Hildenbrand static inline pgd_t *pgd_alloc_one(void)
125c08c320bSDavid Hildenbrand {
126c08c320bSDavid Hildenbrand pgd_t *pgd = alloc_pages(REGION_TABLE_ORDER);
127c08c320bSDavid Hildenbrand int i;
128c08c320bSDavid Hildenbrand
129c08c320bSDavid Hildenbrand for (i = 0; i < REGION_TABLE_ENTRIES; i++)
130c08c320bSDavid Hildenbrand pgd_val(pgd[i]) = REGION_ENTRY_TT_REGION1 | REGION_ENTRY_I;
131c08c320bSDavid Hildenbrand return pgd;
132c08c320bSDavid Hildenbrand }
133c08c320bSDavid Hildenbrand
p4d_alloc_one(void)134c08c320bSDavid Hildenbrand static inline p4d_t *p4d_alloc_one(void)
135c08c320bSDavid Hildenbrand {
136c08c320bSDavid Hildenbrand p4d_t *p4d = alloc_pages(REGION_TABLE_ORDER);
137c08c320bSDavid Hildenbrand int i;
138c08c320bSDavid Hildenbrand
139c08c320bSDavid Hildenbrand for (i = 0; i < REGION_TABLE_ENTRIES; i++)
140c08c320bSDavid Hildenbrand p4d_val(p4d[i]) = REGION_ENTRY_TT_REGION2 | REGION_ENTRY_I;
141c08c320bSDavid Hildenbrand return p4d;
142c08c320bSDavid Hildenbrand }
143c08c320bSDavid Hildenbrand
p4d_alloc(pgd_t * pgd,unsigned long addr)144c08c320bSDavid Hildenbrand static inline p4d_t *p4d_alloc(pgd_t *pgd, unsigned long addr)
145c08c320bSDavid Hildenbrand {
146c08c320bSDavid Hildenbrand if (pgd_none(*pgd)) {
147c08c320bSDavid Hildenbrand p4d_t *p4d = p4d_alloc_one();
148c08c320bSDavid Hildenbrand pgd_val(*pgd) = __pa(p4d) | REGION_ENTRY_TT_REGION1 |
149149c39d6SClaudio Imbrenda REGION_ENTRY_TL;
150c08c320bSDavid Hildenbrand }
151c08c320bSDavid Hildenbrand return p4d_offset(pgd, addr);
152c08c320bSDavid Hildenbrand }
153c08c320bSDavid Hildenbrand
pud_alloc_one(void)154c08c320bSDavid Hildenbrand static inline pud_t *pud_alloc_one(void)
155c08c320bSDavid Hildenbrand {
156c08c320bSDavid Hildenbrand pud_t *pud = alloc_pages(REGION_TABLE_ORDER);
157c08c320bSDavid Hildenbrand int i;
158c08c320bSDavid Hildenbrand
159c08c320bSDavid Hildenbrand for (i = 0; i < REGION_TABLE_ENTRIES; i++)
160c08c320bSDavid Hildenbrand pud_val(pud[i]) = REGION_ENTRY_TT_REGION3 | REGION_ENTRY_I;
161c08c320bSDavid Hildenbrand return pud;
162c08c320bSDavid Hildenbrand }
163c08c320bSDavid Hildenbrand
pud_alloc(p4d_t * p4d,unsigned long addr)164c08c320bSDavid Hildenbrand static inline pud_t *pud_alloc(p4d_t *p4d, unsigned long addr)
165c08c320bSDavid Hildenbrand {
166c08c320bSDavid Hildenbrand if (p4d_none(*p4d)) {
167c08c320bSDavid Hildenbrand pud_t *pud = pud_alloc_one();
168c08c320bSDavid Hildenbrand p4d_val(*p4d) = __pa(pud) | REGION_ENTRY_TT_REGION2 |
169149c39d6SClaudio Imbrenda REGION_ENTRY_TL;
170c08c320bSDavid Hildenbrand }
171c08c320bSDavid Hildenbrand return pud_offset(p4d, addr);
172c08c320bSDavid Hildenbrand }
173c08c320bSDavid Hildenbrand
pmd_alloc_one(void)174c08c320bSDavid Hildenbrand static inline pmd_t *pmd_alloc_one(void)
175c08c320bSDavid Hildenbrand {
176c08c320bSDavid Hildenbrand pmd_t *pmd = alloc_pages(SEGMENT_TABLE_ORDER);
177c08c320bSDavid Hildenbrand int i;
178c08c320bSDavid Hildenbrand
179c08c320bSDavid Hildenbrand for (i = 0; i < SEGMENT_TABLE_ENTRIES; i++)
180c08c320bSDavid Hildenbrand pmd_val(pmd[i]) = SEGMENT_ENTRY_TT_SEGMENT | SEGMENT_ENTRY_I;
181c08c320bSDavid Hildenbrand return pmd;
182c08c320bSDavid Hildenbrand }
183c08c320bSDavid Hildenbrand
pmd_alloc(pud_t * pud,unsigned long addr)184c08c320bSDavid Hildenbrand static inline pmd_t *pmd_alloc(pud_t *pud, unsigned long addr)
185c08c320bSDavid Hildenbrand {
186c08c320bSDavid Hildenbrand if (pud_none(*pud)) {
187c08c320bSDavid Hildenbrand pmd_t *pmd = pmd_alloc_one();
188c08c320bSDavid Hildenbrand pud_val(*pud) = __pa(pmd) | REGION_ENTRY_TT_REGION3 |
189149c39d6SClaudio Imbrenda REGION_ENTRY_TL;
190c08c320bSDavid Hildenbrand }
191c08c320bSDavid Hildenbrand return pmd_offset(pud, addr);
192c08c320bSDavid Hildenbrand }
193c08c320bSDavid Hildenbrand
pte_alloc_one(void)194c08c320bSDavid Hildenbrand static inline pte_t *pte_alloc_one(void)
195c08c320bSDavid Hildenbrand {
196c08c320bSDavid Hildenbrand pte_t *pte = alloc_pages(PAGE_TABLE_ORDER);
197c08c320bSDavid Hildenbrand int i;
198c08c320bSDavid Hildenbrand
199c08c320bSDavid Hildenbrand for (i = 0; i < PAGE_TABLE_ENTRIES; i++)
200c08c320bSDavid Hildenbrand pte_val(pte[i]) = PAGE_ENTRY_I;
201c08c320bSDavid Hildenbrand return pte;
202c08c320bSDavid Hildenbrand }
203c08c320bSDavid Hildenbrand
pte_alloc(pmd_t * pmd,unsigned long addr)204c08c320bSDavid Hildenbrand static inline pte_t *pte_alloc(pmd_t *pmd, unsigned long addr)
205c08c320bSDavid Hildenbrand {
206c08c320bSDavid Hildenbrand if (pmd_none(*pmd)) {
207c08c320bSDavid Hildenbrand pte_t *pte = pte_alloc_one();
208149c39d6SClaudio Imbrenda pmd_val(*pmd) = __pa(pte) | SEGMENT_ENTRY_TT_SEGMENT;
209c08c320bSDavid Hildenbrand }
210c08c320bSDavid Hildenbrand return pte_offset(pmd, addr);
211c08c320bSDavid Hildenbrand }
212c08c320bSDavid Hildenbrand
ipte(unsigned long vaddr,pteval_t * p_pte)213c08c320bSDavid Hildenbrand static inline void ipte(unsigned long vaddr, pteval_t *p_pte)
214c08c320bSDavid Hildenbrand {
215149c39d6SClaudio Imbrenda unsigned long table_origin = (unsigned long)p_pte;
216c08c320bSDavid Hildenbrand
217c08c320bSDavid Hildenbrand asm volatile(
218c08c320bSDavid Hildenbrand " ipte %0,%1\n"
219c08c320bSDavid Hildenbrand : : "a" (table_origin), "a" (vaddr) : "memory");
220c08c320bSDavid Hildenbrand }
221c08c320bSDavid Hildenbrand
idte(unsigned long table_origin,unsigned long vaddr)222*81c4c141SClaudio Imbrenda static inline void idte(unsigned long table_origin, unsigned long vaddr)
223*81c4c141SClaudio Imbrenda {
224*81c4c141SClaudio Imbrenda vaddr &= SEGMENT_ENTRY_SFAA;
225*81c4c141SClaudio Imbrenda asm volatile(
226*81c4c141SClaudio Imbrenda " idte %0,0,%1\n"
227*81c4c141SClaudio Imbrenda : : "a" (table_origin), "a" (vaddr) : "memory");
228*81c4c141SClaudio Imbrenda }
229*81c4c141SClaudio Imbrenda
idte_pmdp(unsigned long vaddr,pmdval_t * pmdp)230*81c4c141SClaudio Imbrenda static inline void idte_pmdp(unsigned long vaddr, pmdval_t *pmdp)
231*81c4c141SClaudio Imbrenda {
232*81c4c141SClaudio Imbrenda idte((unsigned long)(pmdp - pmd_index(vaddr)) | ASCE_DT_SEGMENT, vaddr);
233*81c4c141SClaudio Imbrenda }
234*81c4c141SClaudio Imbrenda
idte_pudp(unsigned long vaddr,pudval_t * pudp)235*81c4c141SClaudio Imbrenda static inline void idte_pudp(unsigned long vaddr, pudval_t *pudp)
236*81c4c141SClaudio Imbrenda {
237*81c4c141SClaudio Imbrenda idte((unsigned long)(pudp - pud_index(vaddr)) | ASCE_DT_REGION3, vaddr);
238*81c4c141SClaudio Imbrenda }
239*81c4c141SClaudio Imbrenda
idte_p4dp(unsigned long vaddr,p4dval_t * p4dp)240*81c4c141SClaudio Imbrenda static inline void idte_p4dp(unsigned long vaddr, p4dval_t *p4dp)
241*81c4c141SClaudio Imbrenda {
242*81c4c141SClaudio Imbrenda idte((unsigned long)(p4dp - p4d_index(vaddr)) | ASCE_DT_REGION2, vaddr);
243*81c4c141SClaudio Imbrenda }
244*81c4c141SClaudio Imbrenda
idte_pgdp(unsigned long vaddr,pgdval_t * pgdp)245*81c4c141SClaudio Imbrenda static inline void idte_pgdp(unsigned long vaddr, pgdval_t *pgdp)
246*81c4c141SClaudio Imbrenda {
247*81c4c141SClaudio Imbrenda idte((unsigned long)(pgdp - pgd_index(vaddr)) | ASCE_DT_REGION1, vaddr);
248*81c4c141SClaudio Imbrenda }
249*81c4c141SClaudio Imbrenda
250c08c320bSDavid Hildenbrand #endif /* _ASMS390X_PGTABLE_H_ */
251