1 /*
2 * Copyright (c) 2023 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 <sys/mman.h> /* mmap */
17
18 #include "securec.h"
19 #include "pm_util.h"
20 #include "pm_state_c.h"
21 #include "pm_smartptr_util.h"
22 #include "pm_log.h"
23
24 #include "purgeable_ashmem.h"
25
26 namespace OHOS {
27 namespace PurgeableMem {
28 #ifdef LOG_TAG
29 #undef LOG_TAG
30 #endif
31 #define LOG_TAG "PurgeableMem"
32
RoundUp(size_t val,size_t align)33 static inline size_t RoundUp(size_t val, size_t align)
34 {
35 if (align == 0) {
36 return val;
37 }
38 return ((val + align - 1) / align) * align;
39 }
40
PurgeableAshMem(std::unique_ptr<PurgeableMemBuilder> builder)41 PurgeableAshMem::PurgeableAshMem(std::unique_ptr<PurgeableMemBuilder> builder)
42 {
43 dataPtr_ = nullptr;
44 builder_ = nullptr;
45 ashmemFd_ = -1;
46 buildDataCount_ = 0;
47 isSupport_ = false;
48 isChange_ = false;
49 IF_NULL_LOG_ACTION(builder, "%{public}s: input builder nullptr", return);
50 builder_ = std::move(builder);
51 PM_HILOG_DEBUG(LOG_CORE, "%{public}s init succ. %{public}s", __func__, ToString().c_str());
52 }
53
PurgeableAshMem(size_t dataSize,std::unique_ptr<PurgeableMemBuilder> builder)54 PurgeableAshMem::PurgeableAshMem(size_t dataSize, std::unique_ptr<PurgeableMemBuilder> builder)
55 {
56 dataPtr_ = nullptr;
57 builder_ = nullptr;
58 ashmemFd_ = -1;
59 buildDataCount_ = 0;
60 isSupport_ = false;
61 isChange_ = false;
62 if (dataSize == 0) {
63 return;
64 }
65 dataSizeInput_ = dataSize;
66 IF_NULL_LOG_ACTION(builder, "%{public}s: input builder nullptr", return);
67
68 CreatePurgeableData_();
69 builder_ = std::move(builder);
70 PM_HILOG_DEBUG(LOG_CORE, "%{public}s init succ. %{public}s", __func__, ToString().c_str());
71 }
72
~PurgeableAshMem()73 PurgeableAshMem::~PurgeableAshMem()
74 {
75 PM_HILOG_DEBUG(LOG_CORE, "%{public}s %{public}s", __func__, ToString().c_str());
76 if (!isChange_ && dataPtr_) {
77 if (munmap(dataPtr_, RoundUp(dataSizeInput_, PAGE_SIZE)) != 0) {
78 PM_HILOG_ERROR(LOG_CORE, "%{public}s: munmap dataPtr fail", __func__);
79 } else {
80 if (UxpteIsEnabled() && !IsPurged()) {
81 PM_HILOG_ERROR(LOG_CORE, "%{public}s: munmap dataPtr succ, but uxpte present", __func__);
82 }
83 dataPtr_ = nullptr;
84 close(ashmemFd_);
85 }
86 }
87 builder_.reset();
88 }
89
GetAshmemFd()90 int PurgeableAshMem::GetAshmemFd()
91 {
92 return ashmemFd_;
93 }
94
IsPurged()95 bool PurgeableAshMem::IsPurged()
96 {
97 if (!isSupport_) {
98 return false;
99 }
100 int ret = ioctl(ashmemFd_, PURGEABLE_ASHMEM_IS_PURGED);
101 PM_HILOG_DEBUG(LOG_CORE, "%{public}s: IsPurged %{public}d", __func__, ret);
102 return ret > 0 ? true : false;
103 }
104
CreatePurgeableData_()105 bool PurgeableAshMem::CreatePurgeableData_()
106 {
107 PM_HILOG_DEBUG(LOG_CORE, "%{public}s", __func__);
108 if (dataSizeInput_ == 0) {
109 return false;
110 }
111 size_t size = RoundUp(dataSizeInput_, PAGE_SIZE);
112 int fd = AshmemCreate("PurgeableAshmem", size);
113 if (fd < 0) {
114 return false;
115 }
116 if (AshmemSetProt(fd, PROT_READ | PROT_WRITE) < 0) {
117 close(fd);
118 return false;
119 }
120 ashmemFd_ = fd;
121 pin_ = { static_cast<uint32_t>(0), static_cast<uint32_t>(0) };
122 dataPtr_ = mmap(nullptr, size, PROT_READ | PROT_WRITE, MAP_SHARED, ashmemFd_, 0);
123 if (dataPtr_ == MAP_FAILED) {
124 PM_HILOG_ERROR(LOG_CORE, "%{public}s: mmap fail", __func__);
125 dataPtr_ = nullptr;
126 close(ashmemFd_);
127 return false;
128 }
129 TEMP_FAILURE_RETRY(ioctl(ashmemFd_, ASHMEM_SET_PURGEABLE));
130 if (TEMP_FAILURE_RETRY(ioctl(ashmemFd_, ASHMEM_GET_PURGEABLE)) == 1) {
131 isSupport_ = true;
132 }
133 Unpin();
134 return true;
135 }
136
Pin()137 bool PurgeableAshMem::Pin()
138 {
139 if (!isSupport_) {
140 return true;
141 }
142 if (ashmemFd_ > 0) {
143 TEMP_FAILURE_RETRY(ioctl(ashmemFd_, ASHMEM_PIN, &pin_));
144 PM_HILOG_DEBUG(LOG_CORE, "%{public}s: fd:%{pubilc}d PURGEABLE_GET_PIN_STATE: %{public}d",
145 __func__, ashmemFd_, ioctl(ashmemFd_, ASHMEM_GET_PIN_STATUS, &pin_));
146 } else {
147 PM_HILOG_DEBUG(LOG_CORE, "ashmemFd_ not exist!!");
148 return false;
149 }
150 return true;
151 }
152
Unpin()153 bool PurgeableAshMem::Unpin()
154 {
155 if (!isSupport_) {
156 return true;
157 }
158 if (ashmemFd_ > 0) {
159 TEMP_FAILURE_RETRY(ioctl(ashmemFd_, ASHMEM_UNPIN, &pin_));
160 PM_HILOG_DEBUG(LOG_CORE, "%{public}s: fd:%{pubilc}d PURGEABLE_GET_PIN_STATE: %{public}d",
161 __func__, ashmemFd_, ioctl(ashmemFd_, ASHMEM_GET_PIN_STATUS, &pin_));
162 } else {
163 PM_HILOG_DEBUG(LOG_CORE, "ashmemFd_ not exist!!");
164 return false;
165 }
166 return true;
167 }
168
GetPinStatus() const169 int PurgeableAshMem::GetPinStatus() const
170 {
171 int ret = 0;
172 if (!isSupport_) {
173 return ret;
174 }
175 ret = ioctl(ashmemFd_, ASHMEM_GET_PIN_STATUS, &pin_);
176 PM_HILOG_DEBUG(LOG_CORE, "%{public}s: GetPinStatus %{public}d", __func__, ret);
177 return ret;
178 }
179
AfterRebuildSucc()180 void PurgeableAshMem::AfterRebuildSucc()
181 {
182 TEMP_FAILURE_RETRY(ioctl(ashmemFd_, PURGEABLE_ASHMEM_REBUILD_SUCCESS));
183 }
184
ResizeData(size_t newSize)185 void PurgeableAshMem::ResizeData(size_t newSize)
186 {
187 if (newSize <= 0) {
188 return;
189 }
190 if (dataPtr_) {
191 if (munmap(dataPtr_, RoundUp(dataSizeInput_, PAGE_SIZE)) != 0) {
192 PM_HILOG_ERROR(LOG_CORE, "%{public}s: munmap dataPtr fail", __func__);
193 } else {
194 dataPtr_ = nullptr;
195 if (ashmemFd_ > 0) {
196 close(ashmemFd_);
197 }
198 }
199 }
200 dataSizeInput_ = newSize;
201 CreatePurgeableData_();
202 }
203
ChangeAshmemData(size_t size,int fd,void * data)204 bool PurgeableAshMem::ChangeAshmemData(size_t size, int fd, void *data)
205 {
206 if (dataPtr_) {
207 if (munmap(dataPtr_, RoundUp(dataSizeInput_, PAGE_SIZE)) != 0) {
208 PM_HILOG_ERROR(LOG_CORE, "%{public}s: munmap dataPtr fail", __func__);
209 } else {
210 dataPtr_ = nullptr;
211 if (ashmemFd_ > 0) {
212 close(ashmemFd_);
213 }
214 }
215 }
216 dataSizeInput_ = size;
217 ashmemFd_ = fd;
218 dataPtr_ = data;
219 buildDataCount_++;
220 isChange_ = true;
221 TEMP_FAILURE_RETRY(ioctl(ashmemFd_, ASHMEM_SET_PURGEABLE));
222 if (TEMP_FAILURE_RETRY(ioctl(ashmemFd_, ASHMEM_GET_PURGEABLE)) == 1) {
223 isSupport_ = true;
224 }
225 Unpin();
226 return true;
227 }
228
ToString() const229 inline std::string PurgeableAshMem::ToString() const
230 {
231 return "";
232 }
233 } /* namespace PurgeableMem */
234 } /* namespace OHOS */
235