root/mm/memory.c

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

DEFINITIONS

This source file includes following definitions.
  1. get_free_page
  2. free_page
  3. free_page_tables
  4. copy_page_tables
  5. put_page
  6. un_wp_page
  7. do_wp_page
  8. write_verify
  9. do_no_page
  10. mem_init
  11. calc_mem

   1 /*
   2  *  linux/mm/memory.c
   3  *
   4  *  (C) 1991  Linus Torvalds
   5  */
   6 
   7 #include <signal.h>
   8 
   9 #include <linux/head.h>
  10 #include <linux/kernel.h>
  11 #include <asm/system.h>
  12 
  13 int do_exit(long code);
  14 
  15 #define invalidate() \
  16 __asm__("movl %%eax,%%cr3"::"a" (0))
  17 
  18 /* these are not to be changed without changing head.s etc */
  19 #define LOW_MEM 0x100000
  20 #define PAGING_MEMORY (15*1024*1024)
  21 #define PAGING_PAGES (PAGING_MEMORY>>12)
  22 #define MAP_NR(addr) (((addr)-LOW_MEM)>>12)
  23 #define USED 100
  24 
  25 static long HIGH_MEMORY = 0;
  26 
  27 #define copy_page(from,to) \
  28 __asm__("cld ; rep ; movsl"::"S" (from),"D" (to),"c" (1024):"cx","di","si")
  29 
  30 static unsigned char mem_map [ PAGING_PAGES ] = {0,};
  31 
  32 /*
  33  * Get physical address of first (actually last :-) free page, and mark it
  34  * used. If no free pages left, return 0.
  35  */
  36 unsigned long get_free_page(void)
     /* [previous][next][first][last][top][bottom][index][help] */
  37 {
  38 register unsigned long __res asm("ax");
  39 
  40 __asm__("std ; repne ; scasb\n\t"
  41         "jne 1f\n\t"
  42         "movb $1,1(%%edi)\n\t"
  43         "sall $12,%%ecx\n\t"
  44         "addl %2,%%ecx\n\t"
  45         "movl %%ecx,%%edx\n\t"
  46         "movl $1024,%%ecx\n\t"
  47         "leal 4092(%%edx),%%edi\n\t"
  48         "rep ; stosl\n\t"
  49         "movl %%edx,%%eax\n"
  50         "1:"
  51         :"=a" (__res)
  52         :"0" (0),"i" (LOW_MEM),"c" (PAGING_PAGES),
  53         "D" (mem_map+PAGING_PAGES-1)
  54         :"di","cx","dx");
  55 return __res;
  56 }
  57 
  58 /*
  59  * Free a page of memory at physical address 'addr'. Used by
  60  * 'free_page_tables()'
  61  */
  62 void free_page(unsigned long addr)
     /* [previous][next][first][last][top][bottom][index][help] */
  63 {
  64         if (addr < LOW_MEM) return;
  65         if (addr > HIGH_MEMORY)
  66                 panic("trying to free nonexistent page");
  67         addr -= LOW_MEM;
  68         addr >>= 12;
  69         if (mem_map[addr]--) return;
  70         mem_map[addr]=0;
  71         panic("trying to free free page");
  72 }
  73 
  74 /*
  75  * This function frees a continuos block of page tables, as needed
  76  * by 'exit()'. As does copy_page_tables(), this handles only 4Mb blocks.
  77  */
  78 int free_page_tables(unsigned long from,unsigned long size)
     /* [previous][next][first][last][top][bottom][index][help] */
  79 {
  80         unsigned long *pg_table;
  81         unsigned long * dir, nr;
  82 
  83         if (from & 0x3fffff)
  84                 panic("free_page_tables called with wrong alignment");
  85         if (!from)
  86                 panic("Trying to free up swapper memory space");
  87         size = (size + 0x3fffff) >> 22;
  88         dir = (unsigned long *) ((from>>20) & 0xffc); /* _pg_dir = 0 */
  89         for ( ; size-->0 ; dir++) {
  90                 if (!(1 & *dir))
  91                         continue;
  92                 pg_table = (unsigned long *) (0xfffff000 & *dir);
  93                 for (nr=0 ; nr<1024 ; nr++) {
  94                         if (1 & *pg_table)
  95                                 free_page(0xfffff000 & *pg_table);
  96                         *pg_table = 0;
  97                         pg_table++;
  98                 }
  99                 free_page(0xfffff000 & *dir);
 100                 *dir = 0;
 101         }
 102         invalidate();
 103         return 0;
 104 }
 105 
 106 /*
 107  *  Well, here is one of the most complicated functions in mm. It
 108  * copies a range of linerar addresses by copying only the pages.
 109  * Let's hope this is bug-free, 'cause this one I don't want to debug :-)
 110  *
 111  * Note! We don't copy just any chunks of memory - addresses have to
 112  * be divisible by 4Mb (one page-directory entry), as this makes the
 113  * function easier. It's used only by fork anyway.
 114  *
 115  * NOTE 2!! When from==0 we are copying kernel space for the first
 116  * fork(). Then we DONT want to copy a full page-directory entry, as
 117  * that would lead to some serious memory waste - we just copy the
 118  * first 160 pages - 640kB. Even that is more than we need, but it
 119  * doesn't take any more memory - we don't copy-on-write in the low
 120  * 1 Mb-range, so the pages can be shared with the kernel. Thus the
 121  * special case for nr=xxxx.
 122  */
 123 int copy_page_tables(unsigned long from,unsigned long to,long size)
     /* [previous][next][first][last][top][bottom][index][help] */
 124 {
 125         unsigned long * from_page_table;
 126         unsigned long * to_page_table;
 127         unsigned long this_page;
 128         unsigned long * from_dir, * to_dir;
 129         unsigned long nr;
 130 
 131         if ((from&0x3fffff) || (to&0x3fffff))
 132                 panic("copy_page_tables called with wrong alignment");
 133         from_dir = (unsigned long *) ((from>>20) & 0xffc); /* _pg_dir = 0 */
 134         to_dir = (unsigned long *) ((to>>20) & 0xffc);
 135         size = ((unsigned) (size+0x3fffff)) >> 22;
 136         for( ; size-->0 ; from_dir++,to_dir++) {
 137                 if (1 & *to_dir)
 138                         panic("copy_page_tables: already exist");
 139                 if (!(1 & *from_dir))
 140                         continue;
 141                 from_page_table = (unsigned long *) (0xfffff000 & *from_dir);
 142                 if (!(to_page_table = (unsigned long *) get_free_page()))
 143                         return -1;      /* Out of memory, see freeing */
 144                 *to_dir = ((unsigned long) to_page_table) | 7;
 145                 nr = (from==0)?0xA0:1024;
 146                 for ( ; nr-- > 0 ; from_page_table++,to_page_table++) {
 147                         this_page = *from_page_table;
 148                         if (!(1 & this_page))
 149                                 continue;
 150                         this_page &= ~2;
 151                         *to_page_table = this_page;
 152                         if (this_page > LOW_MEM) {
 153                                 *from_page_table = this_page;
 154                                 this_page -= LOW_MEM;
 155                                 this_page >>= 12;
 156                                 mem_map[this_page]++;
 157                         }
 158                 }
 159         }
 160         invalidate();
 161         return 0;
 162 }
 163 
 164 /*
 165  * This function puts a page in memory at the wanted address.
 166  * It returns the physical address of the page gotten, 0 if
 167  * out of memory (either when trying to access page-table or
 168  * page.)
 169  */
 170 unsigned long put_page(unsigned long page,unsigned long address)
     /* [previous][next][first][last][top][bottom][index][help] */
 171 {
 172         unsigned long tmp, *page_table;
 173 
 174 /* NOTE !!! This uses the fact that _pg_dir=0 */
 175 
 176         if (page < LOW_MEM || page > HIGH_MEMORY)
 177                 printk("Trying to put page %p at %p\n",page,address);
 178         if (mem_map[(page-LOW_MEM)>>12] != 1)
 179                 printk("mem_map disagrees with %p at %p\n",page,address);
 180         page_table = (unsigned long *) ((address>>20) & 0xffc);
 181         if ((*page_table)&1)
 182                 page_table = (unsigned long *) (0xfffff000 & *page_table);
 183         else {
 184                 if (!(tmp=get_free_page()))
 185                         return 0;
 186                 *page_table = tmp|7;
 187                 page_table = (unsigned long *) tmp;
 188         }
 189         page_table[(address>>12) & 0x3ff] = page | 7;
 190         return page;
 191 }
 192 
 193 void un_wp_page(unsigned long * table_entry)
     /* [previous][next][first][last][top][bottom][index][help] */
 194 {
 195         unsigned long old_page,new_page;
 196 
 197         old_page = 0xfffff000 & *table_entry;
 198         if (old_page >= LOW_MEM && mem_map[MAP_NR(old_page)]==1) {
 199                 *table_entry |= 2;
 200                 return;
 201         }
 202         if (!(new_page=get_free_page()))
 203                 do_exit(SIGSEGV);
 204         if (old_page >= LOW_MEM)
 205                 mem_map[MAP_NR(old_page)]--;
 206         *table_entry = new_page | 7;
 207         copy_page(old_page,new_page);
 208 }       
 209 
 210 /*
 211  * This routine handles present pages, when users try to write
 212  * to a shared page. It is done by copying the page to a new address
 213  * and decrementing the shared-page counter for the old page.
 214  */
 215 void do_wp_page(unsigned long error_code,unsigned long address)
     /* [previous][next][first][last][top][bottom][index][help] */
 216 {
 217         un_wp_page((unsigned long *)
 218                 (((address>>10) & 0xffc) + (0xfffff000 &
 219                 *((unsigned long *) ((address>>20) &0xffc)))));
 220 
 221 }
 222 
 223 void write_verify(unsigned long address)
     /* [previous][next][first][last][top][bottom][index][help] */
 224 {
 225         unsigned long page;
 226 
 227         if (!( (page = *((unsigned long *) ((address>>20) & 0xffc)) )&1))
 228                 return;
 229         page &= 0xfffff000;
 230         page += ((address>>10) & 0xffc);
 231         if ((3 & *(unsigned long *) page) == 1)  /* non-writeable, present */
 232                 un_wp_page((unsigned long *) page);
 233         return;
 234 }
 235 
 236 void do_no_page(unsigned long error_code,unsigned long address)
     /* [previous][next][first][last][top][bottom][index][help] */
 237 {
 238         unsigned long tmp;
 239 
 240         if (tmp=get_free_page())
 241                 if (put_page(tmp,address))
 242                         return;
 243         do_exit(SIGSEGV);
 244 }
 245 
 246 void mem_init(long start_mem, long end_mem)
     /* [previous][next][first][last][top][bottom][index][help] */
 247 {
 248         int i;
 249 
 250         HIGH_MEMORY = end_mem;
 251         for (i=0 ; i<PAGING_PAGES ; i++)
 252                 mem_map[i] = USED;
 253         i = MAP_NR(start_mem);
 254         end_mem -= start_mem;
 255         end_mem >>= 12;
 256         while (end_mem-->0)
 257                 mem_map[i++]=0;
 258 }
 259 
 260 void calc_mem(void)
     /* [previous][next][first][last][top][bottom][index][help] */
 261 {
 262         int i,j,k,free=0;
 263         long * pg_tbl;
 264 
 265         for(i=0 ; i<PAGING_PAGES ; i++)
 266                 if (!mem_map[i]) free++;
 267         printk("%d pages free (of %d)\n\r",free,PAGING_PAGES);
 268         for(i=2 ; i<1024 ; i++) {
 269                 if (1&pg_dir[i]) {
 270                         pg_tbl=(long *) (0xfffff000 & pg_dir[i]);
 271                         for(j=k=0 ; j<1024 ; j++)
 272                                 if (pg_tbl[j]&1)
 273                                         k++;
 274                         printk("Pg-dir[%d] uses %d pages\n",i,k);
 275                 }
 276         }
 277 }

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