root / Whitix / branches / hybrid / memory / vmm.c

Revision 539, 7.3 kB (checked in by mwhitworth, 3 months ago)

Add virtual memory functions as a separate module.

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 <llist.h>
22#include <malloc.h>
23#include <module.h>
24#include <typedefs.h>
25#include <i386/i386.h>
26#include <slab.h>
27#include <user_acc.h>
28
29struct Cache* areaCache,*mapCache;
30SYMBOL_EXPORT(areaCache);
31SYMBOL_EXPORT(mapCache);
32
33/***********************************************************************
34 *
35 * FUNCTION: VmLookupAddress
36 *
37 * DESCRIPTION: Look up an address in the process's region list.
38 *
39 * PARAMETERS: process - the process in question.
40 *                         address - the address in question.
41 *
42 * RETURNS: area that the address is contained in.
43 *
44 ***********************************************************************/
45
46struct VMArea* VmLookupAddress(struct Process* process,DWORD address)
47{
48        struct VMArea* curr;
49
50        ListForEachEntry(curr,&process->areaList,list)
51        {
52                /* Ordered by vm area start address, so don't have to search through
53                the whole list if the address has already been passed. */
54                if (curr->start > address)
55                        break;
56
57                /* Is the address in the virtual mapping? */
58                if (address >= curr->start && address < (curr->start+curr->length))
59                        return curr;
60        }
61
62        return NULL;
63}
64
65SYMBOL_EXPORT(VmLookupAddress);
66
67/***********************************************************************
68 *
69 * FUNCTION: VmLookupPage
70 *
71 * DESCRIPTION: Looks up a page in a vNode by file offset to see if anyone
72 *                              has mapped the page already.
73 *
74 * PARAMETERS: vNode - vNode that contains the shared list.
75 *                         offset - file offset needed.
76 *
77 * RETURNS: the virtual page containing the file data.
78 *
79 ***********************************************************************/
80
81struct VMMapPage* VmLookupPage(struct VNode* vNode,DWORD offset)
82{
83        struct VMMapPage* page;
84
85        /* Has anyone else mapped it in? */
86        if (!vNode || vNode->refs == 1 || ListEmpty(&vNode->sharedList))
87                return NULL;
88
89        /* Loop through the shared list and return the one with the offset we need. */
90        ListForEachEntry(page,&vNode->sharedList,list) 
91                if (page->offset == offset)
92                        return page;
93
94        /* Not found - it will be mapped in and added to the shared list */
95        return NULL;
96}
97
98SYMBOL_EXPORT(VmLookupPage);
99
100/***********************************************************************
101 *
102 * FUNCTION: VmCreateMappedPage
103 *
104 * DESCRIPTION: Looks up a page in a vNode by file offset.
105 *
106 * PARAMETERS: vNode - vNode that contains the shared list.
107 *                         offset - file offset needed.
108 *
109 * RETURNS: the virtual page containing the file data.
110 *
111 ***********************************************************************/
112
113struct VMMapPage* VmCreateMappedPage(DWORD offset,struct PhysPage* page)
114{
115        struct VMMapPage* mappedPage=(struct VMMapPage*)MemCacheAlloc(mapCache);
116
117        if (!mappedPage)
118                return NULL;
119
120        mappedPage->offset=offset;
121        mappedPage->page=page;
122
123        return mappedPage;
124}
125
126SYMBOL_EXPORT(VmCreateMappedPage);
127
128/***********************************************************************
129 *
130 * FUNCTION: VmFreeMappedPage
131 *
132 * DESCRIPTION: Free a mapped page by physical address.
133 *
134 * PARAMETERS: vNode - VFS node containing the reference to the page.
135 *                         physAddr - physical address of the page.
136 *
137 * RETURNS: Usual error codes.
138 *
139 ***********************************************************************/
140
141int VmFreeMappedPage(struct VNode* vNode,DWORD physAddr)
142{
143        struct VMMapPage* page=NULL;
144
145        if (!vNode)
146                return -EFAULT;
147
148        /* Remove a physical page from the list, because there are no references and it will be freed */
149
150        ListForEachEntry(page,&vNode->sharedList,list)
151                if (page->page->physAddr == physAddr)
152                {
153                        ListRemove(&page->list);
154                        return 0;
155                }
156
157        return -ENOENT;
158}
159
160SYMBOL_EXPORT(VmFreeMappedPage);
161
162/***********************************************************************
163 *
164 * FUNCTION:    VmWaitOnPage
165 *
166 * DESCRIPTION: Wait for another thread to stop altering the page.
167 *                              A single waitqueue is shared between a number of pages
168 *                              (one per 4mb+ or so) since there are not many conflicts
169 *                              for a single page - it is very unlikely that a page will
170 *                              be locked and someone else try to access it.
171 *
172 * PARAMETERS:  page - page in question.
173 *
174 * RETURNS:             Nothing.
175 *
176 ***********************************************************************/
177
178void VmWaitOnPage(struct VMMapPage* page)
179{
180        WaitQueue* waitQueue;
181       
182        if (!(page->flags & MPAGE_BUSY))
183                return;
184
185        waitQueue=PageGetWaitQueue(page->page);
186
187        if (UNLIKELY(page->flags & MPAGE_BUSY))
188                WAIT_ON(*waitQueue,!(page->flags & MPAGE_BUSY));
189}
190
191SYMBOL_EXPORT(VmWaitOnPage);
192
193/***********************************************************************
194 *
195 * FUNCTION: VmLockPage
196 *
197 * DESCRIPTION: Locks a page to alter data.
198 *
199 * PARAMETERS: page - page in question.
200 *
201 * RETURNS: Nothing.
202 *
203 ***********************************************************************/
204
205void VmLockPage(struct VMMapPage* page)
206{
207        VmWaitOnPage(page);
208        page->flags |= MPAGE_BUSY;
209}
210
211SYMBOL_EXPORT(VmLockPage);
212
213/***********************************************************************
214 *
215 * FUNCTION: VmUnlockPage
216 *
217 * DESCRIPTION: Unlock the page, let any thread access it.
218 *
219 * PARAMETERS: page - page in question.
220 *
221 * RETURNS: Nothing.
222 *
223 ***********************************************************************/
224
225void VmUnlockPage(struct VMMapPage* page)
226{
227        page->flags &= ~MPAGE_BUSY;
228        WakeUp(PageGetWaitQueue(page->page));
229}
230
231SYMBOL_EXPORT(VmUnlockPage);
232
233int VirtCheckArea(void* beginAddr,DWORD size,int mask)
234{
235        DWORD begin=(DWORD)beginAddr;
236        struct VMArea* area,*next;
237
238        /* Don't have to check */
239        if (!size)
240                return 0;
241
242        area=VmLookupAddress(current,begin);
243
244        if (!area)
245                return -EFAULT;
246
247        /* Cannot write to a read-only area */
248        if (!(area->protection & PAGE_RW) && (mask & VER_WRITE))
249                return -EFAULT;
250
251        /* Doesn't overrun */
252        if ((area->start+area->length)-begin >= size)
253                return 0;
254
255        /* Check when size overruns the area */
256        while (1)
257        {
258                /* Assumes all areas are at least readable */
259                if (!(area->protection & PAGE_RW) && (mask & VER_WRITE))
260                        return -EFAULT;
261
262                /* Cannot let the user let the kernel write to it's own pages */
263                if (!(area->protection & PAGE_USER))
264                        return -EFAULT;
265
266                if ((area->start+area->length)-begin >= size)
267                        break;
268
269                next=ListEntry(area->list.next,struct VMArea,list);
270                if (area->list.next == &current->areaList || next->start == area->start+area->length) /* Does next even exist? */
271                        return -EFAULT;
272
273                area=next;
274        }
275
276        return 0;
277}
278
279SYMBOL_EXPORT(VirtCheckArea);
280
281int VmInit()
282{
283        /* Create caches */
284        areaCache=MemCacheCreate("VMArea Cache",sizeof(struct VMArea),NULL,NULL,0);
285        mapCache=MemCacheCreate("Mapped Page Cache",sizeof(struct VMMapPage),NULL,NULL,0);
286
287        if (!areaCache || !mapCache)
288                return -ENOMEM; /* TODO: Panic. */
289
290        return 0;
291}
292
293ModuleInit(VmInit);
Note: See TracBrowser for help on using the browser.