root/Whitix/trunk/memory/mmap.c

Revision 2033, 16.3 KB (checked in by mwhitworth, 3 years ago)

Add stubs for memory protection functions/cases.

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#include <sys.h>
29#include <print.h>
30#include <preempt.h>
31
32extern struct Cache* areaCache,*mapCache;
33
34/* FIXME: Move more functions into vmm.c */
35
36int MmapHandleFault(struct Process* process, DWORD address, int error)
37{
38        struct VMArea* area;
39
40        address=PAGE_ALIGN(address);
41
42        /* A null pointer has been deferenced */
43        if (!process || address < PAGE_SIZE)
44                return -EFAULT;
45
46        area = VmLookupAddress(process, address);
47       
48        /* Area of memory not currently mapped? */
49        if (!area)
50                return -EFAULT;
51
52        /* A few sanity checks here */
53
54        /* Can't write to a read-only page (although processor doesn't know that - page hasn't been mapped in yet */
55        if (!(area->protection & PAGE_RW) && (error & VM_WRITE))
56                return -EFAULT;
57
58        /* Can't let the user write to a kernel region */
59        if (!(area->protection & PAGE_USER) && (error & VM_USER))
60                return -EFAULT;
61
62        if (error & VM_PROTECTION)
63                return VmProtFault(area,address);
64        else
65                return VmHandleNoPage(area, address);
66}
67
68/***********************************************************************
69 *
70 * FUNCTION:    MMapAddArea
71 *
72 * DESCRIPTION: Add an area to the process's area list. The list is
73 *                              sorted by start address, as this makes it much easier
74 *                              for MMapFindAddress.
75 *
76 * PARAMETERS:  process - process in question.
77 *                              area - area to be added.
78 *
79 * RETURNS:             Nothing.
80 *
81 ***********************************************************************/
82
83static void MMapAddArea(struct Process* process, struct VMArea* area)
84{
85        if (ListEmpty(&process->areaList))
86                ListAdd(&area->list,&process->areaList);
87        else
88        {
89                struct VMArea* curr=NULL;
90
91                ListForEachEntry(curr,&process->areaList,list)
92                {
93                        if (curr->start > area->start)
94                                break;
95                }
96
97                DoListAdd(&area->list,curr->list.prev,&curr->list);
98        }
99}
100
101/***********************************************************************
102 *
103 * FUNCTION: MMapRemoveArea
104 *
105 * DESCRIPTION: Remove a memory mapped area.
106 *
107 * PARAMETERS: area - the memory area to be removed.
108 *
109 * RETURNS: Nothing.
110 *
111 ***********************************************************************/
112
113void MMapRemoveArea(struct VMArea* area)
114{
115        ListRemove(&area->list);
116        VNodeRelease(area->vNode);
117        MemCacheFree(areaCache,(void*)area);
118}
119
120/***********************************************************************
121 *
122 * FUNCTION: MMapFreePage
123 *
124 * DESCRIPTION: Free a virtual page. Get the virtual page in question (if
125 *                              it is indeed present) and PutPage it. It does not unmap the
126 *                              page however.
127 *
128 * PARAMETERS: area - memory mapped area containing page.
129 *                         virt - virtual address of page.
130 *
131 * RETURNS: Nothing.
132 *
133 ***********************************************************************/
134       
135void MMapFreePage(DWORD virt)
136{
137        struct PhysPage* page;
138        struct VMArea* area;
139
140//      KePrint("%s, %d: MMapFreePage(%#X)\n", current->name, current->pid, virt);
141//      SymbolPrint(__builtin_return_address(2));
142        return;
143
144        if (PAGE_IS_PRESENT(virt))
145        {
146                DWORD pageAddr;
147               
148                pageAddr=VirtToPhys(virt);
149                page=PageGetStruct(pageAddr);
150
151                if (page->refs == 1)
152                {
153                        area = VmLookupAddress(current, virt);
154                       
155                        if (area)
156                                VmFreeMappedPage(area->vNode, page->physAddr);
157                }
158
159                if (page->refs <= 0)
160                {
161                        KePrint("%#X, %#X: page->physAddr = %#X (refs = %d) pid = %d\n",
162                                virt, page, page->physAddr, page->refs, current->pid);
163                        cli(); hlt();
164                }
165       
166                PagePut(page);
167        }
168}
169
170void MMapFreePages(DWORD start, DWORD end)
171{
172        DWORD i;
173       
174        for (i = PAGE_ALIGN(start); i < PAGE_ALIGN_UP(end); i += PAGE_SIZE)
175                MMapFreePage(i);
176}
177
178/***********************************************************************
179 *
180 * FUNCTION: MMapProcessRemove
181 *
182 * DESCRIPTION: Remove all memory areas linked to a process.
183 *
184 * PARAMETERS: process - process in question.  No need to VirtUnmapPhysRange
185 *                         - as the whole address space will be destroyed soon.
186 *
187 * RETURNS: Nothing.
188 *
189 ***********************************************************************/
190
191void MmapProcessRemove(struct Process* process)
192{
193        struct VMArea* curr, *curr2;
194        DWORD i;
195        DWORD flags;
196
197        IrqSaveFlags(flags);
198
199        ListForEachEntrySafe(curr, curr2, &process->areaList, list)
200        {
201                for (i=curr->start; i< curr->start+curr->length; i+=PAGE_SIZE)
202                        MMapFreePage(i);
203
204                MMapRemoveArea(curr);
205        }
206
207        IrqRestoreFlags(flags);
208}
209
210SYMBOL_EXPORT(MmapProcessRemove);
211
212/***********************************************************************
213 *
214 * FUNCTION: MMapFindAddress
215 *
216 * DESCRIPTION: Find a space in memory for an area of a certain length.
217 *
218 * PARAMETERS: process - process in question.
219 *                         length - length needed.
220 *
221 * RETURNS: Address of suitable area. 0 is returned on error.
222 *
223 ***********************************************************************/
224
225DWORD MMapFindAddress(struct Process* process,DWORD length)
226{
227        struct VMArea* curr;
228        DWORD mapStart=MMAP_BASE,mapEnd;
229
230        length=PAGE_ALIGN_UP(length);
231
232        ListForEachEntry(curr,&process->areaList,list)
233        {
234                mapEnd=curr->start;
235
236                if (mapStart <= mapEnd && (mapEnd-mapStart) >= length)
237                        return mapStart;
238
239                mapStart=curr->start+PAGE_ALIGN_UP(curr->length);
240        }
241
242        /* Got to the end, and found no suitable area */
243        if (UNLIKELY(mapStart == MMAP_END))
244                return 0;
245
246        return mapStart;
247}
248
249/***********************************************************************
250 *
251 * FUNCTION:    MMapMerge
252 *
253 * DESCRIPTION: Merge two memory areas together, if they can indeed be
254 *                              merged.
255 *
256 * PARAMETERS:  first - the first memory area.
257 *                              second - the second memory area.
258 *                              anon - is the area mapped to the Zero device?
259 *
260 * RETURNS:             Nothing.
261 *
262 ***********************************************************************/
263
264void MMapMerge(struct VMArea* first, struct VMArea* second)
265{
266        if (first->start+first->length != second->start)
267                return;
268
269        if (first->vNode != second->vNode)
270                return;
271
272        if (first->areaOps != second->areaOps)
273                return;
274
275        if (first->flags != second->flags)
276                return;
277               
278        if (first->protection != second->protection)
279                return;
280
281        /* Check offsets */
282        if (!(first->flags & MMAP_ANON) && second->offset != first->offset+first->length)
283                return;
284
285        /* Ok to merge now */
286        first->length+=second->length;
287        MMapRemoveArea(second);
288}
289
290void MMapMergeMappings(struct Process* process, struct VMArea* area)
291{
292        /* Get neighbours of this area (in terms of memory), and see whether they are
293           compatible with each other */
294        struct VMArea* prev,*next;
295
296        prev=ListEntry(area->list.prev,struct VMArea,list);
297        next=ListEntry(area->list.next,struct VMArea,list);
298
299        if (area->list.next != &process->areaList)
300                MMapMerge(area, next);
301
302        if (area->list.prev != &process->areaList)
303                MMapMerge(prev, area);
304}
305
306/***********************************************************************
307 *
308 * FUNCTION: MMapDo
309 *
310 * DESCRIPTION: Add a memory mapping to the process's map list.
311 *
312 * PARAMETERS: process - process in question.
313 *                         vNode - VFS node to map to.
314 *                         address - address to start mapping at.
315 *                         length - length of mapping.
316 *                         protection - protection of the mapping.
317 *                         offset - offset in VFS node.
318 *                         flags - see vmm.h
319 *
320 * RETURNS: Address if success, 0 if failure.
321 *
322 ***********************************************************************/
323
324/* FIXME: Split this function up. Eight parameters is bad design. */
325
326DWORD MMapDo(struct Process* process,struct VNode* vNode,DWORD address,DWORD length,int protection,DWORD offset,int flags, struct VmAreaOps* ops)
327{
328        struct VMArea* area=(struct VMArea*)MemCacheAlloc(areaCache);
329
330        if (!process || !length || !area)
331                return 0;
332               
333        length = PAGE_ALIGN_UP(length);
334       
335        if (!(flags & MMAP_FIXED))
336                address=MMapFindAddress(process, length);
337
338        address = PAGE_ALIGN(address);
339
340        if (address < MMAP_BASE || address >= MMAP_END)
341                return 0;
342
343        if (vNode)
344        {
345                /* Can't allow a area mapped to a file to grow up or down */
346                if (flags & (MMAP_GROWDOWN))
347                        return 0;
348
349                if (!(vNode->mode & VFS_ATTR_FILE))
350                        return 0;
351
352        }else if (!vNode)
353        {
354                /* Map to the zero file, so we can use the existing infrastructure. */
355                if (NameToVNode(&vNode,DEVICES_PATH "Special/Zero",0, NULL))
356                        return 0;
357
358                flags |= MMAP_ANON;
359        }
360
361        /* Fill in the new structure */
362        vNode->refs++;
363        area->vNode=vNode;
364        area->start=address;
365        area->length=length;
366        area->offset=offset;
367        area->process=process;
368        area->protection=protection;
369        area->flags=flags;
370        area->areaOps=ops;
371
372        MMapUnmap(process, address, length);   
373
374        MMapAddArea(process,area);
375        MMapMergeMappings(process, area);
376
377        return address;
378}
379
380SYMBOL_EXPORT(MMapDo);
381
382void MMapUnmapArea(struct Process* process,struct VMArea* area,DWORD start,DWORD len)
383{
384        DWORD end=start+len;
385        struct VMArea* newArea;
386       
387        /* Just unmapping the whole area */
388        if (area->start == start && area->length == len)
389                return;
390       
391        if (start >= area->start && end == area->start+area->length)
392        {
393                area->length-=((area->start+area->length)-start);
394        }else if (start == area->start && end <= area->start+area->length)
395        {
396                area->offset+=(end-area->start);
397                area->start=end;
398        }else if (start > area->start && end < area->start+area->length)
399        {
400                newArea=(struct VMArea*)MemCacheAlloc(areaCache);
401                if (!newArea)
402                        return;
403
404                memcpy(newArea,area,sizeof(struct VMArea));
405                newArea->offset+=(end-area->start);
406                newArea->start=end;
407                newArea->vNode->refs++;
408                area->length=end-area->start;
409                MMapAddArea(process,newArea);
410        }
411
412        newArea=(struct VMArea*)MemCacheAlloc(areaCache);
413       
414        if (!newArea)
415                return;
416
417        memcpy(newArea,area,sizeof(struct VMArea));
418
419        MMapAddArea(process,newArea);
420}
421
422/***********************************************************************
423 *
424 * FUNCTION: MMapUnmap
425 *
426 * DESCRIPTION: Unmap a whole or part of a mapping given an address and
427 *                              length.
428 *
429 * PARAMETERS: process - process in question.
430 *                         start - start address of unmapping.
431 *                         len - length of the mapping.
432 *
433 * RETURNS: Usual error codes.
434 *
435 ***********************************************************************/
436
437int MMapUnmap(struct Process* process,DWORD start, DWORD len)
438{
439        struct VMArea* area,*next;
440        struct ListHead* lNext;
441        DWORD currLen=len;
442        DWORD end;
443        int err=0;
444        DWORD flags;
445       
446        IrqSaveFlags(flags);
447       
448        /* Several sanity checks */
449        if (PAGE_OFFSET(start) || start > MMAP_END || len > MMAP_END-start || !len)
450        {
451                err = -EINVAL;
452                goto error;
453        }
454
455        area=VmLookupAddress(process, start);
456
457        if (!area)
458        {
459                err = -EFAULT;
460                goto error;
461        }
462                       
463        end = start + len;
464       
465        if (area->start >= end)
466        {
467                err = 0;
468                goto error;
469        }
470
471        while (currLen)
472        {
473                DWORD currStart=(start < area->start) ? area->start : start;
474                end=start+len;
475                end=(end > area->start+area->length) ? area->start+area->length : end;
476
477                lNext = &area->list.next;
478                next=ListEntry(area->list.next, struct VMArea, list);
479
480                /* TODO: Call MMapFreeArea? Check vNode refs are handled properly. */
481                ListRemove(&area->list);
482
483                MMapUnmapArea(process, area, currStart, end-currStart);
484
485                MemCacheFree(areaCache, area);
486
487                if (lNext == &process->areaList && currLen > 0)
488                {
489                        err=-EINVAL;
490                        goto error;
491                }
492               
493                area = next;
494
495                currLen-=(end-start);
496        }
497       
498        MMapFreePages(start, end);
499
500error:
501        IrqRestoreFlags(flags);
502        return err;
503}
504
505/***********************************************************************
506 *
507 * FUNCTION: SysMemoryMap
508 *
509 * DESCRIPTION: The system call to map memory.
510 *
511 * PARAMETERS: address - address to map to.
512 *                         length - length of mapping.
513 *                         protection - protection for each page of memory.
514 *                         fd - file descriptor to map to.
515 *                         offset - offset in file.
516 *                         flags - general SysMemoryMap flags.
517 *
518 * RETURNS : address that memory was mapped to.
519 *
520 ***********************************************************************/
521
522DWORD SysMemoryMap(DWORD address,DWORD length,int protection,int fd,DWORD offset,int flags)
523{
524        struct File* file=NULL;
525        struct VNode* vNode=NULL;
526       
527        if (fd != -1 && !(file=FileGet(fd)))
528                return 0;
529
530        if (file)
531                vNode=file->vNode;
532                       
533        return MMapDo(current, vNode, PAGE_ALIGN(address),
534        PAGE_ALIGN_UP(length), protection | PAGE_USER, offset, flags, NULL);
535}
536
537int MMapProtectionFixStart(struct VMArea* area, DWORD end, int protection)
538{
539        struct VMArea* areaNext;
540       
541        areaNext = (struct VMArea*)MemCacheAlloc(areaCache);
542       
543        memcpy(areaNext, area, sizeof(struct VMArea));
544       
545        area->length = end - area->start;
546        area->protection = protection;
547       
548        /* VNode refs? */
549       
550        areaNext->offset += (end - areaNext->start) >> PAGE_SHIFT;
551        areaNext->start = end;
552        MMapAddArea(current, areaNext);
553       
554        return 0;
555}
556
557int MMapProtectionFixAll(struct VMArea* area, int protection)
558{
559        area->protection=protection;
560        return 0;
561}
562
563int MMapProtectionFixEnd(struct VMArea* area, DWORD start, int protection)
564{
565        KePrint("MMapProtectionFixEnd\n");
566        return 0;
567}
568
569int MMapProtectionFixMiddle(struct VMArea* area, DWORD start, DWORD end, int protection)
570{
571        KePrint("MMapProtectionFixMiddle\n");
572        return 0;
573}
574
575int MMapProtectionChange(struct VMArea* area, DWORD start, DWORD end, int protection)
576{
577        int error=-ENOTIMPL;
578
579        if (protection ==  area->protection)
580                return 0;
581
582        if (start == area->start)
583        {
584                if (end == area->start+area->length)
585                        error=MMapProtectionFixAll(area, protection);
586                else
587                        error = MMapProtectionFixStart(area, end, protection);
588        }else if (end == area->start + area->length)
589        {
590                error = MMapProtectionFixEnd(area, start, protection);
591        }else{
592                error = MMapProtectionFixMiddle(area, start, end, protection);
593        }
594
595        if (error)
596                return error;
597
598        VirtChangeProtection(start, end, protection);
599
600        return 0;
601}
602
603int SysMemoryProtect(DWORD start, size_t length, int protection)
604{
605        int error=-EINVAL;
606        size_t end;
607        size_t address;
608        struct VMArea* area;
609       
610        protection |= PAGE_USER;
611
612        PreemptDisable();
613
614        /* Can only deal with changing the protection of whole pages. */
615        if (PAGE_OFFSET(start))
616                goto out;
617
618        length=PAGE_ALIGN_UP(length);
619        end=start+length;
620
621        if (end < start)
622                goto out;
623
624        /* Check protection bits. */
625
626        error=0;
627
628        /* A zero length is valid, but useless. */
629        if (start == end)
630                goto out;
631
632        area=VmLookupAddress(current, start);
633
634        error=-EFAULT;
635
636        if (!area)
637                goto out;
638
639        /* The change of protection may extend over one or more mappings. */
640        address=start;
641
642        while (1)
643        {
644                if ((area->start + area->length) >= end)
645                {
646                        error=MMapProtectionChange(area, address, end, protection);
647                        break;
648                }
649
650                KePrint("SysMemoryProtect: todo\n");
651        }
652
653out:
654        PreemptEnable();
655        return error;
656}
657
658/***********************************************************************
659 *
660 * FUNCTION: SysMemoryUnmap
661 *
662 * DESCRIPTION: The system call to unmap memory.
663 *
664 * PARAMETERS: address - base address
665 *                         length - amount of memory to unmap.
666 *
667 * RETURNS: Usual error codes in error.h
668 *
669 ***********************************************************************/
670
671int SysMemoryUnmap(DWORD address,DWORD length)
672{
673        return MMapUnmap(current,address, PAGE_ALIGN_UP(length));
674}
675
676/* FIXME: Legacy system call. Will be removed soon. */
677void* SysMoreCore(int len)
678{
679        len=PAGE_ALIGN(len);
680
681        KePrint("SysMoreCore: legacy. No longer supported\n");
682        return NULL;
683
684        DWORD flags;
685        IrqSaveFlags(flags);
686
687        if (len < 0)
688        {
689                KePrint("TODO: len = %d\n",len);
690                /* TODO: Actually release the memory. Use memoryMap */
691                current->memManager->end=PAGE_ALIGN(current->memManager->end);
692                goto end;
693        }
694
695        if (len > 0)
696                /* MMapDo merges suitable mappings, so it's ok to map multiple times */
697                if (!MMapDo(current, NULL, current->memManager->end, len, PAGE_RW | PAGE_USER | PAGE_PRESENT, 0, MMAP_FIXED | MMAP_PRIVATE, NULL))
698                        return NULL;
699
700end:
701        current->memManager->end+=len;
702        IrqRestoreFlags(flags);
703        return (void*)(current->memManager->end-len);
704}
705
706struct SysCall mmapSysCalls[]=
707{
708        SysEntry(SysMoreCore, sizeof(int)),
709        SysEntry(SysMemoryMap, 24),
710        SysEntry(SysMemoryProtect, 12),
711        SysEntry(SysMemoryUnmap, 8),
712        SysEntryEnd(),
713};
714
715int VmInit();
716
717int MMapInit()
718{
719        VmInit();
720        SysRegisterRange(SYS_MMAP_BASE, mmapSysCalls);
721        return 0;
722}
Note: See TracBrowser for help on using the browser.