summaryrefslogtreecommitdiff
path: root/arch/tile/include/asm/pgtable_64.h
blob: fd80328523b47d9e253dbf90ce8d420ac395ad54 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
/*
 * Copyright 2011 Tilera Corporation. All Rights Reserved.
 *
 *   This program is free software; you can redistribute it and/or
 *   modify it under the terms of the GNU General Public License
 *   as published by the Free Software Foundation, version 2.
 *
 *   This program is distributed in the hope that it will be useful, but
 *   WITHOUT ANY WARRANTY; without even the implied warranty of
 *   MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE, GOOD TITLE or
 *   NON INFRINGEMENT.  See the GNU General Public License for
 *   more details.
 *
 */

#ifndef _ASM_TILE_PGTABLE_64_H
#define _ASM_TILE_PGTABLE_64_H

/* The level-0 page table breaks the address space into 32-bit chunks. */
#define PGDIR_SHIFT	HV_LOG2_L1_SPAN
#define PGDIR_SIZE	HV_L1_SPAN
#define PGDIR_MASK	(~(PGDIR_SIZE-1))
#define PTRS_PER_PGD	HV_L0_ENTRIES
#define SIZEOF_PGD	(PTRS_PER_PGD * sizeof(pgd_t))

/*
 * The level-1 index is defined by the huge page size.  A PMD is composed
 * of PTRS_PER_PMD pgd_t's and is the middle level of the page table.
 */
#define PMD_SHIFT	HV_LOG2_PAGE_SIZE_LARGE
#define PMD_SIZE	HV_PAGE_SIZE_LARGE
#define PMD_MASK	(~(PMD_SIZE-1))
#define PTRS_PER_PMD	(1 << (PGDIR_SHIFT - PMD_SHIFT))
#define SIZEOF_PMD	(PTRS_PER_PMD * sizeof(pmd_t))

/*
 * The level-2 index is defined by the difference between the huge
 * page size and the normal page size.  A PTE is composed of
 * PTRS_PER_PTE pte_t's and is the bottom level of the page table.
 * Note that the hypervisor docs use PTE for what we call pte_t, so
 * this nomenclature is somewhat confusing.
 */
#define PTRS_PER_PTE (1 << (HV_LOG2_PAGE_SIZE_LARGE - HV_LOG2_PAGE_SIZE_SMALL))
#define SIZEOF_PTE	(PTRS_PER_PTE * sizeof(pte_t))

/*
 * Align the vmalloc area to an L2 page table, and leave a guard page
 * at the beginning and end.  The vmalloc code also puts in an internal
 * guard page between each allocation.
 */
#define _VMALLOC_END	HUGE_VMAP_BASE
#define VMALLOC_END	(_VMALLOC_END - PAGE_SIZE)
#define VMALLOC_START	(_VMALLOC_START + PAGE_SIZE)

#define HUGE_VMAP_END	(HUGE_VMAP_BASE + PGDIR_SIZE)

#ifndef __ASSEMBLY__

/* We have no pud since we are a three-level page table. */
#include <asm-generic/pgtable-nopud.h>

static inline int pud_none(pud_t pud)
{
	return pud_val(pud) == 0;
}

static inline int pud_present(pud_t pud)
{
	return pud_val(pud) & _PAGE_PRESENT;
}

#define pmd_ERROR(e) \
	pr_err("%s:%d: bad pmd 0x%016llx.\n", __FILE__, __LINE__, pmd_val(e))

static inline void pud_clear(pud_t *pudp)
{
	__pte_clear(&pudp->pgd);
}

static inline int pud_bad(pud_t pud)
{
	return ((pud_val(pud) & _PAGE_ALL) != _PAGE_TABLE);
}

/* Return the page-table frame number (ptfn) that a pud_t points at. */
#define pud_ptfn(pud) hv_pte_get_ptfn((pud).pgd)

/*
 * A given kernel pud_t maps to a kernel pmd_t table at a specific
 * virtual address.  Since kernel pmd_t tables can be aligned at
 * sub-page granularity, this macro can return non-page-aligned
 * pointers, despite its name.
 */
#define pud_page_vaddr(pud) \
	(__va((phys_addr_t)pud_ptfn(pud) << HV_LOG2_PAGE_TABLE_ALIGN))

/*
 * A pud_t points to a pmd_t array.  Since we can have multiple per
 * page, we don't have a one-to-one mapping of pud_t's to pages.
 */
#define pud_page(pud) pfn_to_page(HV_PTFN_TO_PFN(pud_ptfn(pud)))

static inline unsigned long pud_index(unsigned long address)
{
	return (address >> PUD_SHIFT) & (PTRS_PER_PUD - 1);
}

#define pmd_offset(pud, address) \
	((pmd_t *)pud_page_vaddr(*(pud)) + pmd_index(address))

static inline void __set_pmd(pmd_t *pmdp, pmd_t pmdval)
{
	set_pte(pmdp, pmdval);
}

/* Create a pmd from a PTFN and pgprot. */
static inline pmd_t ptfn_pmd(unsigned long ptfn, pgprot_t prot)
{
	return hv_pte_set_ptfn(prot, ptfn);
}

/* Return the page-table frame number (ptfn) that a pmd_t points at. */
static inline unsigned long pmd_ptfn(pmd_t pmd)
{
	return hv_pte_get_ptfn(pmd);
}

static inline void pmd_clear(pmd_t *pmdp)
{
	__pte_clear(pmdp);
}

/* Normalize an address to having the correct high bits set. */
#define pgd_addr_normalize pgd_addr_normalize
static inline unsigned long pgd_addr_normalize(unsigned long addr)
{
	return ((long)addr << (CHIP_WORD_SIZE() - CHIP_VA_WIDTH())) >>
		(CHIP_WORD_SIZE() - CHIP_VA_WIDTH());
}

/* We don't define any pgds for these addresses. */
static inline int pgd_addr_invalid(unsigned long addr)
{
	return addr >= MEM_HV_START ||
		(addr > MEM_LOW_END && addr < MEM_HIGH_START);
}

/*
 * Use atomic instructions to provide atomicity against the hypervisor.
 */
#define __HAVE_ARCH_PTEP_TEST_AND_CLEAR_YOUNG
static inline int ptep_test_and_clear_young(struct vm_area_struct *vma,
					    unsigned long addr, pte_t *ptep)
{
	return (__insn_fetchand(&ptep->val, ~HV_PTE_ACCESSED) >>
		HV_PTE_INDEX_ACCESSED) & 0x1;
}

#define __HAVE_ARCH_PTEP_SET_WRPROTECT
static inline void ptep_set_wrprotect(struct mm_struct *mm,
				      unsigned long addr, pte_t *ptep)
{
	__insn_fetchand(&ptep->val, ~HV_PTE_WRITABLE);
}

#define __HAVE_ARCH_PTEP_GET_AND_CLEAR
static inline pte_t ptep_get_and_clear(struct mm_struct *mm,
				       unsigned long addr, pte_t *ptep)
{
	return hv_pte(__insn_exch(&ptep->val, 0UL));
}

#endif /* __ASSEMBLY__ */

#endif /* _ASM_TILE_PGTABLE_64_H */