• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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