root/mm/memory.c

/* [previous][next][first][last][top][bottom][index][help] */

DEFINITIONS

This source file includes following definitions.
  1. free_page
  2. free_page_tables
  3. copy_page_tables
  4. unmap_page_range
  5. remap_page_range
  6. put_page
  7. put_dirty_page
  8. un_wp_page
  9. do_wp_page
  10. write_verify
  11. get_empty_page
  12. try_to_share
  13. share_page
  14. get_empty
  15. do_no_page
  16. mem_init
  17. show_mem
  18. do_page_fault

   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 /*
  24  * Real VM (paging to/from disk) started 18.12.91. Much more work and
  25  * thought has to go into this. Oh, well..
  26  * 19.12.91  -  works, somewhat. Sometimes I get faults, don't know why.
  27  *              Found it. Everything seems to work now.
  28  * 20.12.91  -  Ok, making the swap-device changeable like the root.
  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  * Free a page of memory at physical address 'addr'. Used by
  55  * 'free_page_tables()'
  56  */
  57 void free_page(unsigned long addr)
     /* [previous][next][first][last][top][bottom][index][help] */
  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  * This function frees a continuos block of page tables, as needed
  72  * by 'exit()'. As does copy_page_tables(), this handles only 4Mb blocks.
  73  */
  74 int free_page_tables(unsigned long from,unsigned long size)
     /* [previous][next][first][last][top][bottom][index][help] */
  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); /* _pg_dir = 0 */
  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  *  Well, here is one of the most complicated functions in mm. It
 115  * copies a range of linerar addresses by copying only the pages.
 116  * Let's hope this is bug-free, 'cause this one I don't want to debug :-)
 117  *
 118  * Note! We don't copy just any chunks of memory - addresses have to
 119  * be divisible by 4Mb (one page-directory entry), as this makes the
 120  * function easier. It's used only by fork anyway.
 121  *
 122  * NOTE 2!! When from==0 we are copying kernel space for the first
 123  * fork(). Then we DONT want to copy a full page-directory entry, as
 124  * that would lead to some serious memory waste - we just copy the
 125  * first 160 pages - 640kB. Even that is more than we need, but it
 126  * doesn't take any more memory - we don't copy-on-write in the low
 127  * 1 Mb-range, so the pages can be shared with the kernel. Thus the
 128  * special case for nr=xxxx.
 129  */
 130 int copy_page_tables(unsigned long from,unsigned long to,long size)
     /* [previous][next][first][last][top][bottom][index][help] */
 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); /* _pg_dir = 0 */
 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;      /* Out of memory, see freeing */
 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  * a more complete version of free_page_tables which performs with page
 190  * granularity.
 191  */
 192 int
 193 unmap_page_range(unsigned long from, unsigned long size)
     /* [previous][next][first][last][top][bottom][index][help] */
 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); /* _pg_dir = 0 */
 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  * maps a range of physical memory into the requested pages. the old
 247  * mappings are removed. any references to nonexistent pages results
 248  * in null mappings (currently treated as "copy-on-access")
 249  *
 250  * permiss is encoded as cxwr (copy,exec,write,read) where copy modifies
 251  * the behavior of write to be copy-on-write.
 252  *
 253  * due to current limitations, we actually have the following
 254  *              on              off
 255  * read:        yes             yes
 256  * write/copy:  yes/copy        copy/copy
 257  * exec:        yes             yes
 258  */
 259 int
 260 remap_page_range(unsigned long from, unsigned long to, unsigned long size,
     /* [previous][next][first][last][top][bottom][index][help] */
 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); /* _pg_dir = 0 */
 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                          * i'm not sure of the second cond here. should we
 314                          * report failure?
 315                          * the first condition should return an invalid access
 316                          * when the page is referenced. current assumptions
 317                          * cause it to be treated as demand allocation.
 318                          */
 319                         if (mask == 4 || to >= HIGH_MEMORY)
 320                                 *page_table++ = 0;      /* not present */
 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  * This function puts a page in memory at the wanted address.
 343  * It returns the physical address of the page gotten, 0 if
 344  * out of memory (either when trying to access page-table or
 345  * page.)
 346  */
 347 static unsigned long put_page(unsigned long page,unsigned long address)
     /* [previous][next][first][last][top][bottom][index][help] */
 348 {
 349         unsigned long tmp, *page_table;
 350 
 351 /* NOTE !!! This uses the fact that _pg_dir=0 */
 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 /* no need for invalidate */
 378         return page;
 379 }
 380 
 381 /*
 382  * The previous function doesn't work very well if you also want to mark
 383  * the page dirty: exec.c wants this, as it has earlier changed the page,
 384  * and we want the dirty-status to be correct (for VM). Thus the same
 385  * routine, but this time we mark it dirty too.
 386  */
 387 unsigned long put_dirty_page(unsigned long page, unsigned long address)
     /* [previous][next][first][last][top][bottom][index][help] */
 388 {
 389         unsigned long tmp, *page_table;
 390 
 391 /* NOTE !!! This uses the fact that _pg_dir=0 */
 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 /* no need for invalidate */
 414         return page;
 415 }
 416 
 417 void un_wp_page(unsigned long * table_entry)
     /* [previous][next][first][last][top][bottom][index][help] */
 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  * This routine handles present pages, when users try to write
 458  * to a shared page. It is done by copying the page to a new address
 459  * and decrementing the shared-page counter for the old page.
 460  *
 461  * If it's in code space we exit with a segment error.
 462  */
 463 void do_wp_page(unsigned long error_code,unsigned long address)
     /* [previous][next][first][last][top][bottom][index][help] */
 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)
     /* [previous][next][first][last][top][bottom][index][help] */
 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)  /* non-writeable, present */
 488                 un_wp_page((unsigned long *) page);
 489         return;
 490 }
 491 
 492 void get_empty_page(unsigned long address)
     /* [previous][next][first][last][top][bottom][index][help] */
 493 {
 494         unsigned long tmp;
 495 
 496         if (!(tmp=get_free_page()) || !put_page(tmp,address)) {
 497                 free_page(tmp);         /* 0 is ok - ignored */
 498                 oom();
 499         }
 500 }
 501 
 502 /*
 503  * try_to_share() checks the page at address "address" in the task "p",
 504  * to see if it exists, and if it is clean. If so, share it with the current
 505  * task.
 506  *
 507  * NOTE! This assumes we have checked that p != current, and that they
 508  * share the same executable or library.
 509  */
 510 static int try_to_share(unsigned long address, struct task_struct * p)
     /* [previous][next][first][last][top][bottom][index][help] */
 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 /* is there a page-directory at from? */
 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 /* is the page clean and present? */
 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 /* share them: write-protect */
 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  * share_page() tries to find a process that could share a page with
 557  * the current one. Address is the address of the wanted page relative
 558  * to the current data space.
 559  *
 560  * We first check if it is at all feasible by checking executable->i_count.
 561  * It should be >1 if there are other tasks sharing this inode.
 562  */
 563 static int share_page(struct inode * inode, unsigned long address)
     /* [previous][next][first][last][top][bottom][index][help] */
 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  * fill in an empty page or directory if none exists
 593  */
 594 static unsigned long get_empty(unsigned long * p)
     /* [previous][next][first][last][top][bottom][index][help] */
 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,
     /* [previous][next][first][last][top][bottom][index][help] */
 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         /* Thrashing ? Make it interruptible, but don't penalize otherwise */
 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)
     /* [previous][next][first][last][top][bottom][index][help] */
 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)
     /* [previous][next][first][last][top][bottom][index][help] */
 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++;     /* one page/process for task_struct */
 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 /* This routine handles page faults.  It determines the address,
 781    and the problem then passes it off to one of the appropriate
 782    routines. */
 783 void do_page_fault(unsigned long *esp, unsigned long error_code)
     /* [previous][next][first][last][top][bottom][index][help] */
 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         /* get the address */
 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 }

/* [previous][next][first][last][top][bottom][index][help] */