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 <sys/mman.h> /* mmap */
17
18 #include "securec.h"
19 #include "../../common/include/pm_util.h"
20 #include "../../common/include/pm_state_c.h"
21 #include "pm_smartptr_util.h"
22 #include "purgeable_mem.h"
23
24 namespace OHOS {
25 namespace PurgeableMem {
26 #ifdef LOG_TAG
27 #undef LOG_TAG
28 #endif
29 #define LOG_TAG "PurgeableMem"
30
RoundUp_(size_t val,size_t align)31 static inline size_t RoundUp_(size_t val, size_t align)
32 {
33 if (align == 0) {
34 return val;
35 }
36 return ((val + align - 1) / align) * align;
37 }
38
PurgeableMem(size_t dataSize,std::unique_ptr<PurgeableMemBuilder> builder)39 PurgeableMem::PurgeableMem(size_t dataSize, std::unique_ptr<PurgeableMemBuilder> builder)
40 {
41 dataPtr_ = nullptr;
42 builder_ = nullptr;
43 pageTable_ = nullptr;
44 buildDataCount_ = 0;
45
46 if (dataSize == 0) {
47 return;
48 }
49 dataSizeInput_ = dataSize;
50 IF_NULL_LOG_ACTION(builder, "%{public}s: input builder nullptr", return);
51
52 size_t size = RoundUp_(dataSizeInput_, PAGE_SIZE);
53 int type = MAP_ANONYMOUS;
54 type |= (UxpteIsEnabled() ? MAP_PURGEABLE : MAP_PRIVATE);
55 dataPtr_ = mmap(nullptr, size, PROT_READ | PROT_WRITE, type, -1, 0);
56 if (dataPtr_ == MAP_FAILED) {
57 HILOG_ERROR(LOG_CORE, "%{public}s: mmap fail", __func__);
58 dataPtr_ = nullptr;
59 return;
60 }
61
62 builder_ = std::move(builder);
63
64 MAKE_UNIQUE(pageTable_, UxPageTable, "constructor uxpt make_unique fail", return, (uint64_t)dataPtr_, size);
65 HILOG_DEBUG(LOG_CORE, "%{public}s init succ. %{public}s", __func__, ToString_().c_str());
66 }
67
~PurgeableMem()68 PurgeableMem::~PurgeableMem()
69 {
70 HILOG_DEBUG(LOG_CORE, "%{public}s %{public}s", __func__, ToString_().c_str());
71 if (dataPtr_) {
72 if (munmap(dataPtr_, RoundUp_(dataSizeInput_, PAGE_SIZE)) != 0) {
73 HILOG_ERROR(LOG_CORE, "%{public}s: munmap dataPtr fail", __func__);
74 } else {
75 if (UxpteIsEnabled() && !IsPurged_()) {
76 HILOG_ERROR(LOG_CORE, "%{public}s: munmap dataPtr succ, but uxpte present", __func__);
77 }
78 dataPtr_ = nullptr;
79 }
80 }
81 builder_.reset();
82 pageTable_.reset();
83 }
84
BeginRead()85 bool PurgeableMem::BeginRead()
86 {
87 bool succ = false;
88 bool ret = false;
89 HILOG_DEBUG(LOG_CORE, "%{public}s %{public}s", __func__, ToString_().c_str());
90 IF_NULL_LOG_ACTION(dataPtr_, "dataPtr is nullptr in BeginRead", return false);
91 IF_NULL_LOG_ACTION(pageTable_, "pageTable_ is nullptr in BeginRead", return false);
92 IF_NULL_LOG_ACTION(builder_, "builder_ is nullptr in BeginRead", return false);
93 pageTable_->GetUxpte((uint64_t)dataPtr_, dataSizeInput_);
94 PMState err = PM_OK;
95 while (true) {
96 try {
97 rwlock_.lock_shared();
98 } catch (...) {
99 err = PM_LOCK_READ_FAIL;
100 break;
101 }
102 if (!IsPurged_()) {
103 HILOG_INFO(LOG_CORE, "%{public}s: not purged, return true. MAP_PUR=0x%{public}x",
104 __func__, MAP_PURGEABLE);
105 ret = true;
106 break;
107 }
108 /* data is purged, will rebuild it */
109 rwlock_.unlock_shared();
110 try {
111 rwlock_.lock();
112 } catch (...) {
113 err = PM_LOCK_WRITE_FAIL;
114 break;
115 }
116 if (IsPurged_()) {
117 succ = BuildContent_();
118 HILOG_INFO(LOG_CORE, "%{public}s: purged, built %{public}s", __func__, succ ? "succ" : "fail");
119 }
120 rwlock_.unlock();
121 if (!succ) {
122 err = PMB_BUILD_ALL_FAIL;
123 break;
124 }
125 }
126
127 if (!ret) {
128 HILOG_ERROR(LOG_CORE, "%{public}s: err %{public}s, UxptePut.", __func__, GetPMStateName(err));
129 pageTable_->PutUxpte((uint64_t)dataPtr_, dataSizeInput_);
130 }
131 return ret;
132 }
133
EndRead()134 void PurgeableMem::EndRead()
135 {
136 HILOG_DEBUG(LOG_CORE, "%{public}s %{public}s", __func__, ToString_().c_str());
137 IF_NULL_LOG_ACTION(pageTable_, "pageTable_ is nullptr in EndRead", return);
138 rwlock_.unlock_shared();
139 pageTable_->PutUxpte((uint64_t)dataPtr_, dataSizeInput_);
140 }
141
BeginWrite()142 bool PurgeableMem::BeginWrite()
143 {
144 HILOG_DEBUG(LOG_CORE, "%{public}s %{public}s", __func__, ToString_().c_str());
145
146 IF_NULL_LOG_ACTION(dataPtr_, "dataPtr is nullptr in BeginWrite", return false);
147 IF_NULL_LOG_ACTION(pageTable_, "pageTable_ is nullptrin BeginWrite", return false);
148 IF_NULL_LOG_ACTION(builder_, "builder_ is nullptr in BeginWrite", return false);
149
150 pageTable_->GetUxpte((uint64_t)dataPtr_, dataSizeInput_);
151 PMState err = PM_OK;
152 do {
153 try {
154 rwlock_.lock();
155 } catch (...) {
156 err = PM_LOCK_WRITE_FAIL;
157 break;
158 }
159 if (!IsPurged_()) {
160 /* data is not purged, return true */
161 break;
162 }
163 /* data purged, rebuild it */
164 if (BuildContent_()) {
165 /* data rebuild succ, return true */
166 break;
167 }
168 err = PMB_BUILD_ALL_FAIL;
169 } while (0);
170
171 if (err == PM_OK) {
172 return true;
173 }
174
175 rwlock_.unlock();
176 HILOG_ERROR(LOG_CORE, "%{public}s: err %{public}s, UxptePut.", __func__, GetPMStateName(err));
177 pageTable_->PutUxpte((uint64_t)dataPtr_, dataSizeInput_);
178 return false;
179 }
180
EndWrite()181 void PurgeableMem::EndWrite()
182 {
183 HILOG_DEBUG(LOG_CORE, "%{public}s %{public}s", __func__, ToString_().c_str());
184 IF_NULL_LOG_ACTION(pageTable_, "pageTable_ is nullptr in EndWrite", return);
185 rwlock_.unlock();
186 pageTable_->PutUxpte((uint64_t)dataPtr_, dataSizeInput_);
187 }
188
GetContent()189 void* PurgeableMem::GetContent()
190 {
191 return dataPtr_;
192 }
193
GetContentSize()194 size_t PurgeableMem::GetContentSize()
195 {
196 return dataSizeInput_;
197 }
198
ModifyContentByBuilder(std::unique_ptr<PurgeableMemBuilder> modifier)199 bool PurgeableMem::ModifyContentByBuilder(std::unique_ptr<PurgeableMemBuilder> modifier)
200 {
201 IF_NULL_LOG_ACTION(modifier, "input modifier is nullptr", return false);
202 /* apply modify */
203 bool succ = modifier->Build(dataPtr_, dataSizeInput_);
204 if (!succ) {
205 return false;
206 }
207 /* log modify */
208 if (builder_) {
209 builder_->AppendBuilder(std::move(modifier));
210 } else {
211 builder_ = std::move(modifier);
212 }
213 return true;
214 }
215
IsPurged_()216 bool PurgeableMem::IsPurged_()
217 {
218 if (buildDataCount_ == 0) {
219 HILOG_INFO(LOG_CORE, "%{public}s, has never built, return true", __func__);
220 return true;
221 }
222 return !(pageTable_->CheckPresent((uint64_t)dataPtr_, dataSizeInput_));
223 }
224
BuildContent_()225 bool PurgeableMem::BuildContent_()
226 {
227 bool succ = false;
228 /* clear content before rebuild */
229 if (memset_s(dataPtr_, RoundUp_(dataSizeInput_, PAGE_SIZE), 0, dataSizeInput_) != EOK) {
230 HILOG_ERROR(LOG_CORE, "%{public}s, clear content fail", __func__);
231 return succ;
232 }
233 /* builder_ and dataPtr_ is never nullptr since it is checked by BeginAccess() before */
234 succ = builder_->BuildAll(dataPtr_, dataSizeInput_);
235 if (succ) {
236 buildDataCount_++;
237 }
238 return succ;
239 }
240
ToString_() const241 inline std::string PurgeableMem::ToString_() const
242 {
243 std::string dataptrStr = dataPtr_ ? std::to_string((unsigned long long)dataPtr_) : "0";
244 std::string pageTableStr = pageTable_ ? pageTable_->ToString() : "0";
245 return "dataAddr:" + dataptrStr + " dataSizeInput:" + std::to_string(dataSizeInput_) +
246 " " + pageTableStr;
247 }
248 } /* namespace PurgeableMem */
249 } /* namespace OHOS */
250