• 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     }
277 
278     return vmFlags;
279 }
280 
LOS_DoMprotect(VADDR_T vaddr,size_t len,unsigned long prot)281 INT32 LOS_DoMprotect(VADDR_T vaddr, size_t len, unsigned long prot)
282 {
283     LosVmSpace *space = OsCurrProcessGet()->vmSpace;
284     LosVmMapRegion *region = NULL;
285     UINT32 vmFlags;
286     UINT32 count;
287     int ret;
288 
289     (VOID)LOS_MuxAcquire(&space->regionMux);
290     region = LOS_RegionFind(space, vaddr);
291     if (!IS_ALIGNED(vaddr, PAGE_SIZE) || (region == NULL) || (vaddr > vaddr + len)) {
292         ret = -EINVAL;
293         goto OUT_MPROTECT;
294     }
295 
296     if ((prot & ~(PROT_READ | PROT_WRITE | PROT_EXEC))) {
297         ret = -EINVAL;
298         goto OUT_MPROTECT;
299     }
300 
301     if ((region->regionFlags & VM_MAP_REGION_FLAG_VDSO) || (region->regionFlags & VM_MAP_REGION_FLAG_HEAP)) {
302         ret = -EPERM;
303         goto OUT_MPROTECT;
304     }
305 
306     if (LOS_IsRegionTypeFile(region) && (region->regionFlags & VM_MAP_REGION_FLAG_SHARED)) {
307         if (!OsProtMprotectPermCheck(prot, region)) {
308             ret = -EACCES;
309             goto OUT_MPROTECT;
310         }
311     }
312 
313     len = LOS_Align(len, PAGE_SIZE);
314     /* can't operation cross region */
315     if ((region->range.base + region->range.size) < (vaddr + len)) {
316         ret = -EINVAL;
317         goto OUT_MPROTECT;
318     }
319 
320     /* if only move some part of region, we need to split first */
321     if (region->range.size > len) {
322         OsVmRegionAdjust(space, vaddr, len);
323     }
324 
325     vmFlags = OsCvtProtFlagsToRegionFlags(prot, 0);
326     vmFlags |= (region->regionFlags & VM_MAP_REGION_FLAG_SHARED) ? VM_MAP_REGION_FLAG_SHARED : 0;
327     vmFlags |= OsInheritOldRegionName(region->regionFlags);
328     region = LOS_RegionFind(space, vaddr);
329     if (region == NULL) {
330         ret = -ENOMEM;
331         goto OUT_MPROTECT;
332     }
333     region->regionFlags = vmFlags;
334     count = len >> PAGE_SHIFT;
335     ret = LOS_ArchMmuChangeProt(&space->archMmu, vaddr, count, region->regionFlags);
336     if (ret) {
337         ret = -ENOMEM;
338         goto OUT_MPROTECT;
339     }
340     ret = LOS_OK;
341 
342 OUT_MPROTECT:
343 #ifdef LOSCFG_VM_OVERLAP_CHECK
344     if (VmmAspaceRegionsOverlapCheck(aspace) < 0) {
345         (VOID)OsShellCmdDumpVm(0, NULL);
346         ret = -ENOMEM;
347     }
348 #endif
349 
350     (VOID)LOS_MuxRelease(&space->regionMux);
351     return ret;
352 }
353 
OsMremapCheck(VADDR_T addr,size_t oldLen,VADDR_T newAddr,size_t newLen,unsigned int flags)354 STATUS_T OsMremapCheck(VADDR_T addr, size_t oldLen, VADDR_T newAddr, size_t newLen, unsigned int flags)
355 {
356     LosVmSpace *space = OsCurrProcessGet()->vmSpace;
357     LosVmMapRegion *region = LOS_RegionFind(space, addr);
358     VADDR_T regionEnd;
359 
360     if ((region == NULL) || (region->range.base > addr) || (newLen == 0)) {
361         return -EINVAL;
362     }
363 
364     if (flags & ~(MREMAP_FIXED | MREMAP_MAYMOVE)) {
365         return -EINVAL;
366     }
367 
368     if (((flags & MREMAP_FIXED) == MREMAP_FIXED) && ((flags & MREMAP_MAYMOVE) == 0)) {
369         return -EINVAL;
370     }
371 
372     if (!IS_ALIGNED(addr, PAGE_SIZE)) {
373         return -EINVAL;
374     }
375 
376     regionEnd = region->range.base + region->range.size;
377 
378     /* we can't operate across region */
379     if (oldLen > regionEnd - addr) {
380         return -EFAULT;
381     }
382 
383     /* avoiding overflow */
384     if (newLen > oldLen) {
385         if ((addr + newLen) < addr) {
386             return -EINVAL;
387         }
388     }
389 
390     /* avoid new region overlapping with the old one */
391     if (flags & MREMAP_FIXED) {
392         if (((region->range.base + region->range.size) > newAddr) &&
393             (region->range.base < (newAddr + newLen))) {
394             return -EINVAL;
395         }
396 
397         if (!IS_ALIGNED(newAddr, PAGE_SIZE)) {
398             return -EINVAL;
399         }
400     }
401 
402     return LOS_OK;
403 }
404 
LOS_DoMremap(VADDR_T oldAddress,size_t oldSize,size_t newSize,int flags,VADDR_T newAddr)405 VADDR_T LOS_DoMremap(VADDR_T oldAddress, size_t oldSize, size_t newSize, int flags, VADDR_T newAddr)
406 {
407     LosVmMapRegion *regionOld = NULL;
408     LosVmMapRegion *regionNew = NULL;
409     STATUS_T status;
410     VADDR_T ret;
411     LosVmSpace *space = OsCurrProcessGet()->vmSpace;
412 
413     oldSize = LOS_Align(oldSize, PAGE_SIZE);
414     newSize = LOS_Align(newSize, PAGE_SIZE);
415 
416     (VOID)LOS_MuxAcquire(&space->regionMux);
417 
418     status = OsMremapCheck(oldAddress, oldSize, newAddr, newSize, (unsigned int)flags);
419     if (status) {
420         ret = status;
421         goto OUT_MREMAP;
422     }
423 
424     /* if only move some part of region, we need to split first */
425     status = OsVmRegionAdjust(space, oldAddress, oldSize);
426     if (status) {
427         ret = -ENOMEM;
428         goto OUT_MREMAP;
429     }
430 
431     regionOld = LOS_RegionFind(space, oldAddress);
432     if (regionOld == NULL) {
433         ret = -ENOMEM;
434         goto OUT_MREMAP;
435     }
436 
437     if ((unsigned int)flags & MREMAP_FIXED) {
438         regionNew = OsVmRegionDup(space, regionOld, newAddr, newSize);
439         if (!regionNew) {
440             ret = -ENOMEM;
441             goto OUT_MREMAP;
442         }
443         status = LOS_ArchMmuMove(&space->archMmu, oldAddress, newAddr,
444                                  ((newSize < regionOld->range.size) ? newSize : regionOld->range.size) >> PAGE_SHIFT,
445                                  regionOld->regionFlags);
446         if (status) {
447             LOS_RegionFree(space, regionNew);
448             ret = -ENOMEM;
449             goto OUT_MREMAP;
450         }
451         LOS_RegionFree(space, regionOld);
452         ret = newAddr;
453         goto OUT_MREMAP;
454     }
455     // take it as shrink operation
456     if (oldSize > newSize) {
457         LOS_UnMMap(oldAddress + newSize, oldSize - newSize);
458         ret = oldAddress;
459         goto OUT_MREMAP;
460     }
461     status = OsIsRegionCanExpand(space, regionOld, newSize);
462     // we can expand directly.
463     if (!status) {
464         regionOld->range.size = newSize;
465         ret = oldAddress;
466         goto OUT_MREMAP;
467     }
468 
469     if ((unsigned int)flags & MREMAP_MAYMOVE) {
470         regionNew = OsVmRegionDup(space, regionOld, 0, newSize);
471         if (regionNew  == NULL) {
472             ret = -ENOMEM;
473             goto OUT_MREMAP;
474         }
475         status = LOS_ArchMmuMove(&space->archMmu, oldAddress, regionNew->range.base,
476                                  regionOld->range.size >> PAGE_SHIFT, regionOld->regionFlags);
477         if (status) {
478             LOS_RegionFree(space, regionNew);
479             ret = -ENOMEM;
480             goto OUT_MREMAP;
481         }
482         LOS_RegionFree(space, regionOld);
483         ret = regionNew->range.base;
484         goto OUT_MREMAP;
485     }
486 
487     ret = -EINVAL;
488 OUT_MREMAP:
489 #ifdef LOSCFG_VM_OVERLAP_CHECK
490     if (VmmAspaceRegionsOverlapCheck(aspace) < 0) {
491         (VOID)OsShellCmdDumpVm(0, NULL);
492         ret = -ENOMEM;
493     }
494 #endif
495 
496     (VOID)LOS_MuxRelease(&space->regionMux);
497     return ret;
498 }
499 
LOS_DumpMemRegion(VADDR_T vaddr)500 VOID LOS_DumpMemRegion(VADDR_T vaddr)
501 {
502     LosVmSpace *space = NULL;
503 
504     space = OsCurrProcessGet()->vmSpace;
505     if (space == NULL) {
506         return;
507     }
508 
509     if (LOS_IsRangeInSpace(space, ROUNDDOWN(vaddr, MB), MB) == FALSE) {
510         return;
511     }
512 
513     OsDumpPte(vaddr);
514     OsDumpAspace(space);
515 }
516 #endif
517 
518