This source file includes following definitions.
- free_page
- free_page_tables
- copy_page_tables
- unmap_page_range
- remap_page_range
- put_page
- put_dirty_page
- un_wp_page
- do_wp_page
- write_verify
- get_empty_page
- try_to_share
- share_page
- get_empty
- do_no_page
- mem_init
- show_mem
- do_page_fault
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 #include <signal.h>
32
33 #include <asm/system.h>
34
35 #include <linux/sched.h>
36 #include <linux/head.h>
37 #include <linux/kernel.h>
38
39 #define CODE_SPACE(addr) ((((addr)+4095)&~4095) < \
40 current->start_code + current->end_code)
41
42 unsigned long HIGH_MEMORY = 0;
43
44 #define copy_page(from,to) \
45 __asm__("cld ; rep ; movsl"::"S" (from),"D" (to),"c" (1024):"cx","di","si")
46
47 #define CHECK_LAST_NR 16
48
49 static unsigned long last_pages[CHECK_LAST_NR] = { 0, };
50
51 unsigned char mem_map [ PAGING_PAGES ] = {0,};
52
53
54
55
56
57 void free_page(unsigned long addr)
58 {
59 if (addr < LOW_MEM) return;
60 if (addr < HIGH_MEMORY) {
61 addr -= LOW_MEM;
62 addr >>= 12;
63 if (mem_map[addr]--)
64 return;
65 mem_map[addr]=0;
66 }
67 printk("trying to free free page: memory probably corrupted");
68 }
69
70
71
72
73
74 int free_page_tables(unsigned long from,unsigned long size)
75 {
76 unsigned long page;
77 unsigned long page_dir;
78 unsigned long *pg_table;
79 unsigned long * dir, nr;
80
81 if (from & 0x3fffff)
82 panic("free_page_tables called with wrong alignment");
83 if (!from)
84 panic("Trying to free up swapper memory space");
85 size = (size + 0x3fffff) >> 22;
86 dir = (unsigned long *) ((from>>20) & 0xffc);
87 for ( ; size-->0 ; dir++) {
88 if (!(page_dir = *dir))
89 continue;
90 *dir = 0;
91 if (!(page_dir & 1)) {
92 printk("free_page_tables: bad page directory.");
93 continue;
94 }
95 pg_table = (unsigned long *) (0xfffff000 & page_dir);
96 for (nr=0 ; nr<1024 ; nr++,pg_table++) {
97 if (!(page = *pg_table))
98 continue;
99 *pg_table = 0;
100 if (1 & page)
101 free_page(0xfffff000 & page);
102 else
103 swap_free(page >> 1);
104 }
105 free_page(0xfffff000 & page_dir);
106 }
107 invalidate();
108 for (page = 0; page < CHECK_LAST_NR ; page++)
109 last_pages[page] = 0;
110 return 0;
111 }
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130 int copy_page_tables(unsigned long from,unsigned long to,long size)
131 {
132 unsigned long * from_page_table;
133 unsigned long * to_page_table;
134 unsigned long this_page;
135 unsigned long * from_dir, * to_dir;
136 unsigned long new_page;
137 unsigned long nr;
138
139 if ((from&0x3fffff) || (to&0x3fffff))
140 panic("copy_page_tables called with wrong alignment");
141 from_dir = (unsigned long *) ((from>>20) & 0xffc);
142 to_dir = (unsigned long *) ((to>>20) & 0xffc);
143 size = ((unsigned) (size+0x3fffff)) >> 22;
144 for( ; size-->0 ; from_dir++,to_dir++) {
145 if (*to_dir)
146 printk("copy_page_tables: already exist, "
147 "probable memory corruption\n");
148 if (!*from_dir)
149 continue;
150 if (!(1 & *from_dir)) {
151 printk("copy_page_tables: page table swapped out, "
152 "probable memory corruption");
153 *from_dir = 0;
154 continue;
155 }
156 from_page_table = (unsigned long *) (0xfffff000 & *from_dir);
157 if (!(to_page_table = (unsigned long *) get_free_page()))
158 return -1;
159 *to_dir = ((unsigned long) to_page_table) | 7;
160 nr = (from==0)?0xA0:1024;
161 for ( ; nr-- > 0 ; from_page_table++,to_page_table++) {
162 this_page = *from_page_table;
163 if (!this_page)
164 continue;
165 if (!(1 & this_page)) {
166 if (!(new_page = get_free_page()))
167 return -1;
168 ++current->rss;
169 read_swap_page(this_page>>1, (char *) new_page);
170 *to_page_table = this_page;
171 *from_page_table = new_page | (PAGE_DIRTY | 7);
172 continue;
173 }
174 this_page &= ~2;
175 *to_page_table = this_page;
176 if (this_page > LOW_MEM) {
177 *from_page_table = this_page;
178 this_page -= LOW_MEM;
179 this_page >>= 12;
180 mem_map[this_page]++;
181 }
182 }
183 }
184 invalidate();
185 return 0;
186 }
187
188
189
190
191
192 int
193 unmap_page_range(unsigned long from, unsigned long size)
194 {
195 unsigned long page, page_dir;
196 unsigned long *page_table, *dir;
197 unsigned long poff, pcnt, pc;
198
199 if (from & 0xfff)
200 panic("unmap_page_range called with wrong alignment");
201 if (!from)
202 panic("unmap_page_range trying to free swapper memory space");
203 size = (size + 0xfff) >> 12;
204 dir = (unsigned long *) ((from >> 20) & 0xffc);
205 poff = (from >> 12) & 0x3ff;
206 if ((pcnt = 1024 - poff) > size)
207 pcnt = size;
208
209 for ( ; size > 0; ++dir, size -= pcnt,
210 pcnt = (size > 1024 ? 1024 : size)) {
211 if (!(page_dir = *dir)) {
212 poff = 0;
213 continue;
214 }
215 if (!(page_dir & 1)) {
216 printk("unmap_page_range: bad page directory.");
217 continue;
218 }
219 page_table = (unsigned long *)(0xfffff000 & page_dir);
220 if (poff) {
221 page_table += poff;
222 poff = 0;
223 }
224 for (pc = pcnt; pc--; page_table++) {
225 if (page = *page_table) {
226 --current->rss;
227 *page_table = 0;
228 if (1 & page)
229 free_page(0xfffff000 & page);
230 else
231 swap_free(page >> 1);
232 }
233 }
234 if (pcnt == 1024) {
235 free_page(0xfffff000 & page_dir);
236 *dir = 0;
237 }
238 }
239 invalidate();
240 for (page = 0; page < CHECK_LAST_NR ; page++)
241 last_pages[page] = 0;
242 return 0;
243 }
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259 int
260 remap_page_range(unsigned long from, unsigned long to, unsigned long size,
261 int permiss)
262 {
263 unsigned long *page_table, *dir;
264 unsigned long poff, pcnt;
265
266 if ((from & 0xfff) || (to & 0xfff))
267 panic("remap_page_range called with wrong alignment");
268 dir = (unsigned long *) ((from >> 20) & 0xffc);
269 size = (size + 0xfff) >> 12;
270 poff = (from >> 12) & 0x3ff;
271 if ((pcnt = 1024 - poff) > size)
272 pcnt = size;
273
274 while (size > 0) {
275 if (!(1 & *dir)) {
276 if (!(page_table = (unsigned long *)get_free_page())) {
277 invalidate();
278 return -1;
279 }
280 *dir++ = ((unsigned long) page_table) | 7;
281 }
282 else
283 page_table = (unsigned long *)(0xfffff000 & *dir++);
284 if (poff) {
285 page_table += poff;
286 poff = 0;
287 }
288
289 for (size -= pcnt; pcnt-- ;) {
290 int mask;
291
292 mask = 4;
293 if (permiss & 1)
294 mask |= 1;
295 if (permiss & 2) {
296 if (permiss & 8)
297 mask |= 1;
298 else
299 mask |= 3;
300 }
301 if (permiss & 4)
302 mask |= 1;
303
304 if (*page_table) {
305 --current->rss;
306 if (1 & *page_table)
307 free_page(0xfffff000 & *page_table);
308 else
309 swap_free(*page_table >> 1);
310 }
311
312
313
314
315
316
317
318
319 if (mask == 4 || to >= HIGH_MEMORY)
320 *page_table++ = 0;
321 else {
322 ++current->rss;
323 *page_table++ = (to | mask);
324 if (to > LOW_MEM) {
325 unsigned long frame;
326 frame = to - LOW_MEM;
327 frame >>= 12;
328 mem_map[frame]++;
329 }
330 }
331 to += PAGE_SIZE;
332 }
333 pcnt = (size > 1024 ? 1024 : size);
334 }
335 invalidate();
336 for (to = 0; to < CHECK_LAST_NR ; to++)
337 last_pages[to] = 0;
338 return 0;
339 }
340
341
342
343
344
345
346
347 static unsigned long put_page(unsigned long page,unsigned long address)
348 {
349 unsigned long tmp, *page_table;
350
351
352
353 if (page < LOW_MEM || page >= HIGH_MEMORY) {
354 printk("put_page: trying to put page %p at %p\n",page,address);
355 return 0;
356 }
357 if (mem_map[(page-LOW_MEM)>>12] != 1) {
358 printk("put_page: mem_map disagrees with %p at %p\n",page,address);
359 return 0;
360 }
361 page_table = (unsigned long *) ((address>>20) & 0xffc);
362 if ((*page_table)&1)
363 page_table = (unsigned long *) (0xfffff000 & *page_table);
364 else {
365 if (!(tmp=get_free_page()))
366 return 0;
367 *page_table = tmp | 7;
368 page_table = (unsigned long *) tmp;
369 }
370 page_table += (address>>12) & 0x3ff;
371 if (*page_table) {
372 printk("put_page: page already exists\n");
373 *page_table = 0;
374 invalidate();
375 }
376 *page_table = page | 7;
377
378 return page;
379 }
380
381
382
383
384
385
386
387 unsigned long put_dirty_page(unsigned long page, unsigned long address)
388 {
389 unsigned long tmp, *page_table;
390
391
392
393 if (page < LOW_MEM || page >= HIGH_MEMORY)
394 printk("put_dirty_page: trying to put page %p at %p\n",page,address);
395 if (mem_map[(page-LOW_MEM)>>12] != 1)
396 printk("mem_map disagrees with %p at %p\n",page,address);
397 page_table = (unsigned long *) ((address>>20) & 0xffc);
398 if ((*page_table)&1)
399 page_table = (unsigned long *) (0xfffff000 & *page_table);
400 else {
401 if (!(tmp=get_free_page()))
402 return 0;
403 *page_table = tmp|7;
404 page_table = (unsigned long *) tmp;
405 }
406 page_table += (address>>12) & 0x3ff;
407 if (*page_table) {
408 printk("put_dirty_page: page already exists\n");
409 *page_table = 0;
410 invalidate();
411 }
412 *page_table = page | (PAGE_DIRTY | 7);
413
414 return page;
415 }
416
417 void un_wp_page(unsigned long * table_entry)
418 {
419 unsigned long old_page;
420 unsigned long new_page = 0;
421 unsigned long dirty;
422
423 repeat:
424 old_page = *table_entry;
425 dirty = old_page & PAGE_DIRTY;
426 if (!(old_page & 1)) {
427 if (new_page)
428 free_page(new_page);
429 return;
430 }
431 old_page &= 0xfffff000;
432 if (old_page >= HIGH_MEMORY) {
433 if (new_page)
434 free_page(new_page);
435 printk("bad page address\n\r");
436 do_exit(SIGSEGV);
437 }
438 if (old_page >= LOW_MEM && mem_map[MAP_NR(old_page)]==1) {
439 *table_entry |= 2;
440 invalidate();
441 if (new_page)
442 free_page(new_page);
443 return;
444 }
445 if (!new_page) {
446 if (!(new_page=get_free_page()))
447 oom();
448 goto repeat;
449 }
450 copy_page(old_page,new_page);
451 *table_entry = new_page | dirty | 7;
452 free_page(old_page);
453 invalidate();
454 }
455
456
457
458
459
460
461
462
463 void do_wp_page(unsigned long error_code,unsigned long address)
464 {
465 if (address < TASK_SIZE) {
466 printk("\n\rBAD! KERNEL MEMORY WP-ERR!\n\r");
467 do_exit(SIGSEGV);
468 }
469 if (address - current->start_code >= TASK_SIZE) {
470 printk("Bad things happen: page error in do_wp_page\n\r");
471 do_exit(SIGSEGV);
472 }
473 ++current->min_flt;
474 un_wp_page((unsigned long *)
475 (((address>>10) & 0xffc) + (0xfffff000 &
476 *((unsigned long *) ((address>>20) &0xffc)))));
477 }
478
479 void write_verify(unsigned long address)
480 {
481 unsigned long page;
482
483 if (!( (page = *((unsigned long *) ((address>>20) & 0xffc)) )&1))
484 return;
485 page &= 0xfffff000;
486 page += ((address>>10) & 0xffc);
487 if ((3 & *(unsigned long *) page) == 1)
488 un_wp_page((unsigned long *) page);
489 return;
490 }
491
492 void get_empty_page(unsigned long address)
493 {
494 unsigned long tmp;
495
496 if (!(tmp=get_free_page()) || !put_page(tmp,address)) {
497 free_page(tmp);
498 oom();
499 }
500 }
501
502
503
504
505
506
507
508
509
510 static int try_to_share(unsigned long address, struct task_struct * p)
511 {
512 unsigned long from;
513 unsigned long to;
514 unsigned long from_page;
515 unsigned long to_page;
516 unsigned long phys_addr;
517
518 from_page = to_page = ((address>>20) & 0xffc);
519 from_page += ((p->start_code>>20) & 0xffc);
520 to_page += ((current->start_code>>20) & 0xffc);
521
522 from = *(unsigned long *) from_page;
523 if (!(from & 1))
524 return 0;
525 from &= 0xfffff000;
526 from_page = from + ((address>>10) & 0xffc);
527 phys_addr = *(unsigned long *) from_page;
528
529 if ((phys_addr & 0x41) != 0x01)
530 return 0;
531 phys_addr &= 0xfffff000;
532 if (phys_addr >= HIGH_MEMORY || phys_addr < LOW_MEM)
533 return 0;
534 to = *(unsigned long *) to_page;
535 if (!(to & 1)) {
536 if (to = get_free_page())
537 *(unsigned long *) to_page = to | 7;
538 else
539 oom();
540 }
541 to &= 0xfffff000;
542 to_page = to + ((address>>10) & 0xffc);
543 if (1 & *(unsigned long *) to_page)
544 panic("try_to_share: to_page already exists");
545
546 *(unsigned long *) from_page &= ~2;
547 *(unsigned long *) to_page = *(unsigned long *) from_page;
548 invalidate();
549 phys_addr -= LOW_MEM;
550 phys_addr >>= 12;
551 mem_map[phys_addr]++;
552 return 1;
553 }
554
555
556
557
558
559
560
561
562
563 static int share_page(struct inode * inode, unsigned long address)
564 {
565 struct task_struct ** p;
566 int i;
567
568 if (inode->i_count < 2 || !inode)
569 return 0;
570 for (p = &LAST_TASK ; p > &FIRST_TASK ; --p) {
571 if (!*p)
572 continue;
573 if (current == *p)
574 continue;
575 if (address < LIBRARY_OFFSET) {
576 if (inode != (*p)->executable)
577 continue;
578 } else {
579 for (i=0; i < (*p)->numlibraries; i++)
580 if (inode == (*p)->libraries[i].library)
581 break;
582 if (i >= (*p)->numlibraries)
583 continue;
584 }
585 if (try_to_share(address,*p))
586 return 1;
587 }
588 return 0;
589 }
590
591
592
593
594 static unsigned long get_empty(unsigned long * p)
595 {
596 unsigned long page = 0;
597
598 repeat:
599 if (1 & *p) {
600 free_page(page);
601 return *p;
602 }
603 if (*p) {
604 printk("get_empty: bad page entry \n");
605 *p = 0;
606 }
607 if (page) {
608 *p = page | 7;
609 return *p;
610 }
611 if (!(page = get_free_page()))
612 oom();
613 goto repeat;
614 }
615
616 void do_no_page(unsigned long error_code, unsigned long address,
617 struct task_struct *tsk, unsigned long user_esp)
618 {
619 static unsigned int last_checked = 0;
620 int nr[4];
621 unsigned long tmp;
622 unsigned long page;
623 unsigned int block,i;
624 struct inode * inode;
625
626
627 for (i = 0; i < CHECK_LAST_NR; i++)
628 if ((address & 0xfffff000) == last_pages[i]) {
629 current->counter = 0;
630 schedule();
631 }
632 last_checked++;
633 if (last_checked >= CHECK_LAST_NR)
634 last_checked = 0;
635 last_pages[last_checked] = address & 0xfffff000;
636 if (address < TASK_SIZE) {
637 printk("\n\rBAD!! KERNEL PAGE MISSING\n\r");
638 do_exit(SIGSEGV);
639 }
640 if (address - tsk->start_code >= TASK_SIZE) {
641 printk("Bad things happen: nonexistent page error in do_no_page\n\r");
642 do_exit(SIGSEGV);
643 }
644 page = get_empty((unsigned long *) ((address >> 20) & 0xffc));
645 page &= 0xfffff000;
646 page += (address >> 10) & 0xffc;
647 tmp = *(unsigned long *) page;
648 if (tmp & 1) {
649 printk("bogus do_no_page\n");
650 return;
651 }
652 ++tsk->rss;
653 if (tmp) {
654 ++tsk->maj_flt;
655 swap_in((unsigned long *) page);
656 return;
657 }
658 address &= 0xfffff000;
659 tmp = address - tsk->start_code;
660 inode = NULL;
661 block = 0;
662 if (tmp < tsk->end_data) {
663 inode = tsk->executable;
664 block = 1 + tmp / BLOCK_SIZE;
665 } else {
666 i = tsk->numlibraries;
667 while (i-- > 0) {
668 if (tmp < tsk->libraries[i].start)
669 continue;
670 block = tmp - tsk->libraries[i].start;
671 if (block >= tsk->libraries[i].length)
672 continue;
673 inode = tsk->libraries[i].library;
674 block = 1 + block / BLOCK_SIZE;
675 break;
676 }
677 }
678 if (!inode) {
679 ++tsk->min_flt;
680 get_empty_page(address);
681 if (tsk != current)
682 return;
683 if (tmp >= LIBRARY_OFFSET || tmp < tsk->brk)
684 return;
685 if (tmp+8192 >= (user_esp & 0xfffff000))
686 return;
687 send_sig(SIGSEGV,tsk,1);
688 return;
689 }
690 if (tsk == current)
691 if (share_page(inode,tmp)) {
692 ++tsk->min_flt;
693 return;
694 }
695 ++tsk->maj_flt;
696 if (!(page = get_free_page()))
697 oom();
698 for (i=0 ; i<4 ; block++,i++)
699 nr[i] = bmap(inode,block);
700 bread_page(page,inode->i_dev,nr);
701 i = tmp + 4096 - tsk->end_data;
702 if (i>4095)
703 i = 0;
704 tmp = page + 4096;
705 while (i--) {
706 tmp--;
707 *(char *)tmp = 0;
708 }
709 if (put_page(page,address))
710 return;
711 free_page(page);
712 oom();
713 }
714
715 void mem_init(long start_mem, long end_mem)
716 {
717 int i;
718
719 swap_device = 0;
720 swap_file = NULL;
721 HIGH_MEMORY = end_mem;
722 for (i=0 ; i<PAGING_PAGES ; i++)
723 mem_map[i] = USED;
724 i = MAP_NR(start_mem);
725 end_mem -= start_mem;
726 end_mem >>= 12;
727 while (end_mem-->0)
728 mem_map[i++]=0;
729 }
730
731 void show_mem(void)
732 {
733 int i,j,k,free=0,total=0;
734 int shared = 0;
735 unsigned long * pg_tbl;
736
737 printk("Mem-info:\n\r");
738 for(i=0 ; i<PAGING_PAGES ; i++) {
739 if (mem_map[i] == USED)
740 continue;
741 total++;
742 if (!mem_map[i])
743 free++;
744 else
745 shared += mem_map[i]-1;
746 }
747 printk("%d free pages of %d\n\r",free,total);
748 printk("%d pages shared\n\r",shared);
749 k = 0;
750 for(i=4 ; i<1024 ;) {
751 if (1&pg_dir[i]) {
752 if (pg_dir[i]>HIGH_MEMORY) {
753 printk("page directory[%d]: %08X\n\r",
754 i,pg_dir[i]);
755 i++;
756 continue;
757 }
758 if (pg_dir[i]>LOW_MEM)
759 free++,k++;
760 pg_tbl=(unsigned long *) (0xfffff000 & pg_dir[i]);
761 for(j=0 ; j<1024 ; j++)
762 if ((pg_tbl[j]&1) && pg_tbl[j]>LOW_MEM)
763 if (pg_tbl[j]>HIGH_MEMORY)
764 printk("page_dir[%d][%d]: %08X\n\r",
765 i,j, pg_tbl[j]);
766 else
767 k++,free++;
768 }
769 i++;
770 if (!(i&15) && k) {
771 k++,free++;
772 printk("Process %d: %d pages\n\r",(i>>4)-1,k);
773 k = 0;
774 }
775 }
776 printk("Memory found: %d (%d)\n\r",free-shared,total);
777 }
778
779
780
781
782
783 void do_page_fault(unsigned long *esp, unsigned long error_code)
784 {
785 unsigned long address;
786 unsigned long user_esp;
787
788 if ((0xffff & esp[1]) == 0xf)
789 user_esp = esp[3];
790 else
791 user_esp = 0;
792
793 __asm__("movl %%cr2,%0":"=r" (address));
794 if (!(error_code & 1)) {
795 do_no_page(error_code, address, current, user_esp);
796 return;
797 } else {
798 do_wp_page(error_code, address);
799 return;
800 }
801 }