summaryrefslogtreecommitdiffstats
path: root/src/mm/memory.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/mm/memory.c')
-rw-r--r--src/mm/memory.c431
1 files changed, 431 insertions, 0 deletions
diff --git a/src/mm/memory.c b/src/mm/memory.c
new file mode 100644
index 0000000..340a7fc
--- /dev/null
+++ b/src/mm/memory.c
@@ -0,0 +1,431 @@
1/*
2 * linux/mm/memory.c
3 *
4 * (C) 1991 Linus Torvalds
5 */
6
7/*
8 * demand-loading started 01.12.91 - seems it is high on the list of
9 * things wanted, and it should be easy to implement. - Linus
10 */
11
12/*
13 * Ok, demand-loading was easy, shared pages a little bit tricker. Shared
14 * pages started 02.12.91, seems to work. - Linus.
15 *
16 * Tested sharing by executing about 30 /bin/sh: under the old kernel it
17 * would have taken more than the 6M I have free, but it worked well as
18 * far as I could see.
19 *
20 * Also corrected some "invalidate()"s - I wasn't doing enough of them.
21 */
22
23#include <signal.h>
24
25#include <asm/system.h>
26
27#include <linux/sched.h>
28#include <linux/head.h>
29#include <linux/kernel.h>
30
31void do_exit(long code);
32
33static inline void oom(void)
34{
35 printk("out of memory\n\r");
36 do_exit(SIGSEGV);
37}
38
39#define invalidate() \
40__asm__("movl %%eax,%%cr3"::"a" (0))
41
42/* these are not to be changed without changing head.s etc */
43#define LOW_MEM 0x100000
44#define PAGING_MEMORY (15*1024*1024)
45#define PAGING_PAGES (PAGING_MEMORY>>12)
46#define MAP_NR(addr) (((addr)-LOW_MEM)>>12)
47#define USED 100
48
49#define CODE_SPACE(addr) ((((addr)+4095)&~4095) < \
50current->start_code + current->end_code)
51
52static long HIGH_MEMORY = 0;
53
54#define copy_page(from,to) \
55__asm__("cld ; rep ; movsl"::"S" (from),"D" (to),"c" (1024))
56
57static unsigned char mem_map [ PAGING_PAGES ] = {0,};
58
59/*
60 * Get physical address of first (actually last :-) free page, and mark it
61 * used. If no free pages left, return 0.
62 */
63unsigned long get_free_page(void)
64{
65register unsigned long __res asm("ax");
66
67__asm__("std ; repne ; scasb\n\t"
68 "jne 1f\n\t"
69 "movb $1,1(%%edi)\n\t"
70 "sall $12,%%ecx\n\t"
71 "addl %2,%%ecx\n\t"
72 "movl %%ecx,%%edx\n\t"
73 "movl $1024,%%ecx\n\t"
74 "leal 4092(%%edx),%%edi\n\t"
75 "rep ; stosl\n\t"
76 " movl %%edx,%%eax\n"
77 "1: cld"
78 :"=a" (__res)
79 :"0" (0),"i" (LOW_MEM),"c" (PAGING_PAGES),
80 "D" (mem_map+PAGING_PAGES-1)
81 );
82return __res;
83}
84
85/*
86 * Free a page of memory at physical address 'addr'. Used by
87 * 'free_page_tables()'
88 */
89void free_page(unsigned long addr)
90{
91 if (addr < LOW_MEM) return;
92 if (addr >= HIGH_MEMORY)
93 panic("trying to free nonexistent page");
94 addr -= LOW_MEM;
95 addr >>= 12;
96 if (mem_map[addr]--) return;
97 mem_map[addr]=0;
98 panic("trying to free free page");
99}
100
101/*
102 * This function frees a continuos block of page tables, as needed
103 * by 'exit()'. As does copy_page_tables(), this handles only 4Mb blocks.
104 */
105int free_page_tables(unsigned long from,unsigned long size)
106{
107 unsigned long *pg_table;
108 unsigned long * dir, nr;
109
110 if (from & 0x3fffff)
111 panic("free_page_tables called with wrong alignment");
112 if (!from)
113 panic("Trying to free up swapper memory space");
114 size = (size + 0x3fffff) >> 22;
115 dir = (unsigned long *) ((from>>20) & 0xffc); /* _pg_dir = 0 */
116 for ( ; size-->0 ; dir++) {
117 if (!(1 & *dir))
118 continue;
119 pg_table = (unsigned long *) (0xfffff000 & *dir);
120 for (nr=0 ; nr<1024 ; nr++) {
121 if (1 & *pg_table)
122 free_page(0xfffff000 & *pg_table);
123 *pg_table = 0;
124 pg_table++;
125 }
126 free_page(0xfffff000 & *dir);
127 *dir = 0;
128 }
129 invalidate();
130 return 0;
131}
132
133/*
134 * Well, here is one of the most complicated functions in mm. It
135 * copies a range of linerar addresses by copying only the pages.
136 * Let's hope this is bug-free, 'cause this one I don't want to debug :-)
137 *
138 * Note! We don't copy just any chunks of memory - addresses have to
139 * be divisible by 4Mb (one page-directory entry), as this makes the
140 * function easier. It's used only by fork anyway.
141 *
142 * NOTE 2!! When from==0 we are copying kernel space for the first
143 * fork(). Then we DONT want to copy a full page-directory entry, as
144 * that would lead to some serious memory waste - we just copy the
145 * first 160 pages - 640kB. Even that is more than we need, but it
146 * doesn't take any more memory - we don't copy-on-write in the low
147 * 1 Mb-range, so the pages can be shared with the kernel. Thus the
148 * special case for nr=xxxx.
149 */
150int copy_page_tables(unsigned long from,unsigned long to,long size)
151{
152 unsigned long * from_page_table;
153 unsigned long * to_page_table;
154 unsigned long this_page;
155 unsigned long * from_dir, * to_dir;
156 unsigned long nr;
157
158 if ((from&0x3fffff) || (to&0x3fffff))
159 panic("copy_page_tables called with wrong alignment");
160 from_dir = (unsigned long *) ((from>>20) & 0xffc); /* _pg_dir = 0 */
161 to_dir = (unsigned long *) ((to>>20) & 0xffc);
162 size = ((unsigned) (size+0x3fffff)) >> 22;
163 for( ; size-->0 ; from_dir++,to_dir++) {
164 if (1 & *to_dir)
165 panic("copy_page_tables: already exist");
166 if (!(1 & *from_dir))
167 continue;
168 from_page_table = (unsigned long *) (0xfffff000 & *from_dir);
169 if (!(to_page_table = (unsigned long *) get_free_page()))
170 return -1; /* Out of memory, see freeing */
171 *to_dir = ((unsigned long) to_page_table) | 7;
172 nr = (from==0)?0xA0:1024;
173 for ( ; nr-- > 0 ; from_page_table++,to_page_table++) {
174 this_page = *from_page_table;
175 if (!(1 & this_page))
176 continue;
177 this_page &= ~2;
178 *to_page_table = this_page;
179 if (this_page > LOW_MEM) {
180 *from_page_table = this_page;
181 this_page -= LOW_MEM;
182 this_page >>= 12;
183 mem_map[this_page]++;
184 }
185 }
186 }
187 invalidate();
188 return 0;
189}
190
191/*
192 * This function puts a page in memory at the wanted address.
193 * It returns the physical address of the page gotten, 0 if
194 * out of memory (either when trying to access page-table or
195 * page.)
196 */
197unsigned long put_page(unsigned long page,unsigned long address)
198{
199 unsigned long tmp, *page_table;
200
201/* NOTE !!! This uses the fact that _pg_dir=0 */
202
203 if (page < LOW_MEM || page >= HIGH_MEMORY)
204 printk("Trying to put page %p at %p\n",page,address);
205 if (mem_map[(page-LOW_MEM)>>12] != 1)
206 printk("mem_map disagrees with %p at %p\n",page,address);
207 page_table = (unsigned long *) ((address>>20) & 0xffc);
208 if ((*page_table)&1)
209 page_table = (unsigned long *) (0xfffff000 & *page_table);
210 else {
211 if (!(tmp=get_free_page()))
212 return 0;
213 *page_table = tmp|7;
214 page_table = (unsigned long *) tmp;
215 }
216 page_table[(address>>12) & 0x3ff] = page | 7;
217/* no need for invalidate */
218 return page;
219}
220
221void un_wp_page(unsigned long * table_entry)
222{
223 unsigned long old_page,new_page;
224
225 old_page = 0xfffff000 & *table_entry;
226 if (old_page >= LOW_MEM && mem_map[MAP_NR(old_page)]==1) {
227 *table_entry |= 2;
228 invalidate();
229 return;
230 }
231 if (!(new_page=get_free_page()))
232 oom();
233 if (old_page >= LOW_MEM)
234 mem_map[MAP_NR(old_page)]--;
235 *table_entry = new_page | 7;
236 invalidate();
237 copy_page(old_page,new_page);
238}
239
240/*
241 * This routine handles present pages, when users try to write
242 * to a shared page. It is done by copying the page to a new address
243 * and decrementing the shared-page counter for the old page.
244 *
245 * If it's in code space we exit with a segment error.
246 */
247void do_wp_page(unsigned long error_code,unsigned long address)
248{
249#if 0
250/* we cannot do this yet: the estdio library writes to code space */
251/* stupid, stupid. I really want the libc.a from GNU */
252 if (CODE_SPACE(address))
253 do_exit(SIGSEGV);
254#endif
255 un_wp_page((unsigned long *)
256 (((address>>10) & 0xffc) + (0xfffff000 &
257 *((unsigned long *) ((address>>20) &0xffc)))));
258
259}
260
261void write_verify(unsigned long address)
262{
263 unsigned long page;
264
265 if (!( (page = *((unsigned long *) ((address>>20) & 0xffc)) )&1))
266 return;
267 page &= 0xfffff000;
268 page += ((address>>10) & 0xffc);
269 if ((3 & *(unsigned long *) page) == 1) /* non-writeable, present */
270 un_wp_page((unsigned long *) page);
271 return;
272}
273
274void get_empty_page(unsigned long address)
275{
276 unsigned long tmp;
277
278 if (!(tmp=get_free_page()) || !put_page(tmp,address)) {
279 free_page(tmp); /* 0 is ok - ignored */
280 oom();
281 }
282}
283
284/*
285 * try_to_share() checks the page at address "address" in the task "p",
286 * to see if it exists, and if it is clean. If so, share it with the current
287 * task.
288 *
289 * NOTE! This assumes we have checked that p != current, and that they
290 * share the same executable.
291 */
292static int try_to_share(unsigned long address, struct task_struct * p)
293{
294 unsigned long from;
295 unsigned long to;
296 unsigned long from_page;
297 unsigned long to_page;
298 unsigned long phys_addr;
299
300 from_page = to_page = ((address>>20) & 0xffc);
301 from_page += ((p->start_code>>20) & 0xffc);
302 to_page += ((current->start_code>>20) & 0xffc);
303/* is there a page-directory at from? */
304 from = *(unsigned long *) from_page;
305 if (!(from & 1))
306 return 0;
307 from &= 0xfffff000;
308 from_page = from + ((address>>10) & 0xffc);
309 phys_addr = *(unsigned long *) from_page;
310/* is the page clean and present? */
311 if ((phys_addr & 0x41) != 0x01)
312 return 0;
313 phys_addr &= 0xfffff000;
314 if (phys_addr >= HIGH_MEMORY || phys_addr < LOW_MEM)
315 return 0;
316 to = *(unsigned long *) to_page;
317 if (!(to & 1)) {
318 if ((to = get_free_page()))
319 *(unsigned long *) to_page = to | 7;
320 else
321 oom();
322 }
323 to &= 0xfffff000;
324 to_page = to + ((address>>10) & 0xffc);
325 if (1 & *(unsigned long *) to_page)
326 panic("try_to_share: to_page already exists");
327/* share them: write-protect */
328 *(unsigned long *) from_page &= ~2;
329 *(unsigned long *) to_page = *(unsigned long *) from_page;
330 invalidate();
331 phys_addr -= LOW_MEM;
332 phys_addr >>= 12;
333 mem_map[phys_addr]++;
334 return 1;
335}
336
337/*
338 * share_page() tries to find a process that could share a page with
339 * the current one. Address is the address of the wanted page relative
340 * to the current data space.
341 *
342 * We first check if it is at all feasible by checking executable->i_count.
343 * It should be >1 if there are other tasks sharing this inode.
344 */
345static int share_page(unsigned long address)
346{
347 struct task_struct ** p;
348
349 if (!current->executable)
350 return 0;
351 if (current->executable->i_count < 2)
352 return 0;
353 for (p = &LAST_TASK ; p > &FIRST_TASK ; --p) {
354 if (!*p)
355 continue;
356 if (current == *p)
357 continue;
358 if ((*p)->executable != current->executable)
359 continue;
360 if (try_to_share(address,*p))
361 return 1;
362 }
363 return 0;
364}
365
366void do_no_page(unsigned long error_code,unsigned long address)
367{
368 int nr[4];
369 unsigned long tmp;
370 unsigned long page;
371 int block,i;
372
373 address &= 0xfffff000;
374 tmp = address - current->start_code;
375 if (!current->executable || tmp >= current->end_data) {
376 get_empty_page(address);
377 return;
378 }
379 if (share_page(tmp))
380 return;
381 if (!(page = get_free_page()))
382 oom();
383/* remember that 1 block is used for header */
384 block = 1 + tmp/BLOCK_SIZE;
385 for (i=0 ; i<4 ; block++,i++)
386 nr[i] = bmap(current->executable,block);
387 bread_page(page,current->executable->i_dev,nr);
388 i = tmp + 4096 - current->end_data;
389 tmp = page + 4096;
390 while (i-- > 0) {
391 tmp--;
392 *(char *)tmp = 0;
393 }
394 if (put_page(page,address))
395 return;
396 free_page(page);
397 oom();
398}
399
400void mem_init(long start_mem, long end_mem)
401{
402 int i;
403
404 HIGH_MEMORY = end_mem;
405 for (i=0 ; i<PAGING_PAGES ; i++)
406 mem_map[i] = USED;
407 i = MAP_NR(start_mem);
408 end_mem -= start_mem;
409 end_mem >>= 12;
410 while (end_mem-->0)
411 mem_map[i++]=0;
412}
413
414void calc_mem(void)
415{
416 int i,j,k,free=0;
417 long * pg_tbl;
418
419 for(i=0 ; i<PAGING_PAGES ; i++)
420 if (!mem_map[i]) free++;
421 printk("%d pages free (of %d)\n\r",free,PAGING_PAGES);
422 for(i=2 ; i<1024 ; i++) {
423 if (1&pg_dir[i]) {
424 pg_tbl=(long *) (0xfffff000 & pg_dir[i]);
425 for(j=k=0 ; j<1024 ; j++)
426 if (pg_tbl[j]&1)
427 k++;
428 printk("Pg-dir[%d] uses %d pages\n",i,k);
429 }
430 }
431}