root/Whitix/trunk/memory/vmm.c

Revision 1848, 10.8 KB (checked in by mwhitworth, 3 years ago)

Fix some Preempt calls.

Line 
1/*  This file is part of Whitix.
2 *
3 *  Whitix is free software; you can redistribute it and/or modify
4 *  it under the terms of the GNU General Public License as published by
5 *  the Free Software Foundation; either version 2 of the License, or
6 *  (at your option) any later version.
7 *
8 *  Whitix is distributed in the hope that it will be useful,
9 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
10 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
11 *  GNU General Public License for more details.
12 *
13 *  You should have received a copy of the GNU General Public License
14 *  along with Whitix; if not, write to the Free Software
15 *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
16 *
17 */
18
19#include <fs/vfs.h>
20#include <vmm.h>
21#include <locks.h>
22#include <llist.h>
23#include <malloc.h>
24#include <module.h>
25#include <typedefs.h>
26#include <i386/i386.h>
27#include <slab.h>
28#include <user_acc.h>
29
30struct Cache* areaCache,*mapCache;
31SYMBOL_EXPORT(areaCache);
32SYMBOL_EXPORT(mapCache);
33
34/***********************************************************************
35 *
36 * FUNCTION: VmLookupAddress
37 *
38 * DESCRIPTION: Look up an address in the process's region list.
39 *
40 * PARAMETERS: process - the process in question.
41 *                         address - the address in question.
42 *
43 * RETURNS: area that the address is contained in.
44 *
45 ***********************************************************************/
46
47struct VMArea* VmLookupAddress(struct Process* process,DWORD address)
48{
49        struct VMArea* curr, *curr2;
50
51        ListForEachEntrySafe(curr, curr2, &process->areaList, list)
52        {
53                /* Ordered by vm area start address, so don't have to search through
54                the whole list if the address has already been passed. */
55                if (curr->start > address)
56                        break;
57               
58                /* Is the address in the virtual mapping? */
59                if (address >= curr->start && address < (curr->start+curr->length))
60                        return curr;
61        }
62
63        return NULL;
64}
65
66SYMBOL_EXPORT(VmLookupAddress);
67
68/***********************************************************************
69 *
70 * FUNCTION: VmLookupPage
71 *
72 * DESCRIPTION: Looks up a page in a vNode by file offset to see if anyone
73 *                              has mapped the page already.
74 *
75 * PARAMETERS: vNode - vNode that contains the shared list.
76 *                         offset - file offset needed.
77 *
78 * RETURNS: the virtual page containing the file data.
79 *
80 ***********************************************************************/
81
82struct VMMapPage* VmLookupPage(struct VNode* vNode, DWORD offset)
83{
84        struct VMMapPage* page = NULL, *curr;
85
86        /* Has anyone else mapped it in? */
87        if (!vNode || vNode->refs == 1 || ListEmpty(&vNode->sharedList))
88                return NULL;
89               
90        PreemptDisable();
91
92        /* Loop through the shared list and return the one with the offset we need. */
93        ListForEachEntry(curr, &vNode->sharedList, list)       
94                if (curr->offset == offset)
95                {
96                        page = curr;
97                        break;
98                }
99               
100        PreemptFastEnable();
101
102        if (page)
103        {
104                PageGet(page->page);
105                VmWaitForPage(page);
106        }
107
108        return page;
109}
110
111SYMBOL_EXPORT(VmLookupPage);
112
113/***********************************************************************
114 *
115 * FUNCTION: VmCreateMappedPage
116 *
117 * DESCRIPTION: Looks up a page in a vNode by file offset.
118 *
119 * PARAMETERS: vNode - vNode that contains the shared list.
120 *                         offset - file offset needed.
121 *
122 * RETURNS: the virtual page containing the file data.
123 *
124 ***********************************************************************/
125
126struct VMMapPage* VmCreateMappedPage(DWORD offset, struct PhysPage* page)
127{
128        struct VMMapPage* mappedPage=(struct VMMapPage*)MemCacheAlloc(mapCache);
129
130        if (!mappedPage)
131                return NULL;
132
133        mappedPage->offset=offset;
134        mappedPage->page=page;
135
136        return mappedPage;
137}
138
139SYMBOL_EXPORT(VmCreateMappedPage);
140
141/***********************************************************************
142 *
143 * FUNCTION: VmFreeMappedPage
144 *
145 * DESCRIPTION: Free a mapped page by physical address.
146 *
147 * PARAMETERS: vNode - VFS node containing the reference to the page.
148 *                         physAddr - physical address of the page.
149 *
150 * RETURNS: Usual error codes.
151 *
152 ***********************************************************************/
153
154int VmFreeMappedPage(struct VNode* vNode, DWORD physAddr)
155{
156        struct VMMapPage* page=NULL, *page2;
157
158        if (!vNode)
159                return -EFAULT;
160
161        /* Remove a physical page from the list, because there are no references and it will be freed */
162
163        ListForEachEntrySafe(page, page2, &vNode->sharedList, list)
164                if (page->page->physAddr == physAddr)
165                {
166                        VmWaitForPage(page);
167                        ListRemove(&page->list);
168                        return 0;
169                }
170       
171        return -ENOENT;
172}
173
174SYMBOL_EXPORT(VmFreeMappedPage);
175
176/***********************************************************************
177 *
178 * FUNCTION:    VmWaitOnPage
179 *
180 * DESCRIPTION: Wait for another thread to stop altering the page.
181 *                              A single waitqueue is shared between a number of pages
182 *                              (one per 4mb+ or so) since there are not many conflicts
183 *                              for a single page - it is very unlikely that a page will
184 *                              be locked and someone else try to access it.
185 *
186 * PARAMETERS:  page - page in question.
187 *
188 * RETURNS:             Nothing.
189 *
190 ***********************************************************************/
191
192static void _VmWaitOnPage(struct VMMapPage* page)
193{
194        WaitQueue* waitQueue;
195
196        PageGet(page->page);
197
198        waitQueue=PageGetWaitQueue(page->page);
199
200        WAIT_ON(waitQueue, !PageLocked(page));
201       
202        PagePut(page->page);
203}
204
205void VmWaitForPage(struct VMMapPage* page)
206{
207        if (PageLocked(page))
208                _VmWaitOnPage(page);
209}
210
211SYMBOL_EXPORT(VmWaitForPage);
212
213/***********************************************************************
214 *
215 * FUNCTION: VmLockPage
216 *
217 * DESCRIPTION: Locks a page to alter data.
218 *
219 * PARAMETERS: page - page in question.
220 *
221 * RETURNS: Nothing.
222 *
223 ***********************************************************************/
224
225void VmLockPage(struct VMMapPage* page)
226{
227        while (BitTestAndSet(&page->flags, PAGE_LOCKED))
228                _VmWaitOnPage(page);
229}
230
231SYMBOL_EXPORT(VmLockPage);
232
233/***********************************************************************
234 *
235 * FUNCTION: VmUnlockPage
236 *
237 * DESCRIPTION: Unlock the page, let any thread access it.
238 *
239 * PARAMETERS: page - page in question.
240 *
241 * RETURNS: Nothing.
242 *
243 ***********************************************************************/
244
245void VmUnlockPage(struct VMMapPage* page)
246{
247        BitClear(&page->flags, PAGE_LOCKED);   
248       
249        /* Wake up one? */
250       
251        WakeUpAll(PageGetWaitQueue(page->page));
252}
253
254SYMBOL_EXPORT(VmUnlockPage);
255
256int VirtCheckArea(void* beginAddr,DWORD size,int mask)
257{
258        DWORD begin=(DWORD)beginAddr;
259        struct VMArea* area,*next;
260
261        /* Don't have to check */
262        if (!size)
263                return 0;
264
265        area=VmLookupAddress(current,begin);
266
267        if (!area)
268                return -EFAULT;
269
270        /* Cannot write to a read-only area */
271        if (!(area->protection & PAGE_RW) && (mask & VER_WRITE))
272                return -EFAULT;
273
274        /* Doesn't overrun */
275        if ((area->start+area->length)-begin >= size)
276                return 0;
277
278        /* Check when size overruns the area */
279        while (1)
280        {
281                /* Assumes all areas are at least readable */
282                if (!(area->protection & PAGE_RW) && (mask & VER_WRITE))
283                        return -EFAULT;
284
285                /* User cannot access kernel memory. */
286                if (!(area->protection & PAGE_USER))
287                        return -EFAULT;
288
289                if ((area->start+area->length)-begin >= size)
290                        break;
291
292                next=ListEntry(area->list.next,struct VMArea,list);
293                if (area->list.next == &current->areaList || next->start != area->start+area->length) /* Does next even exist? */
294                        return -EFAULT;
295
296                area=next;
297        }
298
299        return 0;
300}
301
302SYMBOL_EXPORT(VirtCheckArea);
303
304/***********************************************************************
305 *
306 * FUNCTION: VmHandleNoPage
307 *
308 * DESCRIPTION: Handle a page not present error, map in a page to satisfy
309 *                              it.
310 *
311 * PARAMETERS: area - memory area that processor faulted in.
312 *                         address - memory address that processor faulted at.
313 *
314 * RETURNS: -EFAULT if page could not be mapped in, 0 otherwise.
315 *
316 ***********************************************************************/
317
318int VmHandleNoPage(struct VMArea* area, DWORD address)
319{
320        DWORD offset=(address-area->start)+area->offset;
321        struct VMMapPage* mappedPage;
322        struct PhysPage* newPage;
323        int ret;
324
325        if (area->areaOps && area->areaOps->handleNoPage)
326                if (!area->areaOps->handleNoPage(area, address, offset))
327                        return 0;
328
329        if (area->protection & PAGE_RW)
330        {
331                /* Create private page, unless area is shared */
332
333                if (area->flags & MMAP_SHARED)
334                {
335                        KePrint("address = %#X\n", address);
336                        MachineHalt();
337                }else{
338                        PreemptDisable(); /* Needed? */
339                        newPage=PageAlloc();
340
341                        if (!newPage)
342                        {
343                                PreemptFastEnable();
344                                return -ENOMEM;
345                        }
346
347                        VirtMemMapPage(address, newPage->physAddr, area->protection);
348
349                        PreemptFastEnable();
350
351                        if (area->vNode->fileOps->mMap)
352                                ret=area->vNode->fileOps->mMap(area->vNode, address, offset);
353                        else
354                                ret=FileGenericReadPage(address, area->vNode, offset);
355
356                        if (ret)
357                                goto fail;
358                }
359        }else{
360                mappedPage=VmLookupPage(area->vNode, offset);
361
362                if (!mappedPage)
363                {
364                        /* So no luck finding it in the VNode's mapping list - read it in */
365                        newPage=PageAlloc();
366
367                        if (!newPage)
368                                return -ENOMEM;
369                               
370                        VirtMemMapPage(address, newPage->physAddr,
371                                area->protection | PAGE_ACCESSED | PAGE_SHARED);
372
373                        /* Add to shared list so other read-only pages can map this page */
374                        mappedPage=VmCreateMappedPage(offset, newPage);
375
376                        if (!mappedPage)
377                                return -EFAULT;
378
379                        VmLockPage(mappedPage);
380                       
381                        ListAdd(&mappedPage->list, &area->vNode->sharedList);
382                       
383                        if (area->vNode->fileOps->mMap)
384                                ret=area->vNode->fileOps->mMap(area->vNode, address, offset);
385                        else
386                                ret=FileGenericReadPage(address,area->vNode,offset);
387
388                        VmUnlockPage(mappedPage);
389
390                        if (ret)
391                                return ret;
392                }else{
393                        /* We don't wait or get the page here, because VmLookupPage already
394                         * did that. */
395                        VirtMemMapPage(address, mappedPage->page->physAddr,
396                                area->protection | PAGE_ACCESSED | PAGE_SHARED);
397                }
398        }
399
400        return 0;
401
402fail:
403        /* Failed to read in the page, and cannot continue */
404        KePrint("VmHandleNoPage failed: killing process\n");
405        /* Not reached */
406        return -EIO;
407}
408
409/***********************************************************************
410 *
411 * FUNCTION: VmProtFault
412 *
413 * DESCRIPTION: A protection err (writing to a read-only area for example).
414 *                              Fatal error for the thread in question, print some debug
415 *                              information.
416 *
417 * PARAMETERS: area - memory area that processor faulted in.
418 *                         address - memory address that processor faulted at.
419 *
420 * RETURNS: -EFAULT always - picked up by upper layers and thread/process
421 *                      killed.
422 *
423 ***********************************************************************/
424
425int VmProtFault(struct VMArea* area,DWORD address)
426{
427        KePrint("Protection fault at %#X, area = %#X, area->start = %#X, area->end = %#X (protection = %d)",
428                address,area,area->start,area->start+area->length, area->protection);
429
430        return -EFAULT;
431}
432
433int VmInit()
434{
435        /* Create caches */
436        areaCache=MemCacheCreate("VmAreas", sizeof(struct VMArea), NULL, NULL, 0);
437        mapCache=MemCacheCreate("MappedPages", sizeof(struct VMMapPage), NULL, NULL, 0);
438
439        if (!areaCache || !mapCache)
440                return -ENOMEM; /* TODO: Panic. */
441
442        return 0;
443}
Note: See TracBrowser for help on using the browser.