• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (c) 2013-2019 Huawei Technologies Co., Ltd. All rights reserved.
3  * Copyright (c) 2020-2021 Huawei Device Co., Ltd. All rights reserved.
4  *
5  * Redistribution and use in source and binary forms, with or without modification,
6  * are permitted provided that the following conditions are met:
7  *
8  * 1. Redistributions of source code must retain the above copyright notice, this list of
9  *    conditions and the following disclaimer.
10  *
11  * 2. Redistributions in binary form must reproduce the above copyright notice, this list
12  *    of conditions and the following disclaimer in the documentation and/or other materials
13  *    provided with the distribution.
14  *
15  * 3. Neither the name of the copyright holder nor the names of its contributors may be used
16  *    to endorse or promote products derived from this software without specific prior written
17  *    permission.
18  *
19  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
20  * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
21  * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
22  * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR
23  * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
24  * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
25  * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
26  * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
27  * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
28  * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
29  * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
30  */
31 
32 /**
33  * @defgroup los_vm_syscall vm syscall definition
34  * @ingroup kernel
35  */
36 
37 #include "los_typedef.h"
38 #include "los_vm_syscall.h"
39 #include "los_vm_common.h"
40 #include "los_rbtree.h"
41 #include "los_vm_map.h"
42 #include "los_vm_dump.h"
43 #include "los_vm_lock.h"
44 #include "los_vm_filemap.h"
45 #include "los_process_pri.h"
46 
47 
48 #ifdef LOSCFG_KERNEL_VM
49 
OsCheckMMapParams(VADDR_T * vaddr,unsigned long flags,size_t len,unsigned long pgoff)50 STATUS_T OsCheckMMapParams(VADDR_T *vaddr, unsigned long flags, size_t len, unsigned long pgoff)
51 {
52     if ((len == 0) || (len > USER_ASPACE_SIZE)) {
53         return -EINVAL;
54     }
55     if (len > OsCurrProcessGet()->vmSpace->mapSize) {
56         return -ENOMEM;
57     }
58 
59     if (((flags & MAP_FIXED) == 0) && ((flags & MAP_FIXED_NOREPLACE) == 0)) {
60         *vaddr = ROUNDUP(*vaddr, PAGE_SIZE);
61         if ((*vaddr != 0) && (!LOS_IsUserAddressRange(*vaddr, len))) {
62             *vaddr = 0;
63         }
64     } else if ((!LOS_IsUserAddressRange(*vaddr, len)) || (!IS_ALIGNED(*vaddr, PAGE_SIZE))) {
65         return -EINVAL;
66     }
67 
68     if ((flags & MAP_SUPPORT_MASK) == 0) {
69         return -EINVAL;
70     }
71     if (((flags & MAP_SHARED_PRIVATE) == 0) || ((flags & MAP_SHARED_PRIVATE) == MAP_SHARED_PRIVATE)) {
72         return -EINVAL;
73     }
74 
75     if (((len >> PAGE_SHIFT) + pgoff) < pgoff) {
76         return -EINVAL;
77     }
78 
79     return LOS_OK;
80 }
81 
OsNamedMmapingPermCheck(struct file * filep,unsigned long flags,unsigned prot)82 STATUS_T OsNamedMmapingPermCheck(struct file *filep, unsigned long flags, unsigned prot)
83 {
84     if (!((unsigned int)filep->f_oflags & O_RDWR) && (((unsigned int)filep->f_oflags & O_ACCMODE) ^ O_RDONLY)) {
85         return -EACCES;
86     }
87     if (flags & MAP_SHARED) {
88         if (((unsigned int)filep->f_oflags & O_APPEND) && (prot & PROT_WRITE)) {
89             return -EACCES;
90         }
91         if ((prot & PROT_WRITE) && !((unsigned int)filep->f_oflags & O_RDWR)) {
92             return -EACCES;
93         }
94     }
95 
96     return LOS_OK;
97 }
98 
OsAnonMMap(LosVmMapRegion * region)99 STATUS_T OsAnonMMap(LosVmMapRegion *region)
100 {
101     LOS_SetRegionTypeAnon(region);
102     return LOS_OK;
103 }
104 
LOS_MMap(VADDR_T vaddr,size_t len,unsigned prot,unsigned long flags,int fd,unsigned long pgoff)105 VADDR_T LOS_MMap(VADDR_T vaddr, size_t len, unsigned prot, unsigned long flags, int fd, unsigned long pgoff)
106 {
107     STATUS_T status;
108     VADDR_T resultVaddr;
109     UINT32 regionFlags;
110     LosVmMapRegion *newRegion = NULL;
111     struct file *filep = NULL;
112     LosVmSpace *vmSpace = OsCurrProcessGet()->vmSpace;
113 
114     len = ROUNDUP(len, PAGE_SIZE);
115     STATUS_T checkRst = OsCheckMMapParams(&vaddr, flags, len, pgoff);
116     if (checkRst != LOS_OK) {
117         return checkRst;
118     }
119 
120     if (LOS_IsNamedMapping(flags)) {
121         status = fs_getfilep(fd, &filep);
122         if (status < 0) {
123             return -EBADF;
124         }
125 
126         status = OsNamedMmapingPermCheck(filep, flags, prot);
127         if (status < 0) {
128             return status;
129         }
130     }
131 
132     (VOID)LOS_MuxAcquire(&vmSpace->regionMux);
133     /* user mode calls mmap to release heap physical memory without releasing heap virtual space */
134     status = OsUserHeapFree(vmSpace, vaddr, len);
135     if (status == LOS_OK) {
136         resultVaddr = vaddr;
137         goto MMAP_DONE;
138     }
139 
140     regionFlags = OsCvtProtFlagsToRegionFlags(prot, flags);
141     newRegion = LOS_RegionAlloc(vmSpace, vaddr, len, regionFlags, pgoff);
142     if (newRegion == NULL) {
143         resultVaddr = (VADDR_T)-ENOMEM;
144         goto MMAP_DONE;
145     }
146     newRegion->regionFlags |= VM_MAP_REGION_FLAG_MMAP;
147     resultVaddr = newRegion->range.base;
148 
149     if (LOS_IsNamedMapping(flags)) {
150         status = OsNamedMMap(filep, newRegion);
151     } else {
152         status = OsAnonMMap(newRegion);
153     }
154 
155     if (status != LOS_OK) {
156         LOS_RbDelNode(&vmSpace->regionRbTree, &newRegion->rbNode);
157         LOS_RegionFree(vmSpace, newRegion);
158         resultVaddr = (VADDR_T)-ENOMEM;
159         goto MMAP_DONE;
160     }
161 
162 MMAP_DONE:
163     (VOID)LOS_MuxRelease(&vmSpace->regionMux);
164     return resultVaddr;
165 }
166 
LOS_UnMMap(VADDR_T addr,size_t size)167 STATUS_T LOS_UnMMap(VADDR_T addr, size_t size)
168 {
169     if ((addr <= 0) || (size == 0)) {
170         return -EINVAL;
171     }
172 
173     return OsUnMMap(OsCurrProcessGet()->vmSpace, addr, size);
174 }
175 
OsProtMprotectPermCheck(unsigned long prot,LosVmMapRegion * region)176 STATIC INLINE BOOL OsProtMprotectPermCheck(unsigned long prot, LosVmMapRegion *region)
177 {
178     UINT32 protFlags = 0;
179     UINT32 permFlags = 0;
180     UINT32 fileFlags = region->unTypeData.rf.f_oflags;
181     permFlags |= ((fileFlags & O_ACCMODE) ^ O_RDONLY) ? 0 : VM_MAP_REGION_FLAG_PERM_READ;
182     permFlags |= (fileFlags & O_WRONLY) ? VM_MAP_REGION_FLAG_PERM_WRITE : 0;
183     permFlags |= (fileFlags & O_RDWR) ? (VM_MAP_REGION_FLAG_PERM_READ | VM_MAP_REGION_FLAG_PERM_WRITE) : 0;
184     protFlags |= (prot & PROT_READ) ? VM_MAP_REGION_FLAG_PERM_READ : 0;
185     protFlags |= (prot & PROT_WRITE) ? VM_MAP_REGION_FLAG_PERM_WRITE : 0;
186 
187     return ((protFlags & permFlags) == protFlags);
188 }
189 
OsShrinkHeap(VOID * addr,LosVmSpace * space)190 VOID *OsShrinkHeap(VOID *addr, LosVmSpace *space)
191 {
192     VADDR_T newBrk, oldBrk;
193 
194     newBrk = LOS_Align((VADDR_T)(UINTPTR)addr, PAGE_SIZE);
195     oldBrk = LOS_Align(space->heapNow, PAGE_SIZE);
196     if (LOS_UnMMap(newBrk, (oldBrk - newBrk)) < 0) {
197         return (void *)(UINTPTR)space->heapNow;
198     }
199     space->heapNow = (VADDR_T)(UINTPTR)addr;
200     return addr;
201 }
202 
LOS_DoBrk(VOID * addr)203 VOID *LOS_DoBrk(VOID *addr)
204 {
205     LosVmSpace *space = OsCurrProcessGet()->vmSpace;
206     size_t size;
207     VOID *ret = NULL;
208     LosVmMapRegion *region = NULL;
209     VOID *alignAddr = NULL;
210     VOID *shrinkAddr = NULL;
211 
212     if (addr == NULL) {
213         return (void *)(UINTPTR)space->heapNow;
214     }
215 
216     if ((UINTPTR)addr < (UINTPTR)space->heapBase) {
217         return (VOID *)-ENOMEM;
218     }
219 
220     size = (UINTPTR)addr - (UINTPTR)space->heapBase;
221     size = ROUNDUP(size, PAGE_SIZE);
222     alignAddr = (CHAR *)(UINTPTR)(space->heapBase) + size;
223     PRINT_INFO("brk addr %p , size 0x%x, alignAddr %p, align %d\n", addr, size, alignAddr, PAGE_SIZE);
224 
225     (VOID)LOS_MuxAcquire(&space->regionMux);
226     if (addr < (VOID *)(UINTPTR)space->heapNow) {
227         shrinkAddr = OsShrinkHeap(addr, space);
228         (VOID)LOS_MuxRelease(&space->regionMux);
229         return shrinkAddr;
230     }
231 
232     if ((UINTPTR)alignAddr >= space->mapBase) {
233         VM_ERR("Process heap memory space is insufficient");
234         ret = (VOID *)-ENOMEM;
235         goto REGION_ALLOC_FAILED;
236     }
237 
238     if (space->heapBase == space->heapNow) {
239         region = LOS_RegionAlloc(space, space->heapBase, size,
240                                  VM_MAP_REGION_FLAG_PERM_READ | VM_MAP_REGION_FLAG_PERM_WRITE |
241                                  VM_MAP_REGION_FLAG_FIXED | VM_MAP_REGION_FLAG_PERM_USER, 0);
242         if (region == NULL) {
243             ret = (VOID *)-ENOMEM;
244             VM_ERR("LOS_RegionAlloc failed");
245             goto REGION_ALLOC_FAILED;
246         }
247         region->regionFlags |= VM_MAP_REGION_FLAG_HEAP;
248         space->heap = region;
249     }
250 
251     space->heapNow = (VADDR_T)(UINTPTR)alignAddr;
252     space->heap->range.size = size;
253     ret = (VOID *)(UINTPTR)space->heapNow;
254 
255 REGION_ALLOC_FAILED:
256     (VOID)LOS_MuxRelease(&space->regionMux);
257     return ret;
258 }
259 
OsInheritOldRegionName(UINT32 oldRegionFlags)260 STATIC UINT32 OsInheritOldRegionName(UINT32 oldRegionFlags)
261 {
262     UINT32 vmFlags = 0;
263 
264     if (oldRegionFlags & VM_MAP_REGION_FLAG_HEAP) {
265         vmFlags |= VM_MAP_REGION_FLAG_HEAP;
266     } else if (oldRegionFlags & VM_MAP_REGION_FLAG_STACK) {
267         vmFlags |= VM_MAP_REGION_FLAG_STACK;
268     } else if (oldRegionFlags & VM_MAP_REGION_FLAG_TEXT) {
269         vmFlags |= VM_MAP_REGION_FLAG_TEXT;
270     } else if (oldRegionFlags & VM_MAP_REGION_FLAG_VDSO) {
271         vmFlags |= VM_MAP_REGION_FLAG_VDSO;
272     } else if (oldRegionFlags & VM_MAP_REGION_FLAG_MMAP) {
273         vmFlags |= VM_MAP_REGION_FLAG_MMAP;
274     } else if (oldRegionFlags & VM_MAP_REGION_FLAG_SHM) {
275         vmFlags |= VM_MAP_REGION_FLAG_SHM;
276     } else if (oldRegionFlags & VM_MAP_REGION_FLAG_LITEIPC) {
277         vmFlags |= VM_MAP_REGION_FLAG_LITEIPC;
278     }
279 
280     return vmFlags;
281 }
282 
LOS_DoMprotect(VADDR_T vaddr,size_t len,unsigned long prot)283 INT32 LOS_DoMprotect(VADDR_T vaddr, size_t len, unsigned long prot)
284 {
285     LosVmSpace *space = OsCurrProcessGet()->vmSpace;
286     LosVmMapRegion *region = NULL;
287     UINT32 vmFlags;
288     UINT32 count;
289     int ret;
290 
291     (VOID)LOS_MuxAcquire(&space->regionMux);
292     region = LOS_RegionFind(space, vaddr);
293     if (!IS_ALIGNED(vaddr, PAGE_SIZE) || (region == NULL) || (vaddr > vaddr + len)) {
294         ret = -EINVAL;
295         goto OUT_MPROTECT;
296     }
297 
298     if ((prot & ~(PROT_READ | PROT_WRITE | PROT_EXEC))) {
299         ret = -EINVAL;
300         goto OUT_MPROTECT;
301     }
302 
303     if ((region->regionFlags & VM_MAP_REGION_FLAG_VDSO) ||
304         (region->regionFlags & VM_MAP_REGION_FLAG_HEAP) ||
305         (region->regionFlags & VM_MAP_REGION_FLAG_LITEIPC)) {
306         ret = -EPERM;
307         goto OUT_MPROTECT;
308     }
309 
310     if (LOS_IsRegionTypeFile(region) && (region->regionFlags & VM_MAP_REGION_FLAG_SHARED)) {
311         if (!OsProtMprotectPermCheck(prot, region)) {
312             ret = -EACCES;
313             goto OUT_MPROTECT;
314         }
315     }
316 
317     len = LOS_Align(len, PAGE_SIZE);
318     /* can't operation cross region */
319     if ((region->range.base + region->range.size) < (vaddr + len)) {
320         ret = -EINVAL;
321         goto OUT_MPROTECT;
322     }
323 
324     /* if only move some part of region, we need to split first */
325     if (region->range.size > len) {
326         OsVmRegionAdjust(space, vaddr, len);
327     }
328 
329     vmFlags = OsCvtProtFlagsToRegionFlags(prot, 0);
330     vmFlags |= (region->regionFlags & VM_MAP_REGION_FLAG_SHARED) ? VM_MAP_REGION_FLAG_SHARED : 0;
331     vmFlags |= OsInheritOldRegionName(region->regionFlags);
332     region = LOS_RegionFind(space, vaddr);
333     if (region == NULL) {
334         ret = -ENOMEM;
335         goto OUT_MPROTECT;
336     }
337     region->regionFlags = vmFlags;
338     count = len >> PAGE_SHIFT;
339     ret = LOS_ArchMmuChangeProt(&space->archMmu, vaddr, count, region->regionFlags);
340     if (ret) {
341         ret = -ENOMEM;
342         goto OUT_MPROTECT;
343     }
344     ret = LOS_OK;
345 
346 OUT_MPROTECT:
347 #ifdef LOSCFG_VM_OVERLAP_CHECK
348     if (VmmAspaceRegionsOverlapCheck(aspace) < 0) {
349         (VOID)OsShellCmdDumpVm(0, NULL);
350         ret = -ENOMEM;
351     }
352 #endif
353 
354     (VOID)LOS_MuxRelease(&space->regionMux);
355     return ret;
356 }
357 
OsMremapCheck(VADDR_T addr,size_t oldLen,VADDR_T newAddr,size_t newLen,unsigned int flags)358 STATUS_T OsMremapCheck(VADDR_T addr, size_t oldLen, VADDR_T newAddr, size_t newLen, unsigned int flags)
359 {
360     LosVmSpace *space = OsCurrProcessGet()->vmSpace;
361     LosVmMapRegion *region = LOS_RegionFind(space, addr);
362     VADDR_T regionEnd;
363 
364     if ((region == NULL) || (region->range.base > addr) || (newLen == 0)) {
365         return -EINVAL;
366     }
367 
368     if (flags & ~(MREMAP_FIXED | MREMAP_MAYMOVE)) {
369         return -EINVAL;
370     }
371 
372     if (((flags & MREMAP_FIXED) == MREMAP_FIXED) && ((flags & MREMAP_MAYMOVE) == 0)) {
373         return -EINVAL;
374     }
375 
376     if (!IS_ALIGNED(addr, PAGE_SIZE)) {
377         return -EINVAL;
378     }
379 
380     regionEnd = region->range.base + region->range.size;
381 
382     /* we can't operate across region */
383     if (oldLen > regionEnd - addr) {
384         return -EFAULT;
385     }
386 
387     /* avoiding overflow */
388     if (newLen > oldLen) {
389         if ((addr + newLen) < addr) {
390             return -EINVAL;
391         }
392     }
393 
394     /* avoid new region overlapping with the old one */
395     if (flags & MREMAP_FIXED) {
396         if (((region->range.base + region->range.size) > newAddr) &&
397             (region->range.base < (newAddr + newLen))) {
398             return -EINVAL;
399         }
400 
401         if (!IS_ALIGNED(newAddr, PAGE_SIZE)) {
402             return -EINVAL;
403         }
404     }
405 
406     return LOS_OK;
407 }
408 
LOS_DoMremap(VADDR_T oldAddress,size_t oldSize,size_t newSize,int flags,VADDR_T newAddr)409 VADDR_T LOS_DoMremap(VADDR_T oldAddress, size_t oldSize, size_t newSize, int flags, VADDR_T newAddr)
410 {
411     LosVmMapRegion *regionOld = NULL;
412     LosVmMapRegion *regionNew = NULL;
413     STATUS_T status;
414     VADDR_T ret;
415     LosVmSpace *space = OsCurrProcessGet()->vmSpace;
416 
417     oldSize = LOS_Align(oldSize, PAGE_SIZE);
418     newSize = LOS_Align(newSize, PAGE_SIZE);
419 
420     (VOID)LOS_MuxAcquire(&space->regionMux);
421 
422     status = OsMremapCheck(oldAddress, oldSize, newAddr, newSize, (unsigned int)flags);
423     if (status) {
424         ret = status;
425         goto OUT_MREMAP;
426     }
427 
428     /* if only move some part of region, we need to split first */
429     status = OsVmRegionAdjust(space, oldAddress, oldSize);
430     if (status) {
431         ret = -ENOMEM;
432         goto OUT_MREMAP;
433     }
434 
435     regionOld = LOS_RegionFind(space, oldAddress);
436     if (regionOld == NULL) {
437         ret = -ENOMEM;
438         goto OUT_MREMAP;
439     }
440 
441     if ((unsigned int)flags & MREMAP_FIXED) {
442         regionNew = OsVmRegionDup(space, regionOld, newAddr, newSize);
443         if (!regionNew) {
444             ret = -ENOMEM;
445             goto OUT_MREMAP;
446         }
447         status = LOS_ArchMmuMove(&space->archMmu, oldAddress, newAddr,
448                                  ((newSize < regionOld->range.size) ? newSize : regionOld->range.size) >> PAGE_SHIFT,
449                                  regionOld->regionFlags);
450         if (status) {
451             LOS_RegionFree(space, regionNew);
452             ret = -ENOMEM;
453             goto OUT_MREMAP;
454         }
455         LOS_RegionFree(space, regionOld);
456         ret = newAddr;
457         goto OUT_MREMAP;
458     }
459     // take it as shrink operation
460     if (oldSize > newSize) {
461         LOS_UnMMap(oldAddress + newSize, oldSize - newSize);
462         ret = oldAddress;
463         goto OUT_MREMAP;
464     }
465     status = OsIsRegionCanExpand(space, regionOld, newSize);
466     // we can expand directly.
467     if (!status) {
468         regionOld->range.size = newSize;
469         ret = oldAddress;
470         goto OUT_MREMAP;
471     }
472 
473     if ((unsigned int)flags & MREMAP_MAYMOVE) {
474         regionNew = OsVmRegionDup(space, regionOld, 0, newSize);
475         if (regionNew  == NULL) {
476             ret = -ENOMEM;
477             goto OUT_MREMAP;
478         }
479         status = LOS_ArchMmuMove(&space->archMmu, oldAddress, regionNew->range.base,
480                                  regionOld->range.size >> PAGE_SHIFT, regionOld->regionFlags);
481         if (status) {
482             LOS_RegionFree(space, regionNew);
483             ret = -ENOMEM;
484             goto OUT_MREMAP;
485         }
486         LOS_RegionFree(space, regionOld);
487         ret = regionNew->range.base;
488         goto OUT_MREMAP;
489     }
490 
491     ret = -EINVAL;
492 OUT_MREMAP:
493 #ifdef LOSCFG_VM_OVERLAP_CHECK
494     if (VmmAspaceRegionsOverlapCheck(aspace) < 0) {
495         (VOID)OsShellCmdDumpVm(0, NULL);
496         ret = -ENOMEM;
497     }
498 #endif
499 
500     (VOID)LOS_MuxRelease(&space->regionMux);
501     return ret;
502 }
503 
LOS_DumpMemRegion(VADDR_T vaddr)504 VOID LOS_DumpMemRegion(VADDR_T vaddr)
505 {
506     LosVmSpace *space = NULL;
507 
508     space = OsCurrProcessGet()->vmSpace;
509     if (space == NULL) {
510         return;
511     }
512 
513     if (LOS_IsRangeInSpace(space, ROUNDDOWN(vaddr, MB), MB) == FALSE) {
514         return;
515     }
516 
517     OsDumpPte(vaddr);
518     OsDumpAspace(space);
519 }
520 #endif
521 
522