1 /*
2 * Copyright (c) 2022 Huawei Device Co., Ltd.
3 * Licensed under the Apache License, Version 2.0 (the "License");
4 * you may not use this file except in compliance with the License.
5 * You may obtain a copy of the License at
6 *
7 * http://www.apache.org/licenses/LICENSE-2.0
8 *
9 * Unless required by applicable law or agreed to in writing, software
10 * distributed under the License is distributed on an "AS IS" BASIS,
11 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 * See the License for the specific language governing permissions and
13 * limitations under the License.
14 */
15
16 #include <stddef.h> /* NULL */
17 #include <sys/mman.h> /* mmap */
18 #include <sched.h> /* sched_yield() */
19 #include <limits.h>
20
21 #include "hilog/log_c.h"
22 #include "pm_util.h"
23 #include "ux_page_table_c.h"
24
25 #undef LOG_TAG
26 #define LOG_TAG "PurgeableMemC: UPT"
27
28 #if defined(USE_UXPT) && (USE_UXPT > 0) /* (USE_UXPT > 0) means using uxpt */
29
30 /*
31 * using uint64_t as uxpte_t to avoid avoid confusion on 32-bit and 64 bit systems.
32 * Type uxpte_t may be modified to uint32_t in the future, so typedef is used.
33 */
34 typedef uint64_t uxpte_t;
35
36 typedef struct UserExtendPageTable {
37 uint64_t dataAddr;
38 size_t dataSize;
39 uxpte_t *uxpte;
40 } UxPageTableStruct;
41
42 static bool g_supportUxpt = false;
43
44 /*
45 * -------------------------------------------------------------------------
46 * | virtual page number | |
47 * |--------------------------------------------| vaddr offset in virt page |
48 * | uxpte page number | offset in uxpte page | |
49 * --------------------------------------------------------------------------
50 * | | UXPTE_PER_PAGE_SHIFT | PAGE_SHIFT |
51 */
52 static const size_t UXPTE_SIZE_SHIFT = 3;
53 static const size_t UXPTE_PER_PAGE_SHIFT = PAGE_SHIFT - UXPTE_SIZE_SHIFT;
54 static const size_t UXPTE_PER_PAGE = 1 << UXPTE_PER_PAGE_SHIFT;
55
56 /* get virtual page number from virtual address */
VirtPageNo(uint64_t vaddr)57 static inline uint64_t VirtPageNo(uint64_t vaddr)
58 {
59 return vaddr >> PAGE_SHIFT;
60 }
61
62 /* page number in user page table of uxpte for virtual address */
UxptePageNo(uint64_t vaddr)63 static inline uint64_t UxptePageNo(uint64_t vaddr)
64 {
65 return VirtPageNo(vaddr) >> UXPTE_PER_PAGE_SHIFT;
66 }
67
68 /* uxpte offset in uxpte page for virtual address */
UxpteOffset(uint64_t vaddr)69 static inline uint64_t UxpteOffset(uint64_t vaddr)
70 {
71 return VirtPageNo(vaddr) & (UXPTE_PER_PAGE - 1);
72 }
73
74 static const size_t UXPTE_PRESENT_BIT = 1;
75 static const size_t UXPTE_PRESENT_MASK = (1 << UXPTE_PRESENT_BIT) - 1;
76 static const size_t UXPTE_REFCNT_ONE = 1 << UXPTE_PRESENT_BIT;
77 static const uxpte_t UXPTE_UNDER_RECLAIM = (uxpte_t)(-UXPTE_REFCNT_ONE);
78
IsUxptePresent(uxpte_t pte)79 static inline bool IsUxptePresent(uxpte_t pte)
80 {
81 return pte & (uxpte_t)UXPTE_PRESENT_MASK;
82 }
83
IsUxpteUnderReclaim(uxpte_t pte)84 static inline bool IsUxpteUnderReclaim(uxpte_t pte)
85 {
86 return pte == UXPTE_UNDER_RECLAIM;
87 }
88
GetUxPageSize(uint64_t dataAddr,size_t dataSize)89 static inline size_t GetUxPageSize(uint64_t dataAddr, size_t dataSize)
90 {
91 return (UxptePageNo(dataAddr + dataSize - 1) - UxptePageNo(dataAddr) + 1) * PAGE_SIZE;
92 }
93
RoundUp(uint64_t val,size_t align)94 static inline uint64_t RoundUp(uint64_t val, size_t align)
95 {
96 if (val + align < val || val + align < align) {
97 HILOG_ERROR(LOG_CORE, "%{public}s: Addition overflow!", __func__);
98 return val;
99 }
100 if (align == 0) {
101 return val;
102 }
103 return ((val + align - 1) / align) * align;
104 }
105
RoundDown(uint64_t val,size_t align)106 static inline uint64_t RoundDown(uint64_t val, size_t align)
107 {
108 if (align == 0) {
109 return val;
110 }
111 return val & (~(align - 1));
112 }
113
114 enum UxpteOp {
115 UPT_GET = 0,
116 UPT_PUT = 1,
117 UPT_CLEAR = 2,
118 UPT_IS_PRESENT = 3,
119 };
120
121 static void __attribute__((constructor)) CheckUxpt(void);
122 static void UxpteAdd(uxpte_t *pte, size_t incNum);
123 static void UxpteSub(uxpte_t *pte, size_t decNum);
124
125 static void GetUxpteAt(UxPageTableStruct *upt, uint64_t addr);
126 static void PutUxpteAt(UxPageTableStruct *upt, uint64_t addr);
127 static bool IsPresentAt(UxPageTableStruct *upt, uint64_t addr);
128 static PMState UxpteOps(UxPageTableStruct *upt, uint64_t addr, size_t len, enum UxpteOp op);
129
130 static uxpte_t *MapUxptePages(uint64_t dataAddr, size_t dataSize);
131 static int UnmapUxptePages(uxpte_t *ptes, size_t size);
132
CheckUxpt(void)133 static void __attribute__((constructor)) CheckUxpt(void)
134 {
135 int prot = PROT_READ | PROT_WRITE;
136 int type = MAP_ANONYMOUS | MAP_PURGEABLE;
137 size_t dataSize = PAGE_SIZE;
138 /* try to mmap purgable page */
139 void *dataPtr = mmap(NULL, dataSize, prot, type, -1, 0);
140 if (dataPtr == MAP_FAILED) {
141 HILOG_ERROR(LOG_CORE, "%{public}s: not support MAP_PURG", __func__);
142 g_supportUxpt = false;
143 return;
144 }
145 /* try to mmap uxpt page */
146 type = MAP_ANONYMOUS | MAP_USEREXPTE;
147 size_t uptSize = GetUxPageSize((uint64_t)dataPtr, dataSize);
148 void *ptes = mmap(NULL, uptSize, prot, type, -1, UxptePageNo((uint64_t)dataPtr) * PAGE_SIZE);
149 if (ptes != MAP_FAILED) {
150 g_supportUxpt = true;
151 /* free uxpt */
152 if (munmap(ptes, uptSize) != 0) {
153 HILOG_ERROR(LOG_CORE, "%{public}s: unmap uxpt fail", __func__);
154 }
155 } else { /* MAP_FAILED */
156 g_supportUxpt = false;
157 HILOG_ERROR(LOG_CORE, "%{public}s: not support uxpt", __func__);
158 }
159 ptes = NULL;
160 /* free data */
161 if (munmap(dataPtr, dataSize) != 0) {
162 HILOG_ERROR(LOG_CORE, "%{public}s: unmap purg data fail", __func__);
163 }
164 dataPtr = NULL;
165 HILOG_INFO(LOG_CORE, "%{public}s: supportUxpt=%{public}s", __func__, (g_supportUxpt ? "1" : "0"));
166 return;
167 }
168
UxpteIsEnabled(void)169 bool UxpteIsEnabled(void)
170 {
171 return g_supportUxpt;
172 }
173
UxPageTableSize(void)174 size_t UxPageTableSize(void)
175 {
176 return sizeof(UxPageTableStruct);
177 }
178
InitUxPageTable(UxPageTableStruct * upt,uint64_t addr,size_t len)179 PMState InitUxPageTable(UxPageTableStruct *upt, uint64_t addr, size_t len)
180 {
181 if (!g_supportUxpt) {
182 HILOG_DEBUG(LOG_CORE, "%{public}s: not support uxpt", __func__);
183 return PM_OK;
184 }
185 upt->dataAddr = addr;
186 upt->dataSize = len;
187 upt->uxpte = MapUxptePages(upt->dataAddr, upt->dataSize);
188 if (!(upt->uxpte)) {
189 return PM_MMAP_UXPT_FAIL;
190 }
191 UxpteClear(upt, addr, len);
192 return PM_OK;
193 }
194
DeinitUxPageTable(UxPageTableStruct * upt)195 PMState DeinitUxPageTable(UxPageTableStruct *upt)
196 {
197 if (!g_supportUxpt) {
198 HILOG_DEBUG(LOG_CORE, "%{public}s: not support uxpt", __func__);
199 return PM_OK;
200 }
201 size_t size = GetUxPageSize(upt->dataAddr, upt->dataSize);
202 int unmapRet = 0;
203 if (upt->uxpte) {
204 unmapRet = UnmapUxptePages(upt->uxpte, size);
205 if (unmapRet != 0) {
206 HILOG_ERROR(LOG_CORE, "%{public}s: unmap uxpt fail", __func__);
207 return PM_UNMAP_UXPT_FAIL;
208 }
209 upt->uxpte = NULL;
210 }
211 upt->dataAddr = 0;
212 upt->dataSize = 0;
213 return PM_OK;
214 }
215
UxpteGet(UxPageTableStruct * upt,uint64_t addr,size_t len)216 void UxpteGet(UxPageTableStruct *upt, uint64_t addr, size_t len)
217 {
218 if (!g_supportUxpt) {
219 return;
220 }
221 UxpteOps(upt, addr, len, UPT_GET);
222 }
223
UxptePut(UxPageTableStruct * upt,uint64_t addr,size_t len)224 void UxptePut(UxPageTableStruct *upt, uint64_t addr, size_t len)
225 {
226 if (!g_supportUxpt) {
227 return;
228 }
229 UxpteOps(upt, addr, len, UPT_PUT);
230 }
231
UxpteClear(UxPageTableStruct * upt,uint64_t addr,size_t len)232 void UxpteClear(UxPageTableStruct *upt, uint64_t addr, size_t len)
233 {
234 if (!g_supportUxpt) {
235 return;
236 }
237 UxpteOps(upt, addr, len, UPT_CLEAR);
238 }
239
UxpteIsPresent(UxPageTableStruct * upt,uint64_t addr,size_t len)240 bool UxpteIsPresent(UxPageTableStruct *upt, uint64_t addr, size_t len)
241 {
242 if (!g_supportUxpt) {
243 return true;
244 }
245 PMState ret = UxpteOps(upt, addr, len, UPT_IS_PRESENT);
246 return ret == PM_OK;
247 }
248
UxpteLoad(uxpte_t * uxpte)249 static inline uxpte_t UxpteLoad(uxpte_t *uxpte)
250 {
251 __sync_synchronize();
252 return *uxpte;
253 }
254
UxpteCAS_(uxpte_t * uxpte,uxpte_t old,uxpte_t newVal)255 static inline bool UxpteCAS_(uxpte_t *uxpte, uxpte_t old, uxpte_t newVal)
256 {
257 return __sync_bool_compare_and_swap(uxpte, old, newVal);
258 }
259
UxpteAdd(uxpte_t * pte,size_t incNum)260 static void UxpteAdd(uxpte_t *pte, size_t incNum)
261 {
262 uxpte_t old = 0;
263 uxpte_t newVal = 0;
264 do {
265 old = UxpteLoad(pte);
266 if (old + incNum < old || old + incNum < incNum) {
267 break;
268 }
269 newVal = old + incNum;
270 if (ULONG_MAX - old < incNum) {
271 return;
272 }
273 if (IsUxpteUnderReclaim(old)) {
274 sched_yield();
275 continue;
276 }
277 } while (!UxpteCAS_(pte, old, newVal));
278 }
279
UxpteSub(uxpte_t * pte,size_t decNum)280 static void UxpteSub(uxpte_t *pte, size_t decNum)
281 {
282 uxpte_t old;
283 do {
284 old = UxpteLoad(pte);
285 } while (!UxpteCAS_(pte, old, old - decNum));
286 }
287
UxpteClear_(uxpte_t * pte)288 static void UxpteClear_(uxpte_t *pte)
289 {
290 uxpte_t old = UxpteLoad(pte);
291 if ((unsigned long long)old == 0) {
292 return; /* has been set to zero */
293 }
294 HILOG_ERROR(LOG_CORE, "%{public}s: upte(0x%{public}llx) != 0", __func__, (unsigned long long)old);
295 do {
296 old = UxpteLoad(pte);
297 } while (!UxpteCAS_(pte, old, 0));
298 }
299
GetIndexInUxpte(uint64_t startAddr,uint64_t currAddr)300 static inline size_t GetIndexInUxpte(uint64_t startAddr, uint64_t currAddr)
301 {
302 return UxpteOffset(startAddr) + (VirtPageNo(currAddr) - VirtPageNo(startAddr));
303 }
304
GetUxpteAt(UxPageTableStruct * upt,uint64_t addr)305 static void GetUxpteAt(UxPageTableStruct *upt, uint64_t addr)
306 {
307 size_t index = GetIndexInUxpte(upt->dataAddr, addr);
308 UxpteAdd(&(upt->uxpte[index]), UXPTE_REFCNT_ONE);
309
310 HILOG_DEBUG(LOG_CORE, "%{public}s: addr(0x%{public}llx) upte=0x%{public}llx",
311 __func__, (unsigned long long)addr, (unsigned long long)(upt->uxpte[index]));
312 }
313
PutUxpteAt(UxPageTableStruct * upt,uint64_t addr)314 static void PutUxpteAt(UxPageTableStruct *upt, uint64_t addr)
315 {
316 size_t index = GetIndexInUxpte(upt->dataAddr, addr);
317 UxpteSub(&(upt->uxpte[index]), UXPTE_REFCNT_ONE);
318
319 HILOG_DEBUG(LOG_CORE, "%{public}s: addr(0x%{public}llx) upte=0x%{public}llx",
320 __func__, (unsigned long long)addr, (unsigned long long)(upt->uxpte[index]));
321 }
322
ClearUxpteAt(UxPageTableStruct * upt,uint64_t addr)323 static void ClearUxpteAt(UxPageTableStruct *upt, uint64_t addr)
324 {
325 size_t index = GetIndexInUxpte(upt->dataAddr, addr);
326 UxpteClear_(&(upt->uxpte[index]));
327 }
328
IsPresentAt(UxPageTableStruct * upt,uint64_t addr)329 static bool IsPresentAt(UxPageTableStruct *upt, uint64_t addr)
330 {
331 size_t index = GetIndexInUxpte(upt->dataAddr, addr);
332
333 HILOG_DEBUG(LOG_CORE, "%{public}s: addr(0x%{public}llx) upte=0x%{public}llx PRESENT_MASK=0x%{public}zx",
334 __func__, (unsigned long long)addr, (unsigned long long)(upt->uxpte[index]), UXPTE_PRESENT_MASK);
335
336 return IsUxptePresent(upt->uxpte[index]);
337 }
338
UxpteOps(UxPageTableStruct * upt,uint64_t addr,size_t len,enum UxpteOp op)339 static PMState UxpteOps(UxPageTableStruct *upt, uint64_t addr, size_t len, enum UxpteOp op)
340 {
341 if (upt == NULL) {
342 return PM_BUILDER_NULL;
343 }
344 uint64_t start = RoundDown(addr, PAGE_SIZE);
345 uint64_t end = RoundUp(addr + len, PAGE_SIZE);
346 if (start < upt->dataAddr || end > (upt->dataAddr + upt->dataSize)) {
347 HILOG_ERROR(LOG_CORE, "%{public}s: addr(0x%{public}llx) start(0x%{public}llx) < dataAddr(0x%{public}llx)"
348 " || end(0x%{public}llx) > dataAddr+dataSize(0x%{public}llx) out of bound",
349 __func__, (unsigned long long)addr, (unsigned long long)start, (unsigned long long)(upt->dataAddr),
350 (unsigned long long)end, (unsigned long long)(upt->dataAddr + upt->dataSize));
351
352 return PM_UXPT_OUT_RANGE;
353 }
354
355 for (uint64_t off = start; off < end; off += PAGE_SIZE) {
356 switch (op) {
357 case UPT_GET: {
358 GetUxpteAt(upt, off);
359 break;
360 }
361 case UPT_PUT: {
362 PutUxpteAt(upt, off);
363 break;
364 }
365 case UPT_CLEAR: {
366 ClearUxpteAt(upt, off);
367 break;
368 }
369 case UPT_IS_PRESENT: {
370 if (!IsPresentAt(upt, off)) {
371 HILOG_ERROR(LOG_CORE, "%{public}s: addr(0x%{public}llx) not present", __func__,
372 (unsigned long long)addr);
373 return PM_UXPT_NO_PRESENT;
374 }
375 break;
376 }
377 default:
378 break;
379 }
380 }
381
382 return PM_OK;
383 }
384
MapUxptePages(uint64_t dataAddr,size_t dataSize)385 static uxpte_t *MapUxptePages(uint64_t dataAddr, size_t dataSize)
386 {
387 int prot = PROT_READ | PROT_WRITE;
388 int type = MAP_ANONYMOUS | MAP_USEREXPTE;
389 size_t size = GetUxPageSize(dataAddr, dataSize);
390 uxpte_t *ptes = (uxpte_t*)mmap(NULL, size, prot, type, -1, UxptePageNo(dataAddr) * PAGE_SIZE);
391 if (ptes == MAP_FAILED) {
392 HILOG_ERROR(LOG_CORE, "%{public}s: fail, return NULL", __func__);
393 ptes = NULL;
394 }
395
396 return ptes;
397 }
398
UnmapUxptePages(uxpte_t * ptes,size_t size)399 static int UnmapUxptePages(uxpte_t *ptes, size_t size)
400 {
401 return munmap(ptes, size);
402 }
403
404 #else /* !(defined(USE_UXPT) && (USE_UXPT <= 0)), it means does not using uxpt */
405
406 typedef struct UserExtendPageTable {
407 /* i am empty */
408 } UxPageTableStruct;
409
UxpteIsEnabled(void)410 bool UxpteIsEnabled(void)
411 {
412 return false;
413 }
414
UxPageTableSize(void)415 size_t UxPageTableSize(void)
416 {
417 return 0;
418 }
419
InitUxPageTable(UxPageTableStruct * upt,uint64_t addr,size_t len)420 PMState InitUxPageTable(UxPageTableStruct *upt, uint64_t addr, size_t len)
421 {
422 return PM_OK;
423 }
424
DeinitUxPageTable(UxPageTableStruct * upt)425 PMState DeinitUxPageTable(UxPageTableStruct *upt)
426 {
427 return PM_OK;
428 }
429
UxpteGet(UxPageTableStruct * upt,uint64_t addr,size_t len)430 void UxpteGet(UxPageTableStruct *upt, uint64_t addr, size_t len) {}
431
UxptePut(UxPageTableStruct * upt,uint64_t addr,size_t len)432 void UxptePut(UxPageTableStruct *upt, uint64_t addr, size_t len) {}
433
UxpteIsPresent(UxPageTableStruct * upt,uint64_t addr,size_t len)434 bool UxpteIsPresent(UxPageTableStruct *upt, uint64_t addr, size_t len)
435 {
436 return true;
437 }
438
439 #endif /* USE_UXPT > 0 */
440