summaryrefslogtreecommitdiffstats
path: root/arch/parisc/mm/init.c
blob: c3e16c4638326d7a429f1272997973171af3c499 (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
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
/*
 *  linux/arch/parisc/mm/init.c
 *
 *  Copyright (C) 1995	Linus Torvalds
 *  Copyright 1999 SuSE GmbH
 *    changed by Philipp Rumpf
 *  Copyright 1999 Philipp Rumpf (prumpf@tux.org)
 *
 */

#include <linux/config.h>

#include <linux/mm.h>
#include <linux/bootmem.h>
#include <linux/delay.h>
#include <linux/init.h>
#include <linux/pci.h>		/* for hppa_dma_ops and pcxl_dma_ops */
#include <linux/swap.h>
#include <linux/unistd.h>

#include <asm/pgalloc.h>

static unsigned long totalram_pages;
extern unsigned long max_pfn, mem_max;

void free_initmem(void)  {
}

/*
 * Just an arbitrary offset to serve as a "hole" between mapping areas
 * (between top of physical memory and a potential pcxl dma mapping
 * area, and below the vmalloc mapping area).
 *
 * The current 32K value just means that there will be a 32K "hole"
 * between mapping areas. That means that  any out-of-bounds memory
 * accesses will hopefully be caught. The vmalloc() routines leaves
 * a hole of 4kB between each vmalloced area for the same reason.
 */

#define VM_MAP_OFFSET  (32*1024)
#define SET_MAP_OFFSET(x) ((void *)(((unsigned long)(x) + VM_MAP_OFFSET) \
				     & ~(VM_MAP_OFFSET-1)))

void *vmalloc_start;
unsigned long pcxl_dma_start;

void __init mem_init(void)
{
	max_mapnr = num_physpages = max_low_pfn;
	high_memory = __va(max_low_pfn * PAGE_SIZE);

	totalram_pages += free_all_bootmem();
	printk("Memory: %luk available\n", totalram_pages << (PAGE_SHIFT-10));

	if (hppa_dma_ops == &pcxl_dma_ops) {
	    pcxl_dma_start = (unsigned long)SET_MAP_OFFSET(high_memory);
	    vmalloc_start = SET_MAP_OFFSET(pcxl_dma_start + PCXL_DMA_MAP_SIZE);
	}
	else {
	    pcxl_dma_start = 0;
	    vmalloc_start = SET_MAP_OFFSET(high_memory);
	}
}

void __bad_pgd(pgd_t *pgd)
{
	printk("Bad pgd in pmd_alloc: %08lx\n", pgd_val(*pgd));
	pgd_val(*pgd) = _PAGE_TABLE + __pa(BAD_PAGETABLE);
}

void __bad_pmd(pmd_t *pmd)
{
	printk("Bad pmd in pte_alloc: %08lx\n", pmd_val(*pmd));
	pmd_val(*pmd) = _PAGE_TABLE + __pa(BAD_PAGETABLE);
}

pte_t *get_pte_slow(pmd_t *pmd, unsigned long offset)
{
	pte_t *pte;

	pte = (pte_t *) __get_free_page(GFP_KERNEL);

	if (pmd_none(*pmd)) {
		if (pte) {
			clear_page(pte);
			pmd_val(*pmd) = _PAGE_TABLE + __pa((unsigned long)pte);
			return pte + offset;
		}
		pmd_val(*pmd) = _PAGE_TABLE + __pa(BAD_PAGETABLE);
		return NULL;
	}

	free_page((unsigned long)pte);

	if (pmd_bad(*pmd)) {
		__bad_pmd(pmd);
		return NULL;
	}

	return (pte_t *) pmd_page(*pmd) + offset;
}

int do_check_pgt_cache(int low, int high)
{
	return 0;
}

/*
 * BAD_PAGE is the page that is used for page faults when linux
 * is out-of-memory. Older versions of linux just did a
 * do_exit(), but using this instead means there is less risk
 * for a process dying in kernel mode, possibly leaving an inode
 * unused etc..
 *
 * BAD_PAGETABLE is the accompanying page-table: it is initialized
 * to point to BAD_PAGE entries.
 *
 * ZERO_PAGE is a special page that is used for zero-initialized
 * data and COW.
 */
pte_t * __bad_pagetable(void)
{
	return (pte_t *) NULL;
}

unsigned long *empty_zero_page;
unsigned long *empty_bad_page;

pte_t __bad_page(void)
{
	return *(pte_t *)NULL;
}

void show_mem(void)
{
	int i,free = 0,total = 0,reserved = 0;
	int shared = 0, cached = 0;

	printk("Mem-info:\n");
	show_free_areas();
	printk("Free swap:	 %6dkB\n",nr_swap_pages<<(PAGE_SHIFT-10));
	i = max_mapnr;
	while (i-- > 0) {
		total++;
		if (PageReserved(mem_map+i))
			reserved++;
		else if (PageSwapCache(mem_map+i))
			cached++;
		else if (!atomic_read(&mem_map[i].count))
			free++;
		else
			shared += atomic_read(&mem_map[i].count) - 1;
	}
	printk("%d pages of RAM\n",total);
	printk("%d reserved pages\n",reserved);
	printk("%d pages shared\n",shared);
	printk("%d pages swap cached\n",cached);
	show_buffers();
}

void set_pte_phys (unsigned long vaddr, unsigned long phys)
{
}


/*
 * pagetable_init() sets up the page tables
 *
 * Note that gateway_init() places the Linux gateway page at page 0.
 * Since gateway pages cannot be dereferenced this has the desirable
 * side effect of trapping those pesky NULL-reference errors in the
 * kernel.
 */
static void __init pagetable_init(void)
{
	pgd_t *pg_dir;
	pmd_t *pmd;
	pte_t *pg_table;
	unsigned long tmp1;
	unsigned long tmp2;
	unsigned long address;
	unsigned long ro_start;
	unsigned long ro_end;
	unsigned long fv_addr;
	extern  const int stext;
	extern  int data_start;
	extern  const unsigned long fault_vector_20;

	ro_start = __pa((unsigned long)&stext);
	ro_end   = __pa((unsigned long)&data_start);
	fv_addr  = __pa((unsigned long)&fault_vector_20) & PAGE_MASK;

	printk("pagetable_init\n");

	/* Map whole memory from PAGE_OFFSET */
	pg_dir = (pgd_t *)swapper_pg_dir + USER_PGD_PTRS;

	address = 0;
	while (address < mem_max) {
		/* XXX: BTLB should be done here */

#if PTRS_PER_PMD == 1
		pmd = (pmd_t *)__pa(pg_dir);
#else
		pmd = (pmd_t *) (PAGE_MASK & pgd_val(*pg_dir));

		/*
		 * pmd is physical at this point
		 */

		if (!pmd) {
			pmd = (pmd_t *) alloc_bootmem_low_pages(PAGE_SIZE);
			pmd = (pmd_t *) __pa(pmd);
		}

		pgd_val(*pg_dir) = _PAGE_TABLE | (unsigned long) pmd;
#endif
		pg_dir++;

		/* now change pmd to kernel virtual addresses */

		pmd = (pmd_t *) __va(pmd);
		for (tmp1 = 0 ; tmp1 < PTRS_PER_PMD ; tmp1++,pmd++) {

			/*
			 * pg_table is physical at this point
			 */

			pg_table = (pte_t *) (PAGE_MASK & pmd_val(*pmd));
			if (!pg_table) {
				pg_table = (pte_t *)
					alloc_bootmem_low_pages(PAGE_SIZE);
				pg_table = (pte_t *) __pa(pg_table);
			}

			pmd_val(*pmd) = _PAGE_TABLE |
					   (unsigned long) pg_table;

			/* now change pg_table to kernel virtual addresses */

			pg_table = (pte_t *) __va(pg_table);
			for (tmp2=0; tmp2 < PTRS_PER_PTE; tmp2++,pg_table++) {
				pte_t pte;

#if !defined(CONFIG_KWDB) && !defined(CONFIG_STI_CONSOLE)
#warning STI console should explicitly allocate executable pages but does not
/* KWDB needs to write kernel text when setting break points.
**
** The right thing to do seems like KWDB modify only the pte which
** has a break point on it...otherwise we might mask worse bugs.
*/
				if (address >= ro_start && address < ro_end
							&& address != fv_addr)
				    pte = __mk_pte(address, PAGE_KERNEL_RO);
				else
#endif
				    pte = __mk_pte(address, PAGE_KERNEL);

				if (address >= mem_max)
					pte_val(pte) = 0;

				set_pte(pg_table, pte);

				address += PAGE_SIZE;
			}

			if (address >= mem_max)
			    break;
		}
	}

	empty_zero_page = alloc_bootmem_pages(PAGE_SIZE);
	memset(empty_zero_page, 0, PAGE_SIZE);
}

unsigned long gateway_pgd_offset;
unsigned long gateway_pgd_entry;

static void __init gateway_init(void)
{
	unsigned long hpux_gateway_page_addr;
	unsigned long linux_gateway_page_addr;
	pgd_t *pg_dir;
	pmd_t *pmd_base;
	pmd_t *pmd;
	pte_t *pg_table_base;
	pte_t *pg_table;
	/* FIXME: These are 'const' in order to trick the compiler
           into not treating them as DP-relative data. */
	extern void * const hpux_gateway_page;
	extern void * const linux_gateway_page;
	pte_t pte;

	hpux_gateway_page_addr = HPUX_GATEWAY_ADDR & PAGE_MASK;
	linux_gateway_page_addr = LINUX_GATEWAY_ADDR & PAGE_MASK;

	gateway_pgd_offset = hpux_gateway_page_addr >> PGDIR_SHIFT;

	/*
	 * Setup Linux Gateway page.
	 *
	 * The Linux gateway page will reside in kernel space (on virtual
	 * page 0), so it doesn't need to be aliased into user space.
	 */

	pg_dir = (pgd_t *)swapper_pg_dir;

#if PTRS_PER_PMD == 1
	pmd_base = (pmd_t *)pg_dir;
	pmd = pmd_base +
		((linux_gateway_page_addr) >> PGDIR_SHIFT);

#else
	pmd_base = (pmd_t *) alloc_bootmem_pages(PAGE_SIZE);
	pgd_val(*(pg_dir + (linux_gateway_page_addr >> PGDIR_SHIFT))) =
		_PAGE_TABLE | __pa(pmd_base);

	pmd = pmd_base +
		((linux_gateway_page_addr & (PMD_MASK) & (PGDIR_SIZE - 1)) >>
								PMD_SHIFT);
#endif

	pg_table_base = (pte_t *) alloc_bootmem_pages(PAGE_SIZE);

	pmd_val(*pmd) = _PAGE_TABLE | __pa(pg_table_base);

	pte = __mk_pte(__pa(&linux_gateway_page), PAGE_GATEWAY);

	pg_table = pg_table_base +
		((linux_gateway_page_addr & (PAGE_MASK) & (PMD_SIZE - 1)) >>
								PAGE_SHIFT);

	set_pte(pg_table,pte);

	/*
	 * Setup HP-UX gateway page.
	 * This page will be aliased into each user address space.
	 */

	pg_table_base = (pte_t *) alloc_bootmem_pages(PAGE_SIZE);

	pte = __mk_pte(__pa(&hpux_gateway_page), PAGE_GATEWAY);
	pg_table = pg_table_base +
		((hpux_gateway_page_addr & (PAGE_MASK) & (PMD_SIZE - 1)) >>
								PAGE_SHIFT);

	set_pte(pg_table,pte);


#if PTRS_PER_PMD == 1
	pmd_base = (pmd_t *)pg_table_base;
#else
	pmd_base = (pmd_t *) alloc_bootmem_pages(PAGE_SIZE);
	pmd = pmd_base +
		((hpux_gateway_page_addr & (PMD_MASK) & (PGDIR_SIZE - 1)) >>
								PMD_SHIFT);
	pmd_val(*pmd) = _PAGE_TABLE | __pa(pg_table_base);
#endif

	gateway_pgd_entry = _PAGE_TABLE | __pa(pmd_base);

	/*
	 * We will be aliasing the HP-UX gateway page into all HP-UX
	 * user spaces at the same address (not counting the space register
	 * value) that will be equivalently mapped as long as space register
	 * hashing is disabled. It will be a problem if anyone touches
	 * the gateway pages at its "kernel" address, since that is
	 * NOT equivalently mapped. We'll flush the caches at this
	 * point, just in case some code has touched those addresses
	 * previous to this, but all bets are off if they get touched
	 * after this point.
	 */

	flush_all_caches();

	return;
}

void __init paging_init(void)
{
	pagetable_init();
	gateway_init();

	{
		unsigned long zones_size[MAX_NR_ZONES] = { max_pfn/2, max_pfn/2, };

		free_area_init(zones_size);
	}
}

#define NR_SPACE_IDS	8192

static unsigned long space_id[NR_SPACE_IDS / (8 * sizeof(long))];
static unsigned long space_id_index;
static unsigned long free_space_ids = NR_SPACE_IDS;

/*
 * XXX: We should probably unfold the set_bit / test_bit / clear_bit
 * locking out of these two functions and have a single spinlock on the
 * space_id data structures.
 *
 * Don't bother. This is all going to be significantly changed in the
 * very near future.
 */

#define SPACEID_SHIFT (PAGE_SHIFT + (PT_NLEVELS)*(PAGE_SHIFT - PT_NLEVELS) - 32)

unsigned long alloc_sid(void)
{
	unsigned long index;

	if (free_space_ids == 0)
		BUG();

	free_space_ids--;

	do {
		index = find_next_zero_bit(space_id, NR_SPACE_IDS, space_id_index);
	} while(test_and_set_bit(index, space_id));

	space_id_index = index;

	return index << SPACEID_SHIFT;
}

void free_sid(unsigned long spaceid)
{
	unsigned long index = spaceid >> SPACEID_SHIFT;
	if (index < 0)
		BUG();

	clear_bit(index, space_id);

	if (space_id_index > index) {
		space_id_index = index;
	}
	free_space_ids++;
}

#ifdef CONFIG_BLK_DEV_INITRD
void free_initrd_mem(unsigned long start, unsigned long end)
{
#if 0
	for (; start < end; start += PAGE_SIZE) {
		ClearPageReserved(mem_map + MAP_NR(start));
		set_page_count(mem_map+MAP_NR(start), 1);
		free_page(start);
		totalram_pages++;
	}
	printk ("Freeing initrd memory: %ldk freed\n", (end - start) >> 10);
#endif
}
#endif

void si_meminfo(struct sysinfo *val)
{
	int i;

	i = max_mapnr;
	val->totalram = totalram_pages;
	val->sharedram = 0;
	val->freeram = nr_free_pages();
	val->bufferram = atomic_read(&buffermem_pages);
#if 0
	while (i-- > 0)  {
		if (PageReserved(mem_map+i))
			continue;
		val->totalram++;
		if (!atomic_read(&mem_map[i].count))
			continue;
		val->sharedram += atomic_read(&mem_map[i].count) - 1;
	}
	val->totalram <<= PAGE_SHIFT;
	val->sharedram <<= PAGE_SHIFT;
#endif
	val->totalhigh = 0;
	val->freehigh = 0;
	return;
}