Annotation of linux/mm/memory.c, revision 1.1.1.12

1.1.1.2   root        1: /*
                      2:  *  linux/mm/memory.c
                      3:  *
1.1.1.11  root        4:  *  Copyright (C) 1991, 1992  Linus Torvalds
1.1.1.2   root        5:  */
                      6: 
1.1.1.3   root        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: 
1.1.1.4   root       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: 
1.1.1.3   root       31: #include <asm/system.h>
                     32: 
1.1.1.11  root       33: #include <linux/signal.h>
1.1.1.3   root       34: #include <linux/sched.h>
1.1       root       35: #include <linux/head.h>
                     36: #include <linux/kernel.h>
1.1.1.12! root       37: #include <linux/errno.h>
1.1.1.11  root       38: #include <linux/string.h>
1.1       root       39: 
1.1.1.11  root       40: unsigned long high_memory = 0;
1.1.1.12! root       41: 
        !            42: int nr_free_pages = 0;
        !            43: unsigned long free_page_list = 0;
        !            44: /*
        !            45:  * The secondary free_page_list is used for malloc() etc things that
        !            46:  * may need pages during interrupts etc. Normal get_free_page() operations
        !            47:  * don't touch it, so it stays as a kind of "panic-list", that can be
        !            48:  * accessed when all other mm tricks have failed.
        !            49:  */
        !            50: int nr_secondary_pages = 0;
        !            51: unsigned long secondary_page_list = 0;
1.1       root       52: 
                     53: #define copy_page(from,to) \
                     54: __asm__("cld ; rep ; movsl"::"S" (from),"D" (to),"c" (1024):"cx","di","si")
                     55: 
1.1.1.12! root       56: unsigned short * mem_map = NULL;
1.1.1.5   root       57: 
1.1.1.11  root       58: /*
                     59:  * oom() prints a message (so that the user knows why the process died),
                     60:  * and gives the process an untrappable SIGSEGV.
                     61:  */
                     62: void oom(struct task_struct * task)
                     63: {
                     64:        printk("\nout of memory\n");
                     65:        task->sigaction[SIGSEGV-1].sa_handler = NULL;
                     66:        task->blocked &= ~(1<<(SIGSEGV-1));
                     67:        send_sig(SIGSEGV,task,1);
                     68: }
1.1       root       69: 
1.1.1.12! root       70: static void free_one_table(unsigned long * page_dir)
        !            71: {
        !            72:        int j;
        !            73:        unsigned long pg_table = *page_dir;
        !            74:        unsigned long * page_table;
        !            75: 
        !            76:        if (!pg_table)
        !            77:                return;
        !            78:        if (pg_table >= high_memory || !(pg_table & 1)) {
        !            79:                printk("Bad page table: [%08x]=%08x\n",page_dir,pg_table);
        !            80:                *page_dir = 0;
        !            81:                return;
        !            82:        }
        !            83:        *page_dir = 0;
        !            84:        if (mem_map[MAP_NR(pg_table)] & MAP_PAGE_RESERVED)
        !            85:                return;
        !            86:        page_table = (unsigned long *) (pg_table & 0xfffff000);
        !            87:        for (j = 0 ; j < 1024 ; j++,page_table++) {
        !            88:                unsigned long pg = *page_table;
        !            89:                
        !            90:                if (!pg)
        !            91:                        continue;
        !            92:                *page_table = 0;
        !            93:                if (1 & pg)
        !            94:                        free_page(0xfffff000 & pg);
        !            95:                else
        !            96:                        swap_free(pg >> 1);
        !            97:        }
        !            98:        free_page(0xfffff000 & pg_table);
        !            99: }
        !           100: 
1.1       root      101: /*
1.1.1.12! root      102:  * This function clears all user-level page tables of a process - this
        !           103:  * is needed by execve(), so that old pages aren't in the way. Note that
        !           104:  * unlike 'free_page_tables()', this function still leaves a valid
        !           105:  * page-table-tree in memory: it just removes the user pages. The two
        !           106:  * functions are similar, but there is a fundamental difference.
1.1       root      107:  */
1.1.1.12! root      108: void clear_page_tables(struct task_struct * tsk)
1.1       root      109: {
1.1.1.12! root      110:        int i;
        !           111:        unsigned long * page_dir;
1.1.1.11  root      112: 
1.1.1.12! root      113:        if (!tsk)
        !           114:                return;
        !           115:        if (tsk == task[0])
        !           116:                panic("task[0] (swapper) doesn't support exec() yet\n");
        !           117:        page_dir = (unsigned long *) tsk->tss.cr3;
        !           118:        if (!page_dir) {
        !           119:                printk("Trying to clear kernel page-directory: not good\n");
1.1.1.11  root      120:                return;
1.1.1.5   root      121:        }
1.1.1.12! root      122:        for (i = 0 ; i < 768 ; i++,page_dir++)
        !           123:                free_one_table(page_dir);
        !           124:        invalidate();
        !           125:        return;
1.1       root      126: }
                    127: 
                    128: /*
1.1.1.12! root      129:  * This function frees up all page tables of a process when it exits.
1.1       root      130:  */
1.1.1.12! root      131: void free_page_tables(struct task_struct * tsk)
1.1       root      132: {
1.1.1.12! root      133:        int i;
        !           134:        unsigned long pg_dir;
        !           135:        unsigned long * page_dir;
1.1       root      136: 
1.1.1.12! root      137:        if (!tsk)
        !           138:                return;
        !           139:        if (tsk == task[0]) {
        !           140:                printk("task[0] (swapper) killed: unable to recover\n");
1.1       root      141:                panic("Trying to free up swapper memory space");
                    142:        }
1.1.1.12! root      143:        pg_dir = tsk->tss.cr3;
        !           144:        if (!pg_dir) {
        !           145:                printk("Trying to free kernel page-directory: not good\n");
        !           146:                return;
        !           147:        }
        !           148:        tsk->tss.cr3 = (unsigned long) swapper_pg_dir;
        !           149:        if (tsk == current)
        !           150:                __asm__ __volatile__("movl %0,%%cr3"::"a" (tsk->tss.cr3));
        !           151:        page_dir = (unsigned long *) pg_dir;
        !           152:        for (i = 0 ; i < 1024 ; i++,page_dir++)
        !           153:                free_one_table(page_dir);
        !           154:        free_page(pg_dir);
1.1       root      155:        invalidate();
                    156: }
                    157: 
                    158: /*
1.1.1.12! root      159:  * copy_page_tables() just copies the whole process memory range:
        !           160:  * note the special handling of RESERVED (ie kernel) pages, which
        !           161:  * means that they are always shared by all processes.
        !           162:  */
        !           163: int copy_page_tables(struct task_struct * tsk)
        !           164: {
        !           165:        int i;
        !           166:        unsigned long old_pg_dir, *old_page_dir;
        !           167:        unsigned long new_pg_dir, *new_page_dir;
        !           168: 
        !           169:        old_pg_dir = current->tss.cr3;
        !           170:        new_pg_dir = get_free_page(GFP_KERNEL);
        !           171:        if (!new_pg_dir)
        !           172:                return -ENOMEM;
        !           173:        tsk->tss.cr3 = new_pg_dir;
        !           174:        old_page_dir = (unsigned long *) old_pg_dir;
        !           175:        new_page_dir = (unsigned long *) new_pg_dir;
        !           176:        for (i = 0 ; i < 1024 ; i++,old_page_dir++,new_page_dir++) {
        !           177:                int j;
        !           178:                unsigned long old_pg_table, *old_page_table;
        !           179:                unsigned long new_pg_table, *new_page_table;
        !           180: 
        !           181:                old_pg_table = *old_page_dir;
        !           182:                if (!old_pg_table)
1.1.1.5   root      183:                        continue;
1.1.1.12! root      184:                if (old_pg_table >= high_memory || !(1 & old_pg_table)) {
        !           185:                        printk("copy_page_tables: bad page table: "
1.1.1.5   root      186:                                "probable memory corruption");
1.1.1.12! root      187:                        *old_page_dir = 0;
1.1       root      188:                        continue;
1.1.1.5   root      189:                }
1.1.1.12! root      190:                if (mem_map[MAP_NR(old_pg_table)] & MAP_PAGE_RESERVED) {
        !           191:                        *new_page_dir = old_pg_table;
        !           192:                        continue;
        !           193:                }
        !           194:                new_pg_table = get_free_page(GFP_KERNEL);
        !           195:                if (!new_pg_table) {
        !           196:                        free_page_tables(tsk);
        !           197:                        return -ENOMEM;
        !           198:                }
        !           199:                *new_page_dir = new_pg_table | PAGE_ACCESSED | 7;
        !           200:                old_page_table = (unsigned long *) (0xfffff000 & old_pg_table);
        !           201:                new_page_table = (unsigned long *) (0xfffff000 & new_pg_table);
        !           202:                for (j = 0 ; j < 1024 ; j++,old_page_table++,new_page_table++) {
        !           203:                        unsigned long pg;
        !           204:                        pg = *old_page_table;
        !           205:                        if (!pg)
1.1       root      206:                                continue;
1.1.1.12! root      207:                        if (!(pg & PAGE_PRESENT)) {
        !           208:                                swap_duplicate(pg>>1);
        !           209:                                *new_page_table = pg;
1.1.1.4   root      210:                                continue;
                    211:                        }
1.1.1.12! root      212:                        pg &= ~2;
        !           213:                        *new_page_table = pg;
        !           214:                        if (mem_map[MAP_NR(pg)] & MAP_PAGE_RESERVED)
        !           215:                                continue;
        !           216:                        *old_page_table = pg;
        !           217:                        mem_map[MAP_NR(pg)]++;
1.1       root      218:                }
                    219:        }
                    220:        invalidate();
                    221:        return 0;
                    222: }
                    223: 
                    224: /*
1.1.1.8   root      225:  * a more complete version of free_page_tables which performs with page
                    226:  * granularity.
                    227:  */
1.1.1.10  root      228: int unmap_page_range(unsigned long from, unsigned long size)
1.1.1.8   root      229: {
                    230:        unsigned long page, page_dir;
                    231:        unsigned long *page_table, *dir;
                    232:        unsigned long poff, pcnt, pc;
                    233: 
                    234:        if (from & 0xfff)
                    235:                panic("unmap_page_range called with wrong alignment");
                    236:        if (!from)
                    237:                panic("unmap_page_range trying to free swapper memory space");
1.1.1.12! root      238:        size = (size + 0xfff) >> PAGE_SHIFT;
        !           239:        dir = (unsigned long *) (current->tss.cr3 + ((from >> 20) & 0xffc));
        !           240:        poff = (from >> PAGE_SHIFT) & 0x3ff;
1.1.1.8   root      241:        if ((pcnt = 1024 - poff) > size)
                    242:                pcnt = size;
                    243: 
                    244:        for ( ; size > 0; ++dir, size -= pcnt,
                    245:             pcnt = (size > 1024 ? 1024 : size)) {
                    246:                if (!(page_dir = *dir)) {
                    247:                        poff = 0;
                    248:                        continue;
                    249:                }
                    250:                if (!(page_dir & 1)) {
                    251:                        printk("unmap_page_range: bad page directory.");
                    252:                        continue;
                    253:                }
                    254:                page_table = (unsigned long *)(0xfffff000 & page_dir);
                    255:                if (poff) {
                    256:                        page_table += poff;
                    257:                        poff = 0;
                    258:                }
                    259:                for (pc = pcnt; pc--; page_table++) {
                    260:                        if (page = *page_table) {
                    261:                                --current->rss;
                    262:                                *page_table = 0;
                    263:                                if (1 & page)
                    264:                                        free_page(0xfffff000 & page);
                    265:                                else
                    266:                                        swap_free(page >> 1);
                    267:                        }
                    268:                }
                    269:                if (pcnt == 1024) {
                    270:                        free_page(0xfffff000 & page_dir);
                    271:                        *dir = 0;
                    272:                }
                    273:        }
                    274:        invalidate();
                    275:        return 0;
                    276: }
                    277: 
                    278: /*
                    279:  * maps a range of physical memory into the requested pages. the old
                    280:  * mappings are removed. any references to nonexistent pages results
                    281:  * in null mappings (currently treated as "copy-on-access")
                    282:  *
                    283:  * permiss is encoded as cxwr (copy,exec,write,read) where copy modifies
                    284:  * the behavior of write to be copy-on-write.
                    285:  *
                    286:  * due to current limitations, we actually have the following
                    287:  *             on              off
                    288:  * read:       yes             yes
                    289:  * write/copy: yes/copy        copy/copy
                    290:  * exec:       yes             yes
                    291:  */
1.1.1.10  root      292: int remap_page_range(unsigned long from, unsigned long to, unsigned long size,
1.1.1.8   root      293:                 int permiss)
                    294: {
                    295:        unsigned long *page_table, *dir;
                    296:        unsigned long poff, pcnt;
1.1.1.11  root      297:        unsigned long page;
1.1.1.8   root      298: 
                    299:        if ((from & 0xfff) || (to & 0xfff))
                    300:                panic("remap_page_range called with wrong alignment");
1.1.1.12! root      301:        dir = (unsigned long *) (current->tss.cr3 + ((from >> 20) & 0xffc));
        !           302:        size = (size + 0xfff) >> PAGE_SHIFT;
        !           303:        poff = (from >> PAGE_SHIFT) & 0x3ff;
1.1.1.8   root      304:        if ((pcnt = 1024 - poff) > size)
                    305:                pcnt = size;
                    306: 
                    307:        while (size > 0) {
                    308:                if (!(1 & *dir)) {
1.1.1.11  root      309:                        if (!(page_table = (unsigned long *)get_free_page(GFP_KERNEL))) {
1.1.1.8   root      310:                                invalidate();
                    311:                                return -1;
                    312:                        }
1.1.1.12! root      313:                        *dir++ = ((unsigned long) page_table) | PAGE_ACCESSED | 7;
1.1.1.8   root      314:                }
                    315:                else
                    316:                        page_table = (unsigned long *)(0xfffff000 & *dir++);
                    317:                if (poff) {
                    318:                        page_table += poff;
                    319:                        poff = 0;
                    320:                }
                    321: 
                    322:                for (size -= pcnt; pcnt-- ;) {
                    323:                        int mask;
                    324: 
                    325:                        mask = 4;
                    326:                        if (permiss & 1)
                    327:                                mask |= 1;
                    328:                        if (permiss & 2) {
                    329:                                if (permiss & 8)
                    330:                                        mask |= 1;
                    331:                                else
                    332:                                        mask |= 3;
                    333:                        }
                    334:                        if (permiss & 4)
                    335:                                mask |= 1;
                    336: 
1.1.1.11  root      337:                        if (page = *page_table) {
                    338:                                *page_table = 0;
1.1.1.8   root      339:                                --current->rss;
1.1.1.11  root      340:                                if (1 & page)
                    341:                                        free_page(0xfffff000 & page);
1.1.1.8   root      342:                                else
1.1.1.11  root      343:                                        swap_free(page >> 1);
1.1.1.8   root      344:                        }
                    345: 
                    346:                        /*
                    347:                         * i'm not sure of the second cond here. should we
                    348:                         * report failure?
                    349:                         * the first condition should return an invalid access
                    350:                         * when the page is referenced. current assumptions
                    351:                         * cause it to be treated as demand allocation.
                    352:                         */
1.1.1.12! root      353:                        if (mask == 4 || to >= high_memory || !mem_map[MAP_NR(to)])
1.1.1.8   root      354:                                *page_table++ = 0;      /* not present */
                    355:                        else {
                    356:                                ++current->rss;
                    357:                                *page_table++ = (to | mask);
1.1.1.12! root      358:                                if (!(mem_map[MAP_NR(to)] & MAP_PAGE_RESERVED))
        !           359:                                        mem_map[MAP_NR(to)]++;
1.1.1.8   root      360:                        }
                    361:                        to += PAGE_SIZE;
                    362:                }
                    363:                pcnt = (size > 1024 ? 1024 : size);
                    364:        }
                    365:        invalidate();
                    366:        return 0;
                    367: }
                    368: 
                    369: /*
1.1       root      370:  * This function puts a page in memory at the wanted address.
                    371:  * It returns the physical address of the page gotten, 0 if
                    372:  * out of memory (either when trying to access page-table or
                    373:  * page.)
                    374:  */
1.1.1.12! root      375: static unsigned long put_page(struct task_struct * tsk,unsigned long page,unsigned long address)
1.1       root      376: {
                    377:        unsigned long tmp, *page_table;
                    378: 
                    379: /* NOTE !!! This uses the fact that _pg_dir=0 */
                    380: 
1.1.1.11  root      381:        if (page >= high_memory) {
1.1.1.5   root      382:                printk("put_page: trying to put page %p at %p\n",page,address);
                    383:                return 0;
                    384:        }
1.1.1.12! root      385:        tmp = mem_map[MAP_NR(page)];
        !           386:        if (!(tmp & MAP_PAGE_RESERVED) && (tmp != 1)) {
1.1.1.8   root      387:                printk("put_page: mem_map disagrees with %p at %p\n",page,address);
1.1.1.5   root      388:                return 0;
                    389:        }
1.1.1.12! root      390:        page_table = (unsigned long *) (tsk->tss.cr3 + ((address>>20) & 0xffc));
1.1       root      391:        if ((*page_table)&1)
                    392:                page_table = (unsigned long *) (0xfffff000 & *page_table);
                    393:        else {
1.1.1.11  root      394:                tmp = get_free_page(GFP_KERNEL);
                    395:                if (!tmp) {
1.1.1.12! root      396:                        oom(tsk);
1.1.1.11  root      397:                        tmp = BAD_PAGETABLE;
                    398:                }
1.1.1.12! root      399:                *page_table = tmp | PAGE_ACCESSED | 7;
1.1.1.11  root      400:                return 0;
1.1       root      401:        }
1.1.1.12! root      402:        page_table += (address >> PAGE_SHIFT) & 0x3ff;
1.1.1.6   root      403:        if (*page_table) {
                    404:                printk("put_page: page already exists\n");
                    405:                *page_table = 0;
                    406:                invalidate();
                    407:        }
1.1.1.12! root      408:        *page_table = page | PAGE_ACCESSED | 7;
1.1.1.3   root      409: /* no need for invalidate */
1.1       root      410:        return page;
                    411: }
                    412: 
1.1.1.4   root      413: /*
                    414:  * The previous function doesn't work very well if you also want to mark
                    415:  * the page dirty: exec.c wants this, as it has earlier changed the page,
                    416:  * and we want the dirty-status to be correct (for VM). Thus the same
                    417:  * routine, but this time we mark it dirty too.
                    418:  */
1.1.1.12! root      419: unsigned long put_dirty_page(struct task_struct * tsk, unsigned long page, unsigned long address)
1.1.1.4   root      420: {
                    421:        unsigned long tmp, *page_table;
                    422: 
                    423: /* NOTE !!! This uses the fact that _pg_dir=0 */
                    424: 
1.1.1.12! root      425:        if (page >= high_memory)
1.1.1.5   root      426:                printk("put_dirty_page: trying to put page %p at %p\n",page,address);
1.1.1.12! root      427:        if (mem_map[MAP_NR(page)] != 1)
1.1.1.4   root      428:                printk("mem_map disagrees with %p at %p\n",page,address);
1.1.1.12! root      429:        page_table = (unsigned long *) (tsk->tss.cr3 + ((address>>20) & 0xffc));
1.1.1.4   root      430:        if ((*page_table)&1)
                    431:                page_table = (unsigned long *) (0xfffff000 & *page_table);
                    432:        else {
1.1.1.11  root      433:                if (!(tmp=get_free_page(GFP_KERNEL)))
1.1.1.4   root      434:                        return 0;
                    435:                *page_table = tmp|7;
                    436:                page_table = (unsigned long *) tmp;
                    437:        }
1.1.1.12! root      438:        page_table += (address >> PAGE_SHIFT) & 0x3ff;
1.1.1.6   root      439:        if (*page_table) {
                    440:                printk("put_dirty_page: page already exists\n");
                    441:                *page_table = 0;
                    442:                invalidate();
                    443:        }
1.1.1.12! root      444:        *page_table = page | (PAGE_DIRTY | PAGE_ACCESSED | 7);
1.1.1.4   root      445: /* no need for invalidate */
                    446:        return page;
                    447: }
                    448: 
1.1.1.11  root      449: static void un_wp_page(unsigned long * table_entry, struct task_struct * task)
1.1       root      450: {
1.1.1.5   root      451:        unsigned long old_page;
                    452:        unsigned long new_page = 0;
                    453:        unsigned long dirty;
                    454: 
                    455: repeat:
                    456:        old_page = *table_entry;
                    457:        if (!(old_page & 1)) {
                    458:                if (new_page)
                    459:                        free_page(new_page);
                    460:                return;
                    461:        }
1.1.1.11  root      462:        dirty = old_page & PAGE_DIRTY;
1.1.1.5   root      463:        old_page &= 0xfffff000;
1.1.1.11  root      464:        if (old_page >= high_memory) {
1.1.1.5   root      465:                if (new_page)
                    466:                        free_page(new_page);
                    467:                printk("bad page address\n\r");
1.1.1.11  root      468:                send_sig(SIGSEGV, task, 1);
                    469:                *table_entry = BAD_PAGE | 7;
                    470:                return;
1.1.1.5   root      471:        }
1.1.1.12! root      472:        if (mem_map[MAP_NR(old_page)]==1) {
1.1       root      473:                *table_entry |= 2;
1.1.1.3   root      474:                invalidate();
1.1.1.5   root      475:                if (new_page)
                    476:                        free_page(new_page);
1.1       root      477:                return;
                    478:        }
1.1.1.11  root      479:        if (!new_page && (new_page=get_free_page(GFP_KERNEL)))
1.1.1.5   root      480:                goto repeat;
1.1.1.11  root      481:        if (new_page)
                    482:                copy_page(old_page,new_page);
                    483:        else {
                    484:                new_page = BAD_PAGE;
1.1.1.12! root      485:                oom(task);
1.1.1.5   root      486:        }
1.1.1.12! root      487:        *table_entry = new_page | dirty | PAGE_ACCESSED | 7;
1.1.1.5   root      488:        free_page(old_page);
1.1.1.3   root      489:        invalidate();
1.1       root      490: }      
                    491: 
                    492: /*
                    493:  * This routine handles present pages, when users try to write
                    494:  * to a shared page. It is done by copying the page to a new address
                    495:  * and decrementing the shared-page counter for the old page.
1.1.1.3   root      496:  *
                    497:  * If it's in code space we exit with a segment error.
1.1       root      498:  */
1.1.1.11  root      499: void do_wp_page(unsigned long error_code, unsigned long address,
                    500:        struct task_struct * tsk, unsigned long user_esp)
1.1       root      501: {
1.1.1.11  root      502:        unsigned long pde, pte, page;
                    503: 
1.1.1.12! root      504:        pde = tsk->tss.cr3 + ((address>>20) & 0xffc);
1.1.1.11  root      505:        pte = *(unsigned long *) pde;
                    506:        if ((pte & 3) != 3) {
                    507:                printk("do_wp_page: bogus page-table at address %08x (%08x)\n",address,pte);
                    508:                *(unsigned long *) pde = BAD_PAGETABLE | 7;
                    509:                send_sig(SIGSEGV, tsk, 1);
                    510:                return;
                    511:        }
                    512:        pte &= 0xfffff000;
                    513:        pte += (address>>10) & 0xffc;
                    514:        page = *(unsigned long *) pte;
                    515:        if ((page & 3) != 1) {
                    516:                printk("do_wp_page: bogus page at address %08x (%08x)\n",address,page);
                    517:                *(unsigned long *) pte = BAD_PAGE | 7;
                    518:                send_sig(SIGSEGV, tsk, 1);
                    519:                return;
1.1.1.4   root      520:        }
1.1.1.12! root      521:        tsk->min_flt++;
1.1.1.11  root      522:        un_wp_page((unsigned long *) pte, tsk);
1.1       root      523: }
                    524: 
                    525: void write_verify(unsigned long address)
                    526: {
                    527:        unsigned long page;
                    528: 
1.1.1.12! root      529:        page = *(unsigned long *) (current->tss.cr3 + ((address>>20) & 0xffc));
1.1.1.11  root      530:        if (!(page & PAGE_PRESENT))
1.1       root      531:                return;
                    532:        page &= 0xfffff000;
                    533:        page += ((address>>10) & 0xffc);
                    534:        if ((3 & *(unsigned long *) page) == 1)  /* non-writeable, present */
1.1.1.11  root      535:                un_wp_page((unsigned long *) page, current);
1.1       root      536:        return;
                    537: }
                    538: 
1.1.1.12! root      539: static void get_empty_page(struct task_struct * tsk, unsigned long address)
1.1.1.3   root      540: {
                    541:        unsigned long tmp;
                    542: 
1.1.1.11  root      543:        tmp = get_free_page(GFP_KERNEL);
                    544:        if (!tmp) {
1.1.1.12! root      545:                oom(tsk);
1.1.1.11  root      546:                tmp = BAD_PAGE;
1.1.1.3   root      547:        }
1.1.1.12! root      548:        if (!put_page(tsk,tmp,address))
1.1.1.11  root      549:                free_page(tmp);
1.1.1.3   root      550: }
                    551: 
                    552: /*
                    553:  * try_to_share() checks the page at address "address" in the task "p",
                    554:  * to see if it exists, and if it is clean. If so, share it with the current
                    555:  * task.
                    556:  *
                    557:  * NOTE! This assumes we have checked that p != current, and that they
1.1.1.4   root      558:  * share the same executable or library.
1.1.1.3   root      559:  */
1.1.1.12! root      560: static int try_to_share(unsigned long address, struct task_struct * tsk,
        !           561:        struct task_struct * p)
1.1.1.3   root      562: {
                    563:        unsigned long from;
                    564:        unsigned long to;
                    565:        unsigned long from_page;
                    566:        unsigned long to_page;
                    567:        unsigned long phys_addr;
                    568: 
1.1.1.12! root      569:        from_page = p->tss.cr3 + ((address>>20) & 0xffc);
        !           570:        to_page = tsk->tss.cr3 + ((address>>20) & 0xffc);
1.1.1.3   root      571: /* is there a page-directory at from? */
                    572:        from = *(unsigned long *) from_page;
                    573:        if (!(from & 1))
                    574:                return 0;
                    575:        from &= 0xfffff000;
                    576:        from_page = from + ((address>>10) & 0xffc);
                    577:        phys_addr = *(unsigned long *) from_page;
                    578: /* is the page clean and present? */
                    579:        if ((phys_addr & 0x41) != 0x01)
                    580:                return 0;
                    581:        phys_addr &= 0xfffff000;
1.1.1.12! root      582:        if (phys_addr >= high_memory)
        !           583:                return 0;
        !           584:        if (mem_map[MAP_NR(phys_addr)] & MAP_PAGE_RESERVED)
1.1.1.3   root      585:                return 0;
                    586:        to = *(unsigned long *) to_page;
1.1.1.5   root      587:        if (!(to & 1)) {
1.1.1.11  root      588:                to = get_free_page(GFP_KERNEL);
                    589:                if (!to)
                    590:                        return 0;
1.1.1.12! root      591:                *(unsigned long *) to_page = to | PAGE_ACCESSED | 7;
1.1.1.5   root      592:        }
1.1.1.3   root      593:        to &= 0xfffff000;
                    594:        to_page = to + ((address>>10) & 0xffc);
                    595:        if (1 & *(unsigned long *) to_page)
                    596:                panic("try_to_share: to_page already exists");
                    597: /* share them: write-protect */
                    598:        *(unsigned long *) from_page &= ~2;
                    599:        *(unsigned long *) to_page = *(unsigned long *) from_page;
                    600:        invalidate();
1.1.1.12! root      601:        phys_addr >>= PAGE_SHIFT;
        !           602:        mem_map[phys_addr]++;
1.1.1.3   root      603:        return 1;
                    604: }
                    605: 
                    606: /*
                    607:  * share_page() tries to find a process that could share a page with
                    608:  * the current one. Address is the address of the wanted page relative
                    609:  * to the current data space.
                    610:  *
                    611:  * We first check if it is at all feasible by checking executable->i_count.
                    612:  * It should be >1 if there are other tasks sharing this inode.
                    613:  */
1.1.1.12! root      614: static int share_page(struct task_struct * tsk, struct inode * inode, unsigned long address)
1.1.1.3   root      615: {
                    616:        struct task_struct ** p;
1.1.1.8   root      617:        int i;
1.1.1.3   root      618: 
1.1.1.9   root      619:        if (!inode || inode->i_count < 2)
1.1.1.3   root      620:                return 0;
                    621:        for (p = &LAST_TASK ; p > &FIRST_TASK ; --p) {
                    622:                if (!*p)
                    623:                        continue;
1.1.1.12! root      624:                if (tsk == *p)
1.1.1.3   root      625:                        continue;
1.1.1.12! root      626:                if (inode != (*p)->executable) {
1.1.1.8   root      627:                        for (i=0; i < (*p)->numlibraries; i++)
                    628:                                if (inode == (*p)->libraries[i].library)
                    629:                                        break;
                    630:                        if (i >= (*p)->numlibraries)
1.1.1.4   root      631:                                continue;
                    632:                }
1.1.1.12! root      633:                if (try_to_share(address,tsk,*p))
1.1.1.3   root      634:                        return 1;
                    635:        }
                    636:        return 0;
                    637: }
                    638: 
1.1.1.8   root      639: /*
1.1.1.11  root      640:  * fill in an empty page-table if none exists
1.1.1.8   root      641:  */
1.1.1.12! root      642: static unsigned long get_empty_pgtable(struct task_struct * tsk,unsigned long address)
1.1.1.8   root      643: {
                    644:        unsigned long page = 0;
1.1.1.12! root      645:        unsigned long *p;
1.1.1.8   root      646: repeat:
1.1.1.12! root      647:        p = (unsigned long *) (tsk->tss.cr3 + ((address >> 20) & 0xffc));
1.1.1.8   root      648:        if (1 & *p) {
                    649:                free_page(page);
                    650:                return *p;
                    651:        }
                    652:        if (*p) {
1.1.1.11  root      653:                printk("get_empty_pgtable: bad page-directory entry \n");
1.1.1.8   root      654:                *p = 0;
                    655:        }
                    656:        if (page) {
1.1.1.12! root      657:                *p = page | PAGE_ACCESSED | 7;
1.1.1.8   root      658:                return *p;
                    659:        }
1.1.1.11  root      660:        if (page = get_free_page(GFP_KERNEL))
                    661:                goto repeat;
                    662:        oom(current);
                    663:        *p = BAD_PAGETABLE | 7;
                    664:        return 0;
1.1.1.8   root      665: }
                    666: 
1.1.1.7   root      667: void do_no_page(unsigned long error_code, unsigned long address,
1.1.1.8   root      668:        struct task_struct *tsk, unsigned long user_esp)
1.1       root      669: {
1.1.1.3   root      670:        int nr[4];
1.1       root      671:        unsigned long tmp;
1.1.1.3   root      672:        unsigned long page;
1.1.1.8   root      673:        unsigned int block,i;
1.1.1.5   root      674:        struct inode * inode;
1.1       root      675: 
1.1.1.12! root      676:        page = get_empty_pgtable(tsk,address);
1.1.1.11  root      677:        if (!page)
                    678:                return;
1.1.1.8   root      679:        page &= 0xfffff000;
                    680:        page += (address >> 10) & 0xffc;
                    681:        tmp = *(unsigned long *) page;
                    682:        if (tmp & 1) {
                    683:                printk("bogus do_no_page\n");
                    684:                return;
                    685:        }
1.1.1.7   root      686:        ++tsk->rss;
1.1.1.8   root      687:        if (tmp) {
                    688:                ++tsk->maj_flt;
                    689:                swap_in((unsigned long *) page);
                    690:                return;
1.1.1.4   root      691:        }
1.1.1.3   root      692:        address &= 0xfffff000;
1.1.1.8   root      693:        inode = NULL;
                    694:        block = 0;
1.1.1.12! root      695:        if (address < tsk->end_data) {
1.1.1.5   root      696:                inode = tsk->executable;
1.1.1.12! root      697:                block = 1 + address / BLOCK_SIZE;
1.1.1.4   root      698:        } else {
1.1.1.8   root      699:                i = tsk->numlibraries;
                    700:                while (i-- > 0) {
1.1.1.12! root      701:                        if (address < tsk->libraries[i].start)
1.1.1.8   root      702:                                continue;
1.1.1.12! root      703:                        block = address - tsk->libraries[i].start;
        !           704:                        if (block >= tsk->libraries[i].length + tsk->libraries[i].bss)
1.1.1.8   root      705:                                continue;
                    706:                        inode = tsk->libraries[i].library;
1.1.1.12! root      707:                        if (block < tsk->libraries[i].length)
        !           708:                                block = 1 + block / BLOCK_SIZE;
        !           709:                        else
        !           710:                                block = 0;
1.1.1.8   root      711:                        break;
                    712:                }
1.1.1.4   root      713:        }
                    714:        if (!inode) {
1.1.1.7   root      715:                ++tsk->min_flt;
1.1.1.12! root      716:                get_empty_page(tsk,address);
1.1.1.8   root      717:                if (tsk != current)
                    718:                        return;
1.1.1.12! root      719:                if (address < tsk->brk)
1.1.1.8   root      720:                        return;
1.1.1.12! root      721:                if (address+8192 >= (user_esp & 0xfffff000))
1.1.1.8   root      722:                        return;
                    723:                send_sig(SIGSEGV,tsk,1);
1.1.1.3   root      724:                return;
                    725:        }
1.1.1.12! root      726:        if (share_page(tsk,inode,address)) {
        !           727:                ++tsk->min_flt;
        !           728:                return;
        !           729:        }
1.1.1.7   root      730:        ++tsk->maj_flt;
1.1.1.11  root      731:        page = get_free_page(GFP_KERNEL);
                    732:        if (!page) {
                    733:                oom(current);
1.1.1.12! root      734:                put_page(tsk,BAD_PAGE,address);
1.1.1.11  root      735:                return;
                    736:        }
1.1.1.12! root      737:        if (block) {
        !           738:                for (i=0 ; i<4 ; block++,i++)
        !           739:                        nr[i] = bmap(inode,block);
        !           740:                bread_page(page,inode->i_dev,nr);
        !           741:        }
        !           742:        i = address + PAGE_SIZE - tsk->end_data;
        !           743:        if (i > PAGE_SIZE-1)
1.1.1.4   root      744:                i = 0;
1.1.1.12! root      745:        tmp = page + PAGE_SIZE;
1.1.1.8   root      746:        while (i--) {
1.1.1.3   root      747:                tmp--;
                    748:                *(char *)tmp = 0;
                    749:        }
1.1.1.12! root      750:        if (put_page(tsk,page,address))
1.1.1.3   root      751:                return;
                    752:        free_page(page);
1.1.1.11  root      753:        oom(current);
1.1.1.2   root      754: }
                    755: 
1.1.1.4   root      756: void show_mem(void)
1.1       root      757: {
1.1.1.12! root      758:        int i,free = 0,total = 0,reserved = 0;
1.1.1.7   root      759:        int shared = 0;
1.1.1.4   root      760: 
1.1.1.12! root      761:        printk("Mem-info:\n");
        !           762:        printk("Free pages:      %6d\n",nr_free_pages);
        !           763:        printk("Secondary pages: %6d\n",nr_secondary_pages);
        !           764:        printk("Buffer heads:    %6d\n",nr_buffer_heads);
        !           765:        printk("Buffer blocks:   %6d\n",nr_buffers);
        !           766:        i = high_memory >> PAGE_SHIFT;
        !           767:        while (i-- > 0) {
1.1.1.4   root      768:                total++;
1.1.1.12! root      769:                if (mem_map[i] & MAP_PAGE_RESERVED)
        !           770:                        reserved++;
        !           771:                else if (!mem_map[i])
1.1.1.4   root      772:                        free++;
                    773:                else
                    774:                        shared += mem_map[i]-1;
                    775:        }
1.1.1.12! root      776:        printk("%d pages of RAM\n",total);
        !           777:        printk("%d free pages\n",free);
        !           778:        printk("%d reserved pages\n",reserved);
        !           779:        printk("%d pages shared\n",shared);
1.1       root      780: }
1.1.1.5   root      781: 
                    782: 
1.1.1.12! root      783: /*
        !           784:  * This routine handles page faults.  It determines the address,
        !           785:  * and the problem, and then passes it off to one of the appropriate
        !           786:  * routines.
        !           787:  */
1.1.1.6   root      788: void do_page_fault(unsigned long *esp, unsigned long error_code)
1.1.1.5   root      789: {
                    790:        unsigned long address;
1.1.1.12! root      791:        unsigned long user_esp = 0;
1.1.1.5   root      792: 
1.1.1.8   root      793:        /* get the address */
1.1.1.6   root      794:        __asm__("movl %%cr2,%0":"=r" (address));
1.1.1.12! root      795:        if (address >= TASK_SIZE) {
        !           796:                printk("Unable to handle kernel paging request at address %08x\n",address);
        !           797:                do_exit(SIGSEGV);
        !           798:        }
        !           799:        if (esp[2] & VM_MASK) {
        !           800:                unsigned int bit;
        !           801: 
        !           802:                bit = (address - 0xA0000) >> PAGE_SHIFT;
        !           803:                if (bit < 32)
        !           804:                        current->screen_bitmap |= 1 << bit;
        !           805:        } else
        !           806:                if ((0xffff & esp[1]) == 0xf)
        !           807:                        user_esp = esp[3];
        !           808:        if (!(error_code & 1))
1.1.1.8   root      809:                do_no_page(error_code, address, current, user_esp);
1.1.1.12! root      810:        else
1.1.1.11  root      811:                do_wp_page(error_code, address, current, user_esp);
1.1.1.5   root      812: }
1.1.1.11  root      813: 
1.1.1.12! root      814: void mem_init(unsigned long start_low_mem,
        !           815:              unsigned long start_mem, unsigned long end_mem)
1.1.1.11  root      816: {
1.1.1.12! root      817:        int codepages = 0;
        !           818:        int reservedpages = 0;
        !           819:        int datapages = 0;
        !           820:        unsigned long tmp;
        !           821:        unsigned short * p;
        !           822: 
        !           823:        cli();
1.1.1.11  root      824:        end_mem &= 0xfffff000;
                    825:        high_memory = end_mem;
1.1.1.12! root      826:        start_mem += 0x0000000f;
        !           827:        start_mem &= 0xfffffff0;
        !           828:        tmp = MAP_NR(end_mem);
        !           829:        mem_map = (unsigned short *) start_mem;
        !           830:        p = mem_map + tmp;
        !           831:        start_mem = (unsigned long) p;
        !           832:        while (p > mem_map)
        !           833:                *--p = MAP_PAGE_RESERVED;
        !           834:        start_low_mem += 0x00000fff;
        !           835:        start_low_mem &= 0xfffff000;
        !           836:        start_mem += 0x00000fff;
1.1.1.11  root      837:        start_mem &= 0xfffff000;
1.1.1.12! root      838:        while (start_low_mem < 0xA0000) {
        !           839:                mem_map[MAP_NR(start_low_mem)] = 0;
        !           840:                start_low_mem += 4096;
        !           841:        }
        !           842:        while (start_mem < end_mem) {
        !           843:                mem_map[MAP_NR(start_mem)] = 0;
        !           844:                start_mem += 4096;
        !           845:        }
        !           846:        free_page_list = 0;
        !           847:        nr_free_pages = 0;
        !           848:        for (tmp = 0 ; tmp < end_mem ; tmp += 4096) {
        !           849:                if (mem_map[MAP_NR(tmp)]) {
        !           850:                        if (tmp < 0xA0000)
        !           851:                                codepages++;
        !           852:                        else if (tmp < 0x100000)
        !           853:                                reservedpages++;
        !           854:                        else
        !           855:                                datapages++;
        !           856:                        continue;
        !           857:                }
        !           858:                *(unsigned long *) tmp = free_page_list;
        !           859:                free_page_list = tmp;
        !           860:                nr_free_pages++;
        !           861:        }
        !           862:        tmp = nr_free_pages << PAGE_SHIFT;
        !           863:        printk("Memory: %dk/%dk available (%dk kernel, %dk reserved, %dk data)\n",
        !           864:                tmp >> 10,
        !           865:                end_mem >> 10,
        !           866:                codepages << 2,
        !           867:                reservedpages << 2,
        !           868:                datapages << 2);
        !           869:        return;
1.1.1.11  root      870: }

unix.superglobalmegacorp.com

This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.