1 /*
2  * Copyright (C) 2018 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 #include "mem_map.h"
18 #include "logging.h"
19 #include "mman.h"
20 
21 #include <zircon/process.h>
22 #include <zircon/syscalls.h>
23 
24 namespace art {
25 
26 static zx_handle_t fuchsia_lowmem_vmar = ZX_HANDLE_INVALID;
27 static zx_vaddr_t fuchsia_lowmem_base = 0;
28 static size_t fuchsia_lowmem_size = 0;
29 
30 static const char map_name[] = "mmap-android";
31 static constexpr uintptr_t FUCHSIA_LOWER_MEM_START = 0x80000000;
32 static constexpr uintptr_t FUCHSIA_LOWER_MEM_SIZE  = 0x60000000;
33 
TargetMMapInit()34 void MemMap::TargetMMapInit() {
35   if (fuchsia_lowmem_vmar != ZX_HANDLE_INVALID) {
36     return;
37   }
38 
39   zx_info_vmar_t vmarinfo;
40   CHECK_EQ(zx_object_get_info(zx_vmar_root_self(),
41                               ZX_INFO_VMAR,
42                               &vmarinfo,
43                               sizeof(vmarinfo),
44                               nullptr,
45                               nullptr), ZX_OK) << "could not find info from root vmar";
46 
47   uintptr_t lower_mem_start = FUCHSIA_LOWER_MEM_START - vmarinfo.base;
48   fuchsia_lowmem_size = FUCHSIA_LOWER_MEM_SIZE;
49   uint32_t allocflags = ZX_VM_FLAG_CAN_MAP_READ |
50                         ZX_VM_FLAG_CAN_MAP_WRITE |
51                         ZX_VM_FLAG_CAN_MAP_EXECUTE |
52                         ZX_VM_FLAG_SPECIFIC;
53   CHECK_EQ(zx_vmar_allocate(zx_vmar_root_self(),
54                             lower_mem_start,
55                             fuchsia_lowmem_size,
56                             allocflags,
57                             &fuchsia_lowmem_vmar,
58                             &fuchsia_lowmem_base), ZX_OK) << "could not allocate lowmem vmar";
59 }
60 
TargetMMap(void * start,size_t len,int prot,int flags,int fd,off_t fd_off)61 void* MemMap::TargetMMap(void* start, size_t len, int prot, int flags, int fd, off_t fd_off) {
62   zx_status_t status;
63   uintptr_t mem = 0;
64 
65   bool mmap_lower = (flags & MAP_32BIT) != 0;
66 
67   // for file-based mapping use system library
68   if ((flags & MAP_ANONYMOUS) == 0) {
69     if (start != nullptr) {
70       flags |= MAP_FIXED;
71     }
72     CHECK(!mmap_lower) << "cannot map files into low memory for Fuchsia";
73     return mmap(start, len, prot, flags, fd, fd_off);
74   }
75 
76   uint32_t vmarflags = 0;
77   if ((prot & PROT_READ) != 0) {
78     vmarflags |= ZX_VM_FLAG_PERM_READ;
79   }
80   if ((prot & PROT_WRITE) != 0) {
81     vmarflags |= ZX_VM_FLAG_PERM_WRITE;
82   }
83   if ((prot & PROT_EXEC) != 0) {
84     vmarflags |= ZX_VM_FLAG_PERM_EXECUTE;
85   }
86 
87   if (len == 0) {
88     errno = EINVAL;
89     return MAP_FAILED;
90   }
91 
92   zx_info_vmar_t vmarinfo;
93   size_t vmaroffset = 0;
94   if (start != nullptr) {
95     vmarflags |= ZX_VM_FLAG_SPECIFIC;
96     status = zx_object_get_info((mmap_lower ? fuchsia_lowmem_vmar : zx_vmar_root_self()),
97                                 ZX_INFO_VMAR,
98                                 &vmarinfo,
99                                 sizeof(vmarinfo),
100                                 nullptr,
101                                 nullptr);
102     if (status < 0 || reinterpret_cast<uintptr_t>(start) < vmarinfo.base) {
103       errno = EINVAL;
104       return MAP_FAILED;
105     }
106     vmaroffset = reinterpret_cast<uintptr_t>(start) - vmarinfo.base;
107   }
108 
109   zx_handle_t vmo;
110   if (zx_vmo_create(len, 0, &vmo) < 0) {
111     errno = ENOMEM;
112     return MAP_FAILED;
113   }
114   zx_vmo_get_size(vmo, &len);
115   zx_object_set_property(vmo, ZX_PROP_NAME, map_name, strlen(map_name));
116 
117   if (mmap_lower) {
118     status = zx_vmar_map(fuchsia_lowmem_vmar, vmaroffset, vmo, fd_off, len, vmarflags, &mem);
119   } else {
120     status = zx_vmar_map(zx_vmar_root_self(), vmaroffset, vmo, fd_off, len, vmarflags, &mem);
121   }
122   zx_handle_close(vmo);
123   if (status != ZX_OK) {
124     return MAP_FAILED;
125   }
126 
127   return reinterpret_cast<void *>(mem);
128 }
129 
TargetMUnmap(void * start,size_t len)130 int MemMap::TargetMUnmap(void* start, size_t len) {
131   uintptr_t addr = reinterpret_cast<uintptr_t>(start);
132   zx_handle_t alloc_vmar = zx_vmar_root_self();
133   if (addr >= fuchsia_lowmem_base && addr < fuchsia_lowmem_base + fuchsia_lowmem_size) {
134     alloc_vmar = fuchsia_lowmem_vmar;
135   }
136   zx_status_t status = zx_vmar_unmap(alloc_vmar, addr, len);
137   if (status < 0) {
138     errno = EINVAL;
139     return -1;
140   }
141   return 0;
142 }
143 
144 }  // namespace art
145