summaryrefslogtreecommitdiffstats
path: root/arch/tile/include/asm/page.h
blob: 7d90641cf18d8e58a1a6e39d4ef83292a316388d (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
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
/*
 * Copyright 2010 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_PAGE_H
#define _ASM_TILE_PAGE_H

#include <linux/const.h>

/* PAGE_SHIFT and HPAGE_SHIFT determine the page sizes. */
#define PAGE_SHIFT	16
#define HPAGE_SHIFT	24

#define PAGE_SIZE	(_AC(1, UL) << PAGE_SHIFT)
#define HPAGE_SIZE	(_AC(1, UL) << HPAGE_SHIFT)

#define PAGE_MASK	(~(PAGE_SIZE - 1))
#define HPAGE_MASK	(~(HPAGE_SIZE - 1))

#ifdef __KERNEL__

#include <hv/hypervisor.h>
#include <arch/chip.h>

/*
 * The {,H}PAGE_SHIFT values must match the HV_LOG2_PAGE_SIZE_xxx
 * definitions in <hv/hypervisor.h>.  We validate this at build time
 * here, and again at runtime during early boot.  We provide a
 * separate definition since userspace doesn't have <hv/hypervisor.h>.
 *
 * Be careful to distinguish PAGE_SHIFT from HV_PTE_INDEX_PFN, since
 * they are the same on i386 but not TILE.
 */
#if HV_LOG2_PAGE_SIZE_SMALL != PAGE_SHIFT
# error Small page size mismatch in Linux
#endif
#if HV_LOG2_PAGE_SIZE_LARGE != HPAGE_SHIFT
# error Huge page size mismatch in Linux
#endif

#ifndef __ASSEMBLY__

#include <linux/types.h>
#include <linux/string.h>

struct page;

static inline void clear_page(void *page)
{
	memset(page, 0, PAGE_SIZE);
}

static inline void copy_page(void *to, void *from)
{
	memcpy(to, from, PAGE_SIZE);
}

static inline void clear_user_page(void *page, unsigned long vaddr,
				struct page *pg)
{
	clear_page(page);
}

static inline void copy_user_page(void *to, void *from, unsigned long vaddr,
				struct page *topage)
{
	copy_page(to, from);
}

/*
 * Hypervisor page tables are made of the same basic structure.
 */

typedef __u64 pteval_t;
typedef __u64 pmdval_t;
typedef __u64 pudval_t;
typedef __u64 pgdval_t;
typedef __u64 pgprotval_t;

typedef HV_PTE pte_t;
typedef HV_PTE pgd_t;
typedef HV_PTE pgprot_t;

/*
 * User L2 page tables are managed as one L2 page table per page,
 * because we use the page allocator for them.  This keeps the allocation
 * simple and makes it potentially useful to implement HIGHPTE at some point.
 * However, it's also inefficient, since L2 page tables are much smaller
 * than pages (currently 2KB vs 64KB).  So we should revisit this.
 */
typedef struct page *pgtable_t;

/* Must be a macro since it is used to create constants. */
#define __pgprot(val) hv_pte(val)

static inline u64 pgprot_val(pgprot_t pgprot)
{
	return hv_pte_val(pgprot);
}

static inline u64 pte_val(pte_t pte)
{
	return hv_pte_val(pte);
}

static inline u64 pgd_val(pgd_t pgd)
{
	return hv_pte_val(pgd);
}

#ifdef __tilegx__

typedef HV_PTE pmd_t;

static inline u64 pmd_val(pmd_t pmd)
{
	return hv_pte_val(pmd);
}

#endif

static inline __attribute_const__ int get_order(unsigned long size)
{
	return BITS_PER_LONG - __builtin_clzl((size - 1) >> PAGE_SHIFT);
}

#endif /* !__ASSEMBLY__ */

#define HUGETLB_PAGE_ORDER	(HPAGE_SHIFT - PAGE_SHIFT)

#define HUGE_MAX_HSTATE		2

#ifdef CONFIG_HUGETLB_PAGE
#define HAVE_ARCH_HUGETLB_UNMAPPED_AREA
#endif

/* Each memory controller has PAs distinct in their high bits. */
#define NR_PA_HIGHBIT_SHIFT (CHIP_PA_WIDTH() - CHIP_LOG_NUM_MSHIMS())
#define NR_PA_HIGHBIT_VALUES (1 << CHIP_LOG_NUM_MSHIMS())
#define __pa_to_highbits(pa) ((phys_addr_t)(pa) >> NR_PA_HIGHBIT_SHIFT)
#define __pfn_to_highbits(pfn) ((pfn) >> (NR_PA_HIGHBIT_SHIFT - PAGE_SHIFT))

#ifdef __tilegx__

/*
 * We reserve the lower half of memory for user-space programs, and the
 * upper half for system code.  We re-map all of physical memory in the
 * upper half, which takes a quarter of our VA space.  Then we have
 * the vmalloc regions.  The supervisor code lives at 0xfffffff700000000,
 * with the hypervisor above that.
 *
 * Loadable kernel modules are placed immediately after the static
 * supervisor code, with each being allocated a 256MB region of
 * address space, so we don't have to worry about the range of "jal"
 * and other branch instructions.
 *
 * For now we keep life simple and just allocate one pmd (4GB) for vmalloc.
 * Similarly, for now we don't play any struct page mapping games.
 */

#if CHIP_PA_WIDTH() + 2 > CHIP_VA_WIDTH()
# error Too much PA to map with the VA available!
#endif
#define HALF_VA_SPACE           (_AC(1, UL) << (CHIP_VA_WIDTH() - 1))

#define MEM_LOW_END		(HALF_VA_SPACE - 1)         /* low half */
#define MEM_HIGH_START		(-HALF_VA_SPACE)            /* high half */
#define PAGE_OFFSET		MEM_HIGH_START
#define _VMALLOC_START		_AC(0xfffffff500000000, UL) /* 4 GB */
#define HUGE_VMAP_BASE		_AC(0xfffffff600000000, UL) /* 4 GB */
#define MEM_SV_START		_AC(0xfffffff700000000, UL) /* 256 MB */
#define MEM_SV_INTRPT		MEM_SV_START
#define MEM_MODULE_START	_AC(0xfffffff710000000, UL) /* 256 MB */
#define MEM_MODULE_END		(MEM_MODULE_START + (256*1024*1024))
#define MEM_HV_START		_AC(0xfffffff800000000, UL) /* 32 GB */

/* Highest DTLB address we will use */
#define KERNEL_HIGH_VADDR	MEM_SV_START

/* Since we don't currently provide any fixmaps, we use an impossible VA. */
#define FIXADDR_TOP             MEM_HV_START

#else /* !__tilegx__ */

/*
 * A PAGE_OFFSET of 0xC0000000 means that the kernel has
 * a virtual address space of one gigabyte, which limits the
 * amount of physical memory you can use to about 768MB.
 * If you want more physical memory than this then see the CONFIG_HIGHMEM
 * option in the kernel configuration.
 *
 * The top two 16MB chunks in the table below (VIRT and HV) are
 * unavailable to Linux.  Since the kernel interrupt vectors must live
 * at 0xfd000000, we map all of the bottom of RAM at this address with
 * a huge page table entry to minimize its ITLB footprint (as well as
 * at PAGE_OFFSET).  The last architected requirement is that user
 * interrupt vectors live at 0xfc000000, so we make that range of
 * memory available to user processes.  The remaining regions are sized
 * as shown; after the first four addresses, we show "typical" values,
 * since the actual addresses depend on kernel #defines.
 *
 * MEM_VIRT_INTRPT                 0xff000000
 * MEM_HV_INTRPT                   0xfe000000
 * MEM_SV_INTRPT (kernel code)     0xfd000000
 * MEM_USER_INTRPT (user vector)   0xfc000000
 * FIX_KMAP_xxx                    0xf8000000 (via NR_CPUS * KM_TYPE_NR)
 * PKMAP_BASE                      0xf7000000 (via LAST_PKMAP)
 * HUGE_VMAP                       0xf3000000 (via CONFIG_NR_HUGE_VMAPS)
 * VMALLOC_START                   0xf0000000 (via __VMALLOC_RESERVE)
 * mapped LOWMEM                   0xc0000000
 */

#define MEM_USER_INTRPT		_AC(0xfc000000, UL)
#define MEM_SV_INTRPT		_AC(0xfd000000, UL)
#define MEM_HV_INTRPT		_AC(0xfe000000, UL)
#define MEM_VIRT_INTRPT		_AC(0xff000000, UL)

#define INTRPT_SIZE		0x4000

/* Tolerate page size larger than the architecture interrupt region size. */
#if PAGE_SIZE > INTRPT_SIZE
#undef INTRPT_SIZE
#define INTRPT_SIZE PAGE_SIZE
#endif

#define KERNEL_HIGH_VADDR	MEM_USER_INTRPT
#define FIXADDR_TOP		(KERNEL_HIGH_VADDR - PAGE_SIZE)

#define PAGE_OFFSET		_AC(CONFIG_PAGE_OFFSET, UL)

/* On 32-bit architectures we mix kernel modules in with other vmaps. */
#define MEM_MODULE_START	VMALLOC_START
#define MEM_MODULE_END		VMALLOC_END

#endif /* __tilegx__ */

#ifndef __ASSEMBLY__

#ifdef CONFIG_HIGHMEM

/* Map kernel virtual addresses to page frames, in HPAGE_SIZE chunks. */
extern unsigned long pbase_map[];
extern void *vbase_map[];

static inline unsigned long kaddr_to_pfn(const volatile void *_kaddr)
{
	unsigned long kaddr = (unsigned long)_kaddr;
	return pbase_map[kaddr >> HPAGE_SHIFT] +
		((kaddr & (HPAGE_SIZE - 1)) >> PAGE_SHIFT);
}

static inline void *pfn_to_kaddr(unsigned long pfn)
{
	return vbase_map[__pfn_to_highbits(pfn)] + (pfn << PAGE_SHIFT);
}

static inline phys_addr_t virt_to_phys(const volatile void *kaddr)
{
	unsigned long pfn = kaddr_to_pfn(kaddr);
	return ((phys_addr_t)pfn << PAGE_SHIFT) +
		((unsigned long)kaddr & (PAGE_SIZE-1));
}

static inline void *phys_to_virt(phys_addr_t paddr)
{
	return pfn_to_kaddr(paddr >> PAGE_SHIFT) + (paddr & (PAGE_SIZE-1));
}

/* With HIGHMEM, we pack PAGE_OFFSET through high_memory with all valid VAs. */
static inline int virt_addr_valid(const volatile void *kaddr)
{
	extern void *high_memory;  /* copied from <linux/mm.h> */
	return ((unsigned long)kaddr >= PAGE_OFFSET && kaddr < high_memory);
}

#else /* !CONFIG_HIGHMEM */

static inline unsigned long kaddr_to_pfn(const volatile void *kaddr)
{
	return ((unsigned long)kaddr - PAGE_OFFSET) >> PAGE_SHIFT;
}

static inline void *pfn_to_kaddr(unsigned long pfn)
{
	return (void *)((pfn << PAGE_SHIFT) + PAGE_OFFSET);
}

static inline phys_addr_t virt_to_phys(const volatile void *kaddr)
{
	return (phys_addr_t)((unsigned long)kaddr - PAGE_OFFSET);
}

static inline void *phys_to_virt(phys_addr_t paddr)
{
	return (void *)((unsigned long)paddr + PAGE_OFFSET);
}

/* Check that the given address is within some mapped range of PAs. */
#define virt_addr_valid(kaddr) pfn_valid(kaddr_to_pfn(kaddr))

#endif /* !CONFIG_HIGHMEM */

/* All callers are not consistent in how they call these functions. */
#define __pa(kaddr) virt_to_phys((void *)(unsigned long)(kaddr))
#define __va(paddr) phys_to_virt((phys_addr_t)(paddr))

extern int devmem_is_allowed(unsigned long pagenr);

#ifdef CONFIG_FLATMEM
static inline int pfn_valid(unsigned long pfn)
{
	return pfn < max_mapnr;
}
#endif

/* Provide as macros since these require some other headers included. */
#define page_to_pa(page) ((phys_addr_t)(page_to_pfn(page)) << PAGE_SHIFT)
#define virt_to_page(kaddr) pfn_to_page(kaddr_to_pfn(kaddr))
#define page_to_virt(page) pfn_to_kaddr(page_to_pfn(page))

struct mm_struct;
extern pte_t *virt_to_pte(struct mm_struct *mm, unsigned long addr);

#endif /* !__ASSEMBLY__ */

#define VM_DATA_DEFAULT_FLAGS \
	(VM_READ | VM_WRITE | VM_MAYREAD | VM_MAYWRITE | VM_MAYEXEC)

#include <asm-generic/memory_model.h>

#endif /* __KERNEL__ */

#endif /* _ASM_TILE_PAGE_H */