blob: d948babfc67a70f060e9532e2dd0eb950648993f [file] [log] [blame]
Jeff Dike114069f2005-09-16 19:27:51 -07001/*
Jeff Dike009ec2a92008-02-04 22:30:53 -08002 * Copyright (C) 2000 - 2007 Jeff Dike (jdike@{addtoit,linux.intel}.com)
Linus Torvalds1da177e2005-04-16 15:20:36 -07003 * Licensed under the GPL
4 */
5
Jeff Dike009ec2a92008-02-04 22:30:53 -08006#include <linux/stddef.h>
7#include <linux/bootmem.h>
8#include <linux/gfp.h>
9#include <linux/highmem.h>
10#include <linux/mm.h>
11#include <linux/swap.h>
12#include <asm/fixmap.h>
13#include <asm/page.h>
Jeff Dike4ff83ce2007-05-06 14:51:08 -070014#include "as-layout.h"
Jeff Dike114069f2005-09-16 19:27:51 -070015#include "init.h"
Jeff Dike009ec2a92008-02-04 22:30:53 -080016#include "kern.h"
17#include "kern_util.h"
18#include "mem_user.h"
19#include "os.h"
Linus Torvalds1da177e2005-04-16 15:20:36 -070020
Jeff Dike6bf79482007-02-10 01:44:18 -080021/* allocated in paging_init, zeroed in mem_init, and unchanged thereafter */
Linus Torvalds1da177e2005-04-16 15:20:36 -070022unsigned long *empty_zero_page = NULL;
Jeff Dike6bf79482007-02-10 01:44:18 -080023/* allocated in paging_init and unchanged thereafter */
Linus Torvalds1da177e2005-04-16 15:20:36 -070024unsigned long *empty_bad_page = NULL;
Jeff Dike80e39312008-02-04 22:31:17 -080025
26/*
27 * Initialized during boot, and readonly for initializing page tables
28 * afterwards
29 */
Linus Torvalds1da177e2005-04-16 15:20:36 -070030pgd_t swapper_pg_dir[PTRS_PER_PGD];
Jeff Dike80e39312008-02-04 22:31:17 -080031
32/* Initialized at boot time, and readonly after that */
Jeff Dike9902abd2006-03-31 02:30:09 -080033unsigned long long highmem;
Linus Torvalds1da177e2005-04-16 15:20:36 -070034int kmalloc_ok = 0;
35
Jeff Dike80e39312008-02-04 22:31:17 -080036/* Used during early boot */
Linus Torvalds1da177e2005-04-16 15:20:36 -070037static unsigned long brk_end;
38
Linus Torvalds1da177e2005-04-16 15:20:36 -070039static void map_cb(void *unused)
40{
41 map_memory(brk_end, __pa(brk_end), uml_reserved - brk_end, 1, 1, 0);
42}
43
44#ifdef CONFIG_HIGHMEM
45static void setup_highmem(unsigned long highmem_start,
46 unsigned long highmem_len)
47{
48 struct page *page;
49 unsigned long highmem_pfn;
50 int i;
51
52 highmem_pfn = __pa(highmem_start) >> PAGE_SHIFT;
Jeff Dike009ec2a92008-02-04 22:30:53 -080053 for (i = 0; i < highmem_len >> PAGE_SHIFT; i++) {
Linus Torvalds1da177e2005-04-16 15:20:36 -070054 page = &mem_map[highmem_pfn + i];
55 ClearPageReserved(page);
Nick Piggin7835e982006-03-22 00:08:40 -080056 init_page_count(page);
Linus Torvalds1da177e2005-04-16 15:20:36 -070057 __free_page(page);
58 }
59}
60#endif
61
Jeff Dike97a1fcb2007-07-23 18:43:48 -070062void __init mem_init(void)
Linus Torvalds1da177e2005-04-16 15:20:36 -070063{
Jeff Dike60678bb2007-02-10 01:44:10 -080064 /* clear the zero-page */
WANG Congc0a92902008-02-04 22:30:41 -080065 memset(empty_zero_page, 0, PAGE_SIZE);
Linus Torvalds1da177e2005-04-16 15:20:36 -070066
67 /* Map in the area just after the brk now that kmalloc is about
68 * to be turned on.
69 */
70 brk_end = (unsigned long) UML_ROUND_UP(sbrk(0));
71 map_cb(NULL);
72 initial_thread_cb(map_cb, NULL);
73 free_bootmem(__pa(brk_end), uml_reserved - brk_end);
74 uml_reserved = brk_end;
75
Linus Torvalds1da177e2005-04-16 15:20:36 -070076 /* this will put all low memory onto the freelists */
77 totalram_pages = free_all_bootmem();
Jason Lunzaf84eab2007-03-29 01:20:31 -070078 max_low_pfn = totalram_pages;
Christoph Lameterc1f60a52006-09-25 23:31:11 -070079#ifdef CONFIG_HIGHMEM
Linus Torvalds1da177e2005-04-16 15:20:36 -070080 totalhigh_pages = highmem >> PAGE_SHIFT;
81 totalram_pages += totalhigh_pages;
Christoph Lameterc1f60a52006-09-25 23:31:11 -070082#endif
Linus Torvalds1da177e2005-04-16 15:20:36 -070083 num_physpages = totalram_pages;
84 max_pfn = totalram_pages;
Jeff Dike009ec2a92008-02-04 22:30:53 -080085 printk(KERN_INFO "Memory: %luk available\n",
Linus Torvalds1da177e2005-04-16 15:20:36 -070086 (unsigned long) nr_free_pages() << (PAGE_SHIFT-10));
87 kmalloc_ok = 1;
88
89#ifdef CONFIG_HIGHMEM
90 setup_highmem(end_iomem, highmem);
91#endif
92}
93
Jeff Dike12f49642005-05-20 13:59:12 -070094/*
95 * Create a page table and place a pointer to it in a middle page
96 * directory entry.
97 */
98static void __init one_page_table_init(pmd_t *pmd)
99{
100 if (pmd_none(*pmd)) {
101 pte_t *pte = (pte_t *) alloc_bootmem_low_pages(PAGE_SIZE);
102 set_pmd(pmd, __pmd(_KERNPG_TABLE +
103 (unsigned long) __pa(pte)));
104 if (pte != pte_offset_kernel(pmd, 0))
105 BUG();
106 }
107}
108
109static void __init one_md_table_init(pud_t *pud)
110{
111#ifdef CONFIG_3_LEVEL_PGTABLES
112 pmd_t *pmd_table = (pmd_t *) alloc_bootmem_low_pages(PAGE_SIZE);
113 set_pud(pud, __pud(_KERNPG_TABLE + (unsigned long) __pa(pmd_table)));
114 if (pmd_table != pmd_offset(pud, 0))
115 BUG();
116#endif
117}
118
Jeff Dike009ec2a92008-02-04 22:30:53 -0800119static void __init fixrange_init(unsigned long start, unsigned long end,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700120 pgd_t *pgd_base)
121{
122 pgd_t *pgd;
Jeff Dike12f49642005-05-20 13:59:12 -0700123 pud_t *pud;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700124 pmd_t *pmd;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700125 int i, j;
126 unsigned long vaddr;
127
128 vaddr = start;
129 i = pgd_index(vaddr);
130 j = pmd_index(vaddr);
131 pgd = pgd_base + i;
132
133 for ( ; (i < PTRS_PER_PGD) && (vaddr < end); pgd++, i++) {
Jeff Dike12f49642005-05-20 13:59:12 -0700134 pud = pud_offset(pgd, vaddr);
135 if (pud_none(*pud))
136 one_md_table_init(pud);
137 pmd = pmd_offset(pud, vaddr);
Jeff Dike655e4ed2008-02-04 22:30:55 -0800138 for (; (j < PTRS_PER_PMD) && (vaddr < end); pmd++, j++) {
Jeff Dike12f49642005-05-20 13:59:12 -0700139 one_page_table_init(pmd);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700140 vaddr += PMD_SIZE;
141 }
142 j = 0;
143 }
144}
145
146#ifdef CONFIG_HIGHMEM
147pte_t *kmap_pte;
148pgprot_t kmap_prot;
149
150#define kmap_get_fixmap_pte(vaddr) \
151 pte_offset_kernel(pmd_offset(pud_offset(pgd_offset_k(vaddr), (vaddr)),\
Jeff Dike8192ab42008-02-04 22:30:53 -0800152 (vaddr)), (vaddr))
Linus Torvalds1da177e2005-04-16 15:20:36 -0700153
154static void __init kmap_init(void)
155{
156 unsigned long kmap_vstart;
157
158 /* cache the first kmap pte */
159 kmap_vstart = __fix_to_virt(FIX_KMAP_BEGIN);
160 kmap_pte = kmap_get_fixmap_pte(kmap_vstart);
161
162 kmap_prot = PAGE_KERNEL;
163}
164
Al Virob4a08a12007-10-29 04:36:10 +0000165static void __init init_highmem(void)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700166{
167 pgd_t *pgd;
168 pud_t *pud;
169 pmd_t *pmd;
170 pte_t *pte;
171 unsigned long vaddr;
172
173 /*
174 * Permanent kmaps:
175 */
176 vaddr = PKMAP_BASE;
177 fixrange_init(vaddr, vaddr + PAGE_SIZE*LAST_PKMAP, swapper_pg_dir);
178
179 pgd = swapper_pg_dir + pgd_index(vaddr);
180 pud = pud_offset(pgd, vaddr);
181 pmd = pmd_offset(pud, vaddr);
182 pte = pte_offset_kernel(pmd, vaddr);
183 pkmap_page_table = pte;
184
185 kmap_init();
186}
187#endif /* CONFIG_HIGHMEM */
188
189static void __init fixaddr_user_init( void)
190{
viro@ZenIV.linux.org.uk9a0b3862005-09-07 23:21:11 +0100191#ifdef CONFIG_ARCH_REUSE_HOST_VSYSCALL_AREA
Linus Torvalds1da177e2005-04-16 15:20:36 -0700192 long size = FIXADDR_USER_END - FIXADDR_USER_START;
193 pgd_t *pgd;
194 pud_t *pud;
195 pmd_t *pmd;
196 pte_t *pte;
Jeff Dike655e4ed2008-02-04 22:30:55 -0800197 phys_t p;
198 unsigned long v, vaddr = FIXADDR_USER_START;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700199
Jeff Dike655e4ed2008-02-04 22:30:55 -0800200 if (!size)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700201 return;
202
203 fixrange_init( FIXADDR_USER_START, FIXADDR_USER_END, swapper_pg_dir);
Jeff Dike655e4ed2008-02-04 22:30:55 -0800204 v = (unsigned long) alloc_bootmem_low_pages(size);
205 memcpy((void *) v , (void *) FIXADDR_USER_START, size);
206 p = __pa(v);
Jeff Dike009ec2a92008-02-04 22:30:53 -0800207 for ( ; size > 0; size -= PAGE_SIZE, vaddr += PAGE_SIZE,
Jeff Dike655e4ed2008-02-04 22:30:55 -0800208 p += PAGE_SIZE) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700209 pgd = swapper_pg_dir + pgd_index(vaddr);
210 pud = pud_offset(pgd, vaddr);
211 pmd = pmd_offset(pud, vaddr);
212 pte = pte_offset_kernel(pmd, vaddr);
Jeff Dike655e4ed2008-02-04 22:30:55 -0800213 pte_set_val(*pte, p, PAGE_READONLY);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700214 }
215#endif
216}
217
Jeff Dike36e45462007-05-06 14:51:11 -0700218void __init paging_init(void)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700219{
220 unsigned long zones_size[MAX_NR_ZONES], vaddr;
221 int i;
222
223 empty_zero_page = (unsigned long *) alloc_bootmem_low_pages(PAGE_SIZE);
224 empty_bad_page = (unsigned long *) alloc_bootmem_low_pages(PAGE_SIZE);
Jeff Dike009ec2a92008-02-04 22:30:53 -0800225 for (i = 0; i < ARRAY_SIZE(zones_size); i++)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700226 zones_size[i] = 0;
Jeff Dike91b165c2006-09-25 23:33:00 -0700227
Jeff Dike07155012006-09-27 01:50:34 -0700228 zones_size[ZONE_NORMAL] = (end_iomem >> PAGE_SHIFT) -
229 (uml_physmem >> PAGE_SHIFT);
Christoph Lametere53ef382006-09-25 23:31:14 -0700230#ifdef CONFIG_HIGHMEM
Paolo 'Blaisorblade' Giarrusso353f8d12005-11-07 00:58:58 -0800231 zones_size[ZONE_HIGHMEM] = highmem >> PAGE_SHIFT;
Christoph Lametere53ef382006-09-25 23:31:14 -0700232#endif
Linus Torvalds1da177e2005-04-16 15:20:36 -0700233 free_area_init(zones_size);
234
235 /*
236 * Fixed mappings, only the page table structure has to be
237 * created - mappings will be set by set_fixmap():
238 */
239 vaddr = __fix_to_virt(__end_of_fixed_addresses - 1) & PMD_MASK;
240 fixrange_init(vaddr, FIXADDR_TOP, swapper_pg_dir);
241
242 fixaddr_user_init();
243
244#ifdef CONFIG_HIGHMEM
245 init_highmem();
246#endif
247}
248
Al Viro53f9fc92005-10-21 03:22:24 -0400249struct page *arch_validate(struct page *page, gfp_t mask, int order)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700250{
251 unsigned long addr, zero = 0;
252 int i;
253
254 again:
Jeff Dike009ec2a92008-02-04 22:30:53 -0800255 if (page == NULL)
Jeff Dike60678bb2007-02-10 01:44:10 -0800256 return page;
Jeff Dike009ec2a92008-02-04 22:30:53 -0800257 if (PageHighMem(page))
Jeff Dike60678bb2007-02-10 01:44:10 -0800258 return page;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700259
260 addr = (unsigned long) page_address(page);
Jeff Dike009ec2a92008-02-04 22:30:53 -0800261 for (i = 0; i < (1 << order); i++) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700262 current->thread.fault_addr = (void *) addr;
Jeff Dike009ec2a92008-02-04 22:30:53 -0800263 if (__do_copy_to_user((void __user *) addr, &zero,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700264 sizeof(zero),
265 &current->thread.fault_addr,
Jeff Dike009ec2a92008-02-04 22:30:53 -0800266 &current->thread.fault_catcher)) {
267 if (!(mask & __GFP_WAIT))
Jeff Dike60678bb2007-02-10 01:44:10 -0800268 return NULL;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700269 else break;
270 }
271 addr += PAGE_SIZE;
272 }
273
Jeff Dike009ec2a92008-02-04 22:30:53 -0800274 if (i == (1 << order))
Jeff Dike60678bb2007-02-10 01:44:10 -0800275 return page;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700276 page = alloc_pages(mask, order);
277 goto again;
278}
279
Jeff Dike8192ab42008-02-04 22:30:53 -0800280/*
281 * This can't do anything because nothing in the kernel image can be freed
Linus Torvalds1da177e2005-04-16 15:20:36 -0700282 * since it's not in kernel physical memory.
283 */
284
285void free_initmem(void)
286{
287}
288
289#ifdef CONFIG_BLK_DEV_INITRD
Linus Torvalds1da177e2005-04-16 15:20:36 -0700290void free_initrd_mem(unsigned long start, unsigned long end)
291{
292 if (start < end)
Jeff Dike009ec2a92008-02-04 22:30:53 -0800293 printk(KERN_INFO "Freeing initrd memory: %ldk freed\n",
294 (end - start) >> 10);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700295 for (; start < end; start += PAGE_SIZE) {
296 ClearPageReserved(virt_to_page(start));
Nick Piggin7835e982006-03-22 00:08:40 -0800297 init_page_count(virt_to_page(start));
Linus Torvalds1da177e2005-04-16 15:20:36 -0700298 free_page(start);
299 totalram_pages++;
300 }
301}
Linus Torvalds1da177e2005-04-16 15:20:36 -0700302#endif
303
304void show_mem(void)
305{
Jeff Dike60678bb2007-02-10 01:44:10 -0800306 int pfn, total = 0, reserved = 0;
307 int shared = 0, cached = 0;
308 int highmem = 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700309 struct page *page;
310
Jeff Dike009ec2a92008-02-04 22:30:53 -0800311 printk(KERN_INFO "Mem-info:\n");
Jeff Dike60678bb2007-02-10 01:44:10 -0800312 show_free_areas();
Jeff Dike009ec2a92008-02-04 22:30:53 -0800313 printk(KERN_INFO "Free swap: %6ldkB\n",
314 nr_swap_pages<<(PAGE_SHIFT-10));
Jeff Dike60678bb2007-02-10 01:44:10 -0800315 pfn = max_mapnr;
Jeff Dike009ec2a92008-02-04 22:30:53 -0800316 while (pfn-- > 0) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700317 page = pfn_to_page(pfn);
Jeff Dike60678bb2007-02-10 01:44:10 -0800318 total++;
Jeff Dike009ec2a92008-02-04 22:30:53 -0800319 if (PageHighMem(page))
Jeff Dike60678bb2007-02-10 01:44:10 -0800320 highmem++;
Jeff Dike009ec2a92008-02-04 22:30:53 -0800321 if (PageReserved(page))
Jeff Dike60678bb2007-02-10 01:44:10 -0800322 reserved++;
Jeff Dike009ec2a92008-02-04 22:30:53 -0800323 else if (PageSwapCache(page))
Jeff Dike60678bb2007-02-10 01:44:10 -0800324 cached++;
Jeff Dike009ec2a92008-02-04 22:30:53 -0800325 else if (page_count(page))
Jeff Dike60678bb2007-02-10 01:44:10 -0800326 shared += page_count(page) - 1;
327 }
Jeff Dike009ec2a92008-02-04 22:30:53 -0800328 printk(KERN_INFO "%d pages of RAM\n", total);
329 printk(KERN_INFO "%d pages of HIGHMEM\n", highmem);
330 printk(KERN_INFO "%d reserved pages\n", reserved);
331 printk(KERN_INFO "%d pages shared\n", shared);
332 printk(KERN_INFO "%d pages swap cached\n", cached);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700333}
334
Jeff Dike8192ab42008-02-04 22:30:53 -0800335/* Allocate and free page tables. */
Linus Torvalds1da177e2005-04-16 15:20:36 -0700336
337pgd_t *pgd_alloc(struct mm_struct *mm)
338{
339 pgd_t *pgd = (pgd_t *)__get_free_page(GFP_KERNEL);
340
341 if (pgd) {
342 memset(pgd, 0, USER_PTRS_PER_PGD * sizeof(pgd_t));
Jeff Dike009ec2a92008-02-04 22:30:53 -0800343 memcpy(pgd + USER_PTRS_PER_PGD,
344 swapper_pg_dir + USER_PTRS_PER_PGD,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700345 (PTRS_PER_PGD - USER_PTRS_PER_PGD) * sizeof(pgd_t));
346 }
347 return pgd;
348}
349
Benjamin Herrenschmidt5e541972008-02-04 22:29:14 -0800350void pgd_free(struct mm_struct *mm, pgd_t *pgd)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700351{
352 free_page((unsigned long) pgd);
353}
354
355pte_t *pte_alloc_one_kernel(struct mm_struct *mm, unsigned long address)
356{
357 pte_t *pte;
358
359 pte = (pte_t *)__get_free_page(GFP_KERNEL|__GFP_REPEAT|__GFP_ZERO);
360 return pte;
361}
362
363struct page *pte_alloc_one(struct mm_struct *mm, unsigned long address)
364{
365 struct page *pte;
Jeff Dike60678bb2007-02-10 01:44:10 -0800366
Linus Torvalds1da177e2005-04-16 15:20:36 -0700367 pte = alloc_page(GFP_KERNEL|__GFP_REPEAT|__GFP_ZERO);
368 return pte;
369}
Jeff Dike8192ab42008-02-04 22:30:53 -0800370
371#ifdef CONFIG_3_LEVEL_PGTABLES
372pmd_t *pmd_alloc_one(struct mm_struct *mm, unsigned long address)
373{
374 pmd_t *pmd = (pmd_t *) __get_free_page(GFP_KERNEL);
375
376 if (pmd)
377 memset(pmd, 0, PAGE_SIZE);
378
379 return pmd;
380}
381#endif