Uses kref for struct page
[akaros.git] / kern / src / pmap.c
1 /* See COPYRIGHT for copyright information. */
2
3 /** @file 
4  * This file is responsible for managing physical pages as they 
5  * are mapped into the page tables of a particular virtual address
6  * space.  The functions defined in this file operate on these
7  * page tables to insert and remove physical pages from them at 
8  * particular virtual addresses.
9  *
10  * @author Kevin Klues <klueska@cs.berkeley.edu>
11  * @author Barret Rhoden <brho@cs.berkeley.edu>
12  */
13
14 #ifdef __SHARC__
15 #pragma nosharc
16 #endif
17
18 #ifdef __DEPUTY__
19 #pragma nodeputy
20 #endif
21
22 #include <arch/arch.h>
23 #include <arch/mmu.h>
24
25 #include <error.h>
26
27 #include <kmalloc.h>
28 #include <atomic.h>
29 #include <string.h>
30 #include <assert.h>
31 #include <pmap.h>
32 #include <kclock.h>
33 #include <process.h>
34 #include <stdio.h>
35 #include <mm.h>
36
37 volatile uint32_t vpt_lock = 0;
38 volatile uint32_t vpd_lock = 0;
39
40 /**
41  * @brief Initialize the array of physical pages and memory free list.
42  *
43  * The 'pages' array has one 'page_t' entry per physical page.
44  * Pages are reference counted, and free pages are kept on a linked list.
45  */
46 void page_init(void)
47 {
48         /*
49      * First, make 'pages' point to an array of size 'npages' of
50          * type 'page_t'.
51          * The kernel uses this structure to keep track of physical pages;
52          * 'npages' equals the number of physical pages in memory.
53          * round up to the nearest page
54          */
55         pages = (page_t*)boot_alloc(npages*sizeof(page_t), PGSIZE);
56         memset(pages, 0, npages*sizeof(page_t));
57
58         /*
59      * Then initilaize everything so pages can start to be alloced and freed
60          * from the memory free list
61          */
62         page_alloc_init();
63
64         static_assert(PROCINFO_NUM_PAGES <= PTSIZE);
65         static_assert(PROCDATA_NUM_PAGES <= PTSIZE);
66 }
67
68 /** 
69  * @brief Map the physical page 'pp' into the virtual address 'va' in page
70  *        directory 'pgdir'
71  *
72  * Map the physical page 'pp' at virtual address 'va'.
73  * The permissions (the low 12 bits) of the page table
74  * entry should be set to 'perm|PTE_P'.
75  * 
76  * Details:
77  *   - If there is already a page mapped at 'va', it is page_remove()d.
78  *   - If necessary, on demand, allocates a page table and inserts it into 
79  *     'pgdir'.
80  *   - page_incref() should be called if the insertion succeeds. 
81  *   - The TLB must be invalidated if a page was formerly present at 'va'.
82  *     (this is handled in page_remove)
83  *
84  * No support for jumbos here.  We will need to be careful when trying to
85  * insert regular pages into something that was already jumbo.  We will
86  * also need to be careful with our overloading of the PTE_PS and 
87  * PTE_PAT flags...
88  *
89  * @param[in] pgdir the page directory to insert the page into
90  * @param[in] pp    a pointr to the page struct representing the
91  *                  physical page that should be inserted.
92  * @param[in] va    the virtual address where the page should be
93  *                  inserted.
94  * @param[in] perm  the permition bits with which to set up the 
95  *                  virtual mapping.
96  *
97  * @return ESUCCESS  on success
98  * @return -ENOMEM   if a page table could not be allocated
99  *                   into which the page should be inserted
100  *
101  */
102 int page_insert(pde_t *pgdir, struct page *page, void *va, int perm) 
103 {
104         pte_t* pte = pgdir_walk(pgdir, va, 1);
105         if (!pte)
106                 return -ENOMEM;
107         /* Two things here:  First, we need to up the ref count of the page we want
108          * to insert in case it is already mapped at va.  In that case we don't want
109          * page_remove to ultimately free it, and then for us to continue as if pp
110          * wasn't freed. (moral = up the ref asap)
111          * Secondly, we need to use the __kref_get() since it is possible that pp
112          * has a refcnt of 0 - which is what happens when you get a fresh page from
113          * the free list (for now). */
114         __kref_get(&page->pg_kref, 1);
115         /* Careful, page remove handles the cases where the page is PAGED_OUT. */
116         if (!PAGE_UNMAPPED(*pte))
117                 page_remove(pgdir, va);
118         *pte = PTE(page2ppn(page), PTE_P | perm);
119         return 0;
120 }
121
122 /**
123  * DEPRECATED - this conflicts with VM regions.
124  *
125  * @brief Map the physical page 'pp' at the first virtual address that is free 
126  * in the range 'vab' to 'vae' in page directory 'pgdir'.
127  *
128  * The permissions (the low 12 bits) of the page table entry get set to 
129  * 'perm|PTE_P'.
130  *
131  * Details:
132  *   - If there is no free entry in the range 'vab' to 'vae' this 
133  *     function returns NULL.
134  *   - If necessary, on demand, this function will allocate a page table 
135  *     and inserts it into 'pgdir'.
136  *   - page_incref() will be called if the insertion succeeds.
137  * 
138  * @param[in] pgdir the page directory to insert the page into
139  * @param[in] pp    a pointr to the page struct representing the
140  *                  physical page that should be inserted.
141  * @param[in] vab   the first virtual address in the range in which the 
142  *                  page can be inserted.
143  * @param[in] vae   the last virtual address in the range in which the 
144  *                  page can be inserted.
145  * @param[in] perm  the permition bits with which to set up the 
146  *                  virtual mapping.
147  *
148  * @return VA   the virtual address where pp has been mapped in the 
149  *              range (vab, vae)
150  * @return NULL no free va in the range (vab, vae) could be found
151  */
152 void* page_insert_in_range(pde_t *pgdir, page_t *pp, 
153                            void *vab, void *vae, int perm) 
154 {
155         pte_t* pte = NULL;
156         void*SNT new_va;
157         
158         for(new_va = vab; new_va <= vae; new_va+= PGSIZE) {
159                 pte = pgdir_walk(pgdir, new_va, 1);
160                 if(pte != NULL && PAGE_UNMAPPED(*pte)) break;
161                 else pte = NULL;
162         }
163         if (!pte) return NULL;
164         *pte = page2pa(pp) | PTE_P | perm;
165         return TC(new_va); // trusted because mapping a page is like allocation
166 }
167
168 /**
169  * @brief Return the page mapped at virtual address 'va' in 
170  * page directory 'pgdir'.
171  *
172  * If pte_store is not NULL, then we store in it the address
173  * of the pte for this page.  This is used by page_remove
174  * but should not be used by other callers.
175  *
176  * For jumbos, right now this returns the first Page* in the 4MB range
177  *
178  * @param[in]  pgdir     the page directory from which we should do the lookup
179  * @param[in]  va        the virtual address of the page we are looking up
180  * @param[out] pte_store the address of the page table entry for the returned page
181  *
182  * @return PAGE the page mapped at virtual address 'va'
183  * @return NULL No mapping exists at virtual address 'va', or it's paged out
184  */
185 page_t *page_lookup(pde_t *pgdir, void *va, pte_t **pte_store)
186 {
187         pte_t* pte = pgdir_walk(pgdir, va, 0);
188         if (!pte || !PAGE_PRESENT(*pte))
189                 return 0;
190         if (pte_store)
191                 *pte_store = pte;
192         return pa2page(PTE_ADDR(*pte));
193 }
194
195 /**
196  * @brief Unmaps the physical page at virtual address 'va' in page directory
197  * 'pgdir'.
198  *
199  * If there is no physical page at that address, this function silently 
200  * does nothing.
201  *
202  * Details:
203  *   - The ref count on the physical page is decrement when the page is removed
204  *   - The physical page is freed if the refcount reaches 0.
205  *   - The pg table entry corresponding to 'va' is set to 0.
206  *     (if such a PTE exists)
207  *   - The TLB is invalidated if an entry is removes from the pg dir/pg table.
208  *
209  * This may be wonky wrt Jumbo pages and decref.  
210  *
211  * @param pgdir the page directory from with the page sholuld be removed
212  * @param va    the virtual address at which the page we are trying to 
213  *              remove is mapped
214  * TODO: consider deprecating this, or at least changing how it works with TLBs.
215  * Might want to have the caller need to manage the TLB.  Also note it is used
216  * in env_user_mem_free, minus the walk. */
217 void page_remove(pde_t *pgdir, void *va)
218 {
219         pte_t *pte;
220         page_t *page;
221
222         pte = pgdir_walk(pgdir,va,0);
223         if (!pte || PAGE_UNMAPPED(*pte))
224                 return;
225
226         if (PAGE_PRESENT(*pte)) {
227                 /* TODO: (TLB) need to do a shootdown, inval sucks.  And might want to
228                  * manage the TLB / free pages differently. (like by the caller).
229                  * Careful about the proc/memory lock here. */
230                 page = ppn2page(PTE2PPN(*pte));
231                 *pte = 0;
232                 tlb_invalidate(pgdir, va);
233                 page_decref(page);
234         } else if (PAGE_PAGED_OUT(*pte)) {
235                 /* TODO: (SWAP) need to free this from the swap */
236                 panic("Swapping not supported!");
237                 *pte = 0;
238         }
239 }
240
241 /**
242  * @brief Invalidate a TLB entry, but only if the page tables being
243  * edited are the ones currently in use by the processor.
244  *
245  * TODO: (TLB) Need to sort this for cross core lovin'
246  *
247  * @param pgdir the page directory assocaited with the tlb entry 
248  *              we are trying to invalidate
249  * @param va    the virtual address associated with the tlb entry
250  *              we are trying to invalidate
251  */
252 void tlb_invalidate(pde_t *pgdir, void *va)
253 {
254         // Flush the entry only if we're modifying the current address space.
255         // For now, there is only one address space, so always invalidate.
256         invlpg(va);
257 }