diff options
Diffstat (limited to 'arch/s390/include/asm/pgtable.h')
-rw-r--r-- | arch/s390/include/asm/pgtable.h | 210 |
1 files changed, 210 insertions, 0 deletions
diff --git a/arch/s390/include/asm/pgtable.h b/arch/s390/include/asm/pgtable.h index 6bd7d7483017..979fe3dc0788 100644 --- a/arch/s390/include/asm/pgtable.h +++ b/arch/s390/include/asm/pgtable.h | |||
@@ -42,6 +42,7 @@ extern void fault_init(void); | |||
42 | * tables contain all the necessary information. | 42 | * tables contain all the necessary information. |
43 | */ | 43 | */ |
44 | #define update_mmu_cache(vma, address, ptep) do { } while (0) | 44 | #define update_mmu_cache(vma, address, ptep) do { } while (0) |
45 | #define update_mmu_cache_pmd(vma, address, ptep) do { } while (0) | ||
45 | 46 | ||
46 | /* | 47 | /* |
47 | * ZERO_PAGE is a global shared page that is always zero; used | 48 | * ZERO_PAGE is a global shared page that is always zero; used |
@@ -347,6 +348,12 @@ extern struct page *vmemmap; | |||
347 | 348 | ||
348 | #define _SEGMENT_ENTRY_LARGE 0x400 /* STE-format control, large page */ | 349 | #define _SEGMENT_ENTRY_LARGE 0x400 /* STE-format control, large page */ |
349 | #define _SEGMENT_ENTRY_CO 0x100 /* change-recording override */ | 350 | #define _SEGMENT_ENTRY_CO 0x100 /* change-recording override */ |
351 | #define _SEGMENT_ENTRY_SPLIT_BIT 0 /* THP splitting bit number */ | ||
352 | #define _SEGMENT_ENTRY_SPLIT (1UL << _SEGMENT_ENTRY_SPLIT_BIT) | ||
353 | |||
354 | /* Set of bits not changed in pmd_modify */ | ||
355 | #define _SEGMENT_CHG_MASK (_SEGMENT_ENTRY_ORIGIN | _SEGMENT_ENTRY_LARGE \ | ||
356 | | _SEGMENT_ENTRY_SPLIT | _SEGMENT_ENTRY_CO) | ||
350 | 357 | ||
351 | /* Page status table bits for virtualization */ | 358 | /* Page status table bits for virtualization */ |
352 | #define RCP_ACC_BITS 0xf000000000000000UL | 359 | #define RCP_ACC_BITS 0xf000000000000000UL |
@@ -506,6 +513,30 @@ static inline int pmd_bad(pmd_t pmd) | |||
506 | return (pmd_val(pmd) & mask) != _SEGMENT_ENTRY; | 513 | return (pmd_val(pmd) & mask) != _SEGMENT_ENTRY; |
507 | } | 514 | } |
508 | 515 | ||
516 | #define __HAVE_ARCH_PMDP_SPLITTING_FLUSH | ||
517 | extern void pmdp_splitting_flush(struct vm_area_struct *vma, | ||
518 | unsigned long addr, pmd_t *pmdp); | ||
519 | |||
520 | #define __HAVE_ARCH_PMDP_SET_ACCESS_FLAGS | ||
521 | extern int pmdp_set_access_flags(struct vm_area_struct *vma, | ||
522 | unsigned long address, pmd_t *pmdp, | ||
523 | pmd_t entry, int dirty); | ||
524 | |||
525 | #define __HAVE_ARCH_PMDP_CLEAR_YOUNG_FLUSH | ||
526 | extern int pmdp_clear_flush_young(struct vm_area_struct *vma, | ||
527 | unsigned long address, pmd_t *pmdp); | ||
528 | |||
529 | #define __HAVE_ARCH_PMD_WRITE | ||
530 | static inline int pmd_write(pmd_t pmd) | ||
531 | { | ||
532 | return (pmd_val(pmd) & _SEGMENT_ENTRY_RO) == 0; | ||
533 | } | ||
534 | |||
535 | static inline int pmd_young(pmd_t pmd) | ||
536 | { | ||
537 | return 0; | ||
538 | } | ||
539 | |||
509 | static inline int pte_none(pte_t pte) | 540 | static inline int pte_none(pte_t pte) |
510 | { | 541 | { |
511 | return (pte_val(pte) & _PAGE_INVALID) && !(pte_val(pte) & _PAGE_SWT); | 542 | return (pte_val(pte) & _PAGE_INVALID) && !(pte_val(pte) & _PAGE_SWT); |
@@ -1159,6 +1190,185 @@ static inline pmd_t *pmd_offset(pud_t *pud, unsigned long address) | |||
1159 | #define pte_offset_map(pmd, address) pte_offset_kernel(pmd, address) | 1190 | #define pte_offset_map(pmd, address) pte_offset_kernel(pmd, address) |
1160 | #define pte_unmap(pte) do { } while (0) | 1191 | #define pte_unmap(pte) do { } while (0) |
1161 | 1192 | ||
1193 | static inline void __pmd_idte(unsigned long address, pmd_t *pmdp) | ||
1194 | { | ||
1195 | unsigned long sto = (unsigned long) pmdp - | ||
1196 | pmd_index(address) * sizeof(pmd_t); | ||
1197 | |||
1198 | if (!(pmd_val(*pmdp) & _SEGMENT_ENTRY_INV)) { | ||
1199 | asm volatile( | ||
1200 | " .insn rrf,0xb98e0000,%2,%3,0,0" | ||
1201 | : "=m" (*pmdp) | ||
1202 | : "m" (*pmdp), "a" (sto), | ||
1203 | "a" ((address & HPAGE_MASK)) | ||
1204 | : "cc" | ||
1205 | ); | ||
1206 | } | ||
1207 | } | ||
1208 | |||
1209 | #ifdef CONFIG_TRANSPARENT_HUGEPAGE | ||
1210 | #define __HAVE_ARCH_PGTABLE_DEPOSIT | ||
1211 | extern void pgtable_trans_huge_deposit(struct mm_struct *mm, pgtable_t pgtable); | ||
1212 | |||
1213 | #define __HAVE_ARCH_PGTABLE_WITHDRAW | ||
1214 | extern pgtable_t pgtable_trans_huge_withdraw(struct mm_struct *mm); | ||
1215 | |||
1216 | static inline int pmd_trans_splitting(pmd_t pmd) | ||
1217 | { | ||
1218 | return pmd_val(pmd) & _SEGMENT_ENTRY_SPLIT; | ||
1219 | } | ||
1220 | |||
1221 | static inline void set_pmd_at(struct mm_struct *mm, unsigned long addr, | ||
1222 | pmd_t *pmdp, pmd_t entry) | ||
1223 | { | ||
1224 | *pmdp = entry; | ||
1225 | } | ||
1226 | |||
1227 | static inline unsigned long massage_pgprot_pmd(pgprot_t pgprot) | ||
1228 | { | ||
1229 | unsigned long pgprot_pmd = 0; | ||
1230 | |||
1231 | if (pgprot_val(pgprot) & _PAGE_INVALID) { | ||
1232 | if (pgprot_val(pgprot) & _PAGE_SWT) | ||
1233 | pgprot_pmd |= _HPAGE_TYPE_NONE; | ||
1234 | pgprot_pmd |= _SEGMENT_ENTRY_INV; | ||
1235 | } | ||
1236 | if (pgprot_val(pgprot) & _PAGE_RO) | ||
1237 | pgprot_pmd |= _SEGMENT_ENTRY_RO; | ||
1238 | return pgprot_pmd; | ||
1239 | } | ||
1240 | |||
1241 | static inline pmd_t pmd_modify(pmd_t pmd, pgprot_t newprot) | ||
1242 | { | ||
1243 | pmd_val(pmd) &= _SEGMENT_CHG_MASK; | ||
1244 | pmd_val(pmd) |= massage_pgprot_pmd(newprot); | ||
1245 | return pmd; | ||
1246 | } | ||
1247 | |||
1248 | static inline pmd_t pmd_mkhuge(pmd_t pmd) | ||
1249 | { | ||
1250 | pmd_val(pmd) |= _SEGMENT_ENTRY_LARGE; | ||
1251 | return pmd; | ||
1252 | } | ||
1253 | |||
1254 | static inline pmd_t pmd_mkwrite(pmd_t pmd) | ||
1255 | { | ||
1256 | pmd_val(pmd) &= ~_SEGMENT_ENTRY_RO; | ||
1257 | return pmd; | ||
1258 | } | ||
1259 | |||
1260 | static inline pmd_t pmd_wrprotect(pmd_t pmd) | ||
1261 | { | ||
1262 | pmd_val(pmd) |= _SEGMENT_ENTRY_RO; | ||
1263 | return pmd; | ||
1264 | } | ||
1265 | |||
1266 | static inline pmd_t pmd_mkdirty(pmd_t pmd) | ||
1267 | { | ||
1268 | /* No dirty bit in the segment table entry. */ | ||
1269 | return pmd; | ||
1270 | } | ||
1271 | |||
1272 | static inline pmd_t pmd_mkold(pmd_t pmd) | ||
1273 | { | ||
1274 | /* No referenced bit in the segment table entry. */ | ||
1275 | return pmd; | ||
1276 | } | ||
1277 | |||
1278 | static inline pmd_t pmd_mkyoung(pmd_t pmd) | ||
1279 | { | ||
1280 | /* No referenced bit in the segment table entry. */ | ||
1281 | return pmd; | ||
1282 | } | ||
1283 | |||
1284 | #define __HAVE_ARCH_PMDP_TEST_AND_CLEAR_YOUNG | ||
1285 | static inline int pmdp_test_and_clear_young(struct vm_area_struct *vma, | ||
1286 | unsigned long address, pmd_t *pmdp) | ||
1287 | { | ||
1288 | unsigned long pmd_addr = pmd_val(*pmdp) & HPAGE_MASK; | ||
1289 | long tmp, rc; | ||
1290 | int counter; | ||
1291 | |||
1292 | rc = 0; | ||
1293 | if (MACHINE_HAS_RRBM) { | ||
1294 | counter = PTRS_PER_PTE >> 6; | ||
1295 | asm volatile( | ||
1296 | "0: .insn rre,0xb9ae0000,%0,%3\n" /* rrbm */ | ||
1297 | " ogr %1,%0\n" | ||
1298 | " la %3,0(%4,%3)\n" | ||
1299 | " brct %2,0b\n" | ||
1300 | : "=&d" (tmp), "+&d" (rc), "+d" (counter), | ||
1301 | "+a" (pmd_addr) | ||
1302 | : "a" (64 * 4096UL) : "cc"); | ||
1303 | rc = !!rc; | ||
1304 | } else { | ||
1305 | counter = PTRS_PER_PTE; | ||
1306 | asm volatile( | ||
1307 | "0: rrbe 0,%2\n" | ||
1308 | " la %2,0(%3,%2)\n" | ||
1309 | " brc 12,1f\n" | ||
1310 | " lhi %0,1\n" | ||
1311 | "1: brct %1,0b\n" | ||
1312 | : "+d" (rc), "+d" (counter), "+a" (pmd_addr) | ||
1313 | : "a" (4096UL) : "cc"); | ||
1314 | } | ||
1315 | return rc; | ||
1316 | } | ||
1317 | |||
1318 | #define __HAVE_ARCH_PMDP_GET_AND_CLEAR | ||
1319 | static inline pmd_t pmdp_get_and_clear(struct mm_struct *mm, | ||
1320 | unsigned long address, pmd_t *pmdp) | ||
1321 | { | ||
1322 | pmd_t pmd = *pmdp; | ||
1323 | |||
1324 | __pmd_idte(address, pmdp); | ||
1325 | pmd_clear(pmdp); | ||
1326 | return pmd; | ||
1327 | } | ||
1328 | |||
1329 | #define __HAVE_ARCH_PMDP_CLEAR_FLUSH | ||
1330 | static inline pmd_t pmdp_clear_flush(struct vm_area_struct *vma, | ||
1331 | unsigned long address, pmd_t *pmdp) | ||
1332 | { | ||
1333 | return pmdp_get_and_clear(vma->vm_mm, address, pmdp); | ||
1334 | } | ||
1335 | |||
1336 | #define __HAVE_ARCH_PMDP_INVALIDATE | ||
1337 | static inline void pmdp_invalidate(struct vm_area_struct *vma, | ||
1338 | unsigned long address, pmd_t *pmdp) | ||
1339 | { | ||
1340 | __pmd_idte(address, pmdp); | ||
1341 | } | ||
1342 | |||
1343 | static inline pmd_t mk_pmd_phys(unsigned long physpage, pgprot_t pgprot) | ||
1344 | { | ||
1345 | pmd_t __pmd; | ||
1346 | pmd_val(__pmd) = physpage + massage_pgprot_pmd(pgprot); | ||
1347 | return __pmd; | ||
1348 | } | ||
1349 | |||
1350 | #define pfn_pmd(pfn, pgprot) mk_pmd_phys(__pa((pfn) << PAGE_SHIFT), (pgprot)) | ||
1351 | #define mk_pmd(page, pgprot) pfn_pmd(page_to_pfn(page), (pgprot)) | ||
1352 | |||
1353 | static inline int pmd_trans_huge(pmd_t pmd) | ||
1354 | { | ||
1355 | return pmd_val(pmd) & _SEGMENT_ENTRY_LARGE; | ||
1356 | } | ||
1357 | |||
1358 | static inline int has_transparent_hugepage(void) | ||
1359 | { | ||
1360 | return MACHINE_HAS_HPAGE ? 1 : 0; | ||
1361 | } | ||
1362 | |||
1363 | static inline unsigned long pmd_pfn(pmd_t pmd) | ||
1364 | { | ||
1365 | if (pmd_trans_huge(pmd)) | ||
1366 | return pmd_val(pmd) >> HPAGE_SHIFT; | ||
1367 | else | ||
1368 | return pmd_val(pmd) >> PAGE_SHIFT; | ||
1369 | } | ||
1370 | #endif /* CONFIG_TRANSPARENT_HUGEPAGE */ | ||
1371 | |||
1162 | /* | 1372 | /* |
1163 | * 31 bit swap entry format: | 1373 | * 31 bit swap entry format: |
1164 | * A page-table entry has some bits we have to treat in a special way. | 1374 | * A page-table entry has some bits we have to treat in a special way. |