1 /*
2 * Copyright (c) 2024 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 "hibernate.h"
17
18 #include <fstream>
19 #include <sstream>
20 #include <string>
21 #include <vector>
22 #include <cinttypes>
23 #include <cstdio>
24 #include <thread>
25 #include <fcntl.h>
26 #include <securec.h>
27 #include <sys/stat.h>
28 #include <sys/random.h>
29 #include <sys/swap.h>
30 #include <sys/sysinfo.h>
31 #include <linux/fs.h>
32 #include <linux/fiemap.h>
33 #include <unistd.h>
34 #include <sys/ioctl.h>
35 #include <mntent.h>
36
37 #include <unique_fd.h>
38 #include <hdf_base.h>
39 #include <hdf_log.h>
40 #include <file_ex.h>
41
42
43 namespace OHOS {
44 namespace HDI {
45 namespace Power {
46 namespace V1_2 {
47
48 #ifndef HMFS_IOCTL_MAGIC
49 #define HMFS_IOCTL_MAGIC 0xf5
50 #endif
51
52 #define HMFS_IOC_SWAPFILE_PREALLOC _IOWR(HMFS_IOCTL_MAGIC, 32, uint32_t)
53
54 constexpr int32_t SWAP_HEADER_INFO_VERSION = 1;
55 constexpr int32_t SWAP_HEADER_INFO_UUID_OFFSET = 3;
56 constexpr int32_t SWAP_HEADER_MAGIC_SIZE = 10;
57 constexpr int32_t SWAP_HEADER_SIZE = 129;
58 constexpr int32_t SWAP_HEADER_INFO_BOOTBITS_SIZE = 1024;
59 constexpr int32_t SWAP_HEADER_BUF_LEN = 1024;
60 constexpr int32_t FILE_MAP_BUF_LEN = 2048;
61 // The swap file size, which can be configured in subsequent version.
62 constexpr uint64_t SWAP_FILE_SIZE = 17179869184; // 16G
63 constexpr uint32_t SWAP_FILE_MODE = 0660;
64
65 constexpr int32_t UUID_VERSION_OFFSET = 6;
66 constexpr int32_t UUID_CLOCK_OFFSET = 8;
67 constexpr int32_t UUID_BUF_LEN = 16;
68 constexpr unsigned char UUID_VERSION_OPERAND1 = 0x0F;
69 constexpr unsigned char UUID_VERSION_OPERAND2 = 0x40;
70 constexpr unsigned char UUID_CLOCK_OPERAND1 = 0x3F;
71 constexpr unsigned char UUID_CLOCK_OPERAND2 = 0x80;
72
73 constexpr const char * const SWAP_FILE_PATH = "/data/power/swapfile";
74 constexpr const char * const SWAP_DIR_PATH = "/data/power";
75 constexpr const char * const HIBERNATE_RESUME = "/sys/hibernate/resume";
76 constexpr const char * const SYS_POWER_RESUME = "/sys/power/resume";
77 constexpr const char * const SYS_POWER_RESUME_OFFSET = "/sys/power/resume_offset";
78 constexpr const char * const HIBERNATE_STATE_PATH = "/sys/power/state";
79 constexpr const char * const HIBERNATE_STATE = "disk";
80
81 struct SwapfileCfg {
82 unsigned long long len;
83 };
84
UlongLen(unsigned long arg)85 static int UlongLen(unsigned long arg)
86 {
87 int l = 0;
88 arg >>= 1;
89 while (arg) {
90 l++;
91 arg >>= 1;
92 }
93 return l;
94 }
95
Init()96 void Hibernate::Init()
97 {
98 HDF_LOGI("hibernate init begin.");
99 auto myThread = std::thread([this] { this->InitSwap(); });
100 myThread.detach();
101 }
102
GetResumeInfo(std::string & resumeInfo)103 int32_t Hibernate::GetResumeInfo(std::string &resumeInfo)
104 {
105 FILE *fp;
106 struct mntent *me;
107 int32_t ret = HDF_FAILURE;
108 if (!(fp = setmntent("/proc/mounts", "r"))) {
109 HDF_LOGE("open file failed, errno = %{public}d", errno);
110 return ret;
111 }
112 while ((me = getmntent(fp))) {
113 if (strcmp(me->mnt_dir, "/data") == 0) {
114 char resolvedPath[PATH_MAX] = {0};
115 if (realpath(me->mnt_fsname, resolvedPath) == nullptr) {
116 HDF_LOGE("realpath error, errno = %{public}d", errno);
117 break;
118 }
119 std::string fileSystemInfo = resolvedPath;
120 auto index = fileSystemInfo.find_last_of('/');
121 if (index == std::string::npos) {
122 HDF_LOGE("file system info error");
123 break;
124 }
125 auto partitionNum = fileSystemInfo.substr(index + 1);
126 HDF_LOGI("partition num: %{public}s", partitionNum.c_str());
127 resumeInfo = "/dev/" + partitionNum;
128 ret = HDF_SUCCESS;
129 break;
130 }
131 }
132 endmntent(fp);
133 return ret;
134 }
135
InitSwap()136 void Hibernate::InitSwap()
137 {
138 std::lock_guard<std::mutex> lock(initMutex_);
139 if (swapFileReady_) {
140 HDF_LOGI("swap file is ready, do nothing.");
141 return;
142 }
143 bool needToCreateSwapFile;
144 auto ret = CheckSwapFile(needToCreateSwapFile);
145 if (ret != HDF_SUCCESS) {
146 return;
147 }
148
149 if (needToCreateSwapFile) {
150 ret = CreateSwapFile();
151 if (ret != HDF_SUCCESS) {
152 return;
153 }
154 ret = MkSwap();
155 if (ret != HDF_SUCCESS) {
156 HDF_LOGI("init swap failed");
157 RemoveSwapFile();
158 return;
159 }
160 }
161
162 ret = WriteOffsetAndResume();
163 if (ret != HDF_SUCCESS) {
164 return;
165 }
166 swapFileReady_ = true;
167 }
168
MkSwap()169 int32_t Hibernate::MkSwap()
170 {
171 int fd = open(SWAP_FILE_PATH, O_RDWR);
172 if (fd < 0) {
173 HDF_LOGE("open swap file failed when mkswap");
174 return HDF_FAILURE;
175 }
176 int32_t retvalue = HDF_FAILURE;
177 do {
178 int pagesize = sysconf(_SC_PAGE_SIZE);
179 if (pagesize == 0) {
180 break;
181 }
182 unsigned int pages = (SWAP_FILE_SIZE / pagesize) - 1;
183 char buff[SWAP_HEADER_BUF_LEN];
184 uint32_t *swap = reinterpret_cast<uint32_t *>(buff);
185
186 swap[0] = SWAP_HEADER_INFO_VERSION;
187 swap[1] = pages;
188 if (lseek(fd, SWAP_HEADER_INFO_BOOTBITS_SIZE, SEEK_SET) < 0) {
189 HDF_LOGE("skip bootbits failed when mkswap.");
190 break;
191 }
192
193 char *uuid = reinterpret_cast<char *>(swap + SWAP_HEADER_INFO_UUID_OFFSET);
194 if (getrandom(uuid, UUID_BUF_LEN, GRND_RANDOM) != UUID_BUF_LEN) {
195 HDF_LOGE("create uuid failed when mkswap.");
196 break;
197 }
198 uuid[UUID_VERSION_OFFSET] = (uuid[UUID_VERSION_OFFSET] & UUID_VERSION_OPERAND1) | UUID_VERSION_OPERAND2;
199 uuid[UUID_CLOCK_OFFSET] = (uuid[UUID_CLOCK_OFFSET] & UUID_CLOCK_OPERAND1) | UUID_CLOCK_OPERAND2;
200 size_t len = SWAP_HEADER_SIZE * sizeof(uint32_t);
201 auto ret = write(fd, swap, len);
202 if (ret < 0 || static_cast<size_t>(ret) != len) {
203 HDF_LOGE("write swap header info failed when mkswap.");
204 break;
205 }
206 if (lseek(fd, pagesize - SWAP_HEADER_MAGIC_SIZE, SEEK_SET) < 0) {
207 HDF_LOGE("seek magic of swap failed when mkswap");
208 break;
209 }
210 if (write(fd, "SWAPSPACE2", SWAP_HEADER_MAGIC_SIZE) != SWAP_HEADER_MAGIC_SIZE) {
211 HDF_LOGE("write magic of swap failed when mkswap");
212 break;
213 }
214 fsync(fd);
215 retvalue = HDF_SUCCESS;
216 HDF_LOGI("mkswap success");
217 } while (0);
218 close(fd);
219 return retvalue;
220 }
221
CheckSwapFile(bool & needToCreateSwapFile)222 int32_t Hibernate::CheckSwapFile(bool &needToCreateSwapFile)
223 {
224 needToCreateSwapFile = false;
225 if (!IsSwapFileExist()) {
226 needToCreateSwapFile = true;
227 HDF_LOGI("CheckSwapFile, need to create swap file.");
228 return HDF_SUCCESS;
229 }
230 bool isRightSize;
231 if (CheckSwapFileSize(isRightSize) != HDF_SUCCESS) {
232 return HDF_FAILURE;
233 }
234 if (!isRightSize) {
235 needToCreateSwapFile = true;
236 HDF_LOGI("swapfile size was changed, will remove old swapfile.");
237 if (RemoveSwapFile() != HDF_SUCCESS) {
238 return HDF_FAILURE;
239 }
240 }
241 HDF_LOGI("CheckSwapFile end.");
242 return HDF_SUCCESS;
243 }
244
IsSwapFileExist()245 bool Hibernate::IsSwapFileExist()
246 {
247 return access(SWAP_FILE_PATH, F_OK) == 0;
248 }
249
CheckSwapFileSize(bool & isRightSize)250 int32_t Hibernate::CheckSwapFileSize(bool &isRightSize)
251 {
252 HDF_LOGI("CheckSwapFileSize begin.");
253 struct stat swapFileStat;
254 auto ret = stat(SWAP_FILE_PATH, &swapFileStat);
255 if (ret != 0) {
256 HDF_LOGE("stat swap file failed, errno=%{public}d", errno);
257 return HDF_FAILURE;
258 }
259
260 isRightSize = true;
261 if (swapFileStat.st_size != SWAP_FILE_SIZE) {
262 HDF_LOGE("swap file size error, actual_size=%{public}lld expected_size=%{public}lld",
263 static_cast<long long>(swapFileStat.st_size), static_cast<long long>(SWAP_FILE_SIZE));
264 isRightSize = false;
265 }
266 HDF_LOGI("CheckSwapFileSize end.");
267 return HDF_SUCCESS;
268 }
269
CreateSwapFile()270 int32_t Hibernate::CreateSwapFile()
271 {
272 HDF_LOGI("CreateSwapFile begin.");
273 if (access(SWAP_DIR_PATH, F_OK) != 0) {
274 HDF_LOGE("the swap dir not exist.");
275 return HDF_FAILURE;
276 }
277
278 struct SwapfileCfg cfg;
279 cfg.len = SWAP_FILE_SIZE;
280
281 int fd = open(SWAP_FILE_PATH, O_RDONLY | O_LARGEFILE | O_EXCL | O_CREAT, SWAP_FILE_MODE);
282 if (fd == -1) {
283 HDF_LOGE("open swap file failed, errno=%{public}d", errno);
284 return HDF_FAILURE;
285 }
286 int ret = ioctl(fd, HMFS_IOC_SWAPFILE_PREALLOC, &cfg);
287 if (ret != 0) {
288 HDF_LOGE("ioctl failed, ret=%{public}d", ret);
289 close(fd);
290 return HDF_FAILURE;
291 }
292 close(fd);
293 HDF_LOGI("CreateSwapFile success.");
294 return HDF_SUCCESS;
295 }
296
RemoveSwapFile()297 int32_t Hibernate::RemoveSwapFile()
298 {
299 if (swapoff(SWAP_FILE_PATH) != 0) {
300 HDF_LOGE("swap off failed when remove swap file, errno=%{public}d", errno);
301 }
302
303 if (remove(SWAP_FILE_PATH) != 0) {
304 HDF_LOGE("remove swap file failed, errno=%{public}d", errno);
305 return HDF_FAILURE;
306 }
307
308 HDF_LOGI("remove swap file success.");
309 return HDF_SUCCESS;
310 }
311
EnableSwap()312 int32_t Hibernate::EnableSwap()
313 {
314 HDF_LOGI("swapon begin.");
315 int ret = swapon(SWAP_FILE_PATH, 0);
316 if (ret < 0) {
317 HDF_LOGE("swapon failed, errno=%{public}d", errno);
318 return HDF_FAILURE;
319 }
320 HDF_LOGI("swapon success.");
321 return HDF_SUCCESS;
322 }
323
WriteOffsetAndResume()324 int32_t Hibernate::WriteOffsetAndResume()
325 {
326 uint64_t resumeOffset;
327 auto status = GetResumeOffset(resumeOffset);
328 if (status != HDF_SUCCESS) {
329 return HDF_FAILURE;
330 }
331 UniqueFd fd(TEMP_FAILURE_RETRY(open(HIBERNATE_RESUME, O_RDWR | O_CLOEXEC)));
332 if (fd < 0) {
333 HDF_LOGE("write offset and resume error, fd < 0, errno=%{public}d", errno);
334 return HDF_FAILURE;
335 }
336 std::string resumeInfo;
337 if (GetResumeInfo(resumeInfo) != HDF_SUCCESS) {
338 return HDF_FAILURE;
339 }
340 std::string offsetResume = std::to_string(resumeOffset) + ":" + resumeInfo;
341
342 bool ret = SaveStringToFd(fd, offsetResume.c_str());
343 if (!ret) {
344 HDF_LOGE("WriteOffsetAndResume fail");
345 return HDF_FAILURE;
346 }
347 HDF_LOGI("WriteOffsetAndResume end");
348 return HDF_SUCCESS;
349 }
350
WriteOffset()351 int32_t Hibernate::WriteOffset()
352 {
353 uint64_t resumeOffset;
354 auto status = GetResumeOffset(resumeOffset);
355 if (status != HDF_SUCCESS) {
356 return HDF_FAILURE;
357 }
358 UniqueFd fd(TEMP_FAILURE_RETRY(open(SYS_POWER_RESUME_OFFSET, O_RDWR | O_CLOEXEC)));
359 if (fd < 0) {
360 HDF_LOGE("write offset error, fd < 0, errno=%{public}d", errno);
361 return HDF_FAILURE;
362 }
363
364 std::string offset = std::to_string(resumeOffset);
365
366 bool ret = SaveStringToFd(fd, offset.c_str());
367 if (!ret) {
368 HDF_LOGE("WriteOffset fail");
369 return HDF_FAILURE;
370 }
371 HDF_LOGI("WriteOffset end");
372 return HDF_SUCCESS;
373 }
374
WriteResume()375 int32_t Hibernate::WriteResume()
376 {
377 UniqueFd fd(TEMP_FAILURE_RETRY(open(SYS_POWER_RESUME, O_RDWR | O_CLOEXEC)));
378 if (fd < 0) {
379 HDF_LOGE("write resume error, fd < 0, errno=%{public}d", errno);
380 return HDF_FAILURE;
381 }
382
383 std::string resumeInfo;
384 if (GetResumeInfo(resumeInfo) != HDF_SUCCESS) {
385 return HDF_FAILURE;
386 }
387
388 bool ret = SaveStringToFd(fd, resumeInfo);
389 if (!ret) {
390 HDF_LOGE("WriteResume fail");
391 return HDF_FAILURE;
392 }
393
394 HDF_LOGI("WriteResume end");
395 return HDF_SUCCESS;
396 }
397
WritePowerState()398 int32_t Hibernate::WritePowerState()
399 {
400 UniqueFd fd(TEMP_FAILURE_RETRY(open(HIBERNATE_STATE_PATH, O_RDWR | O_CLOEXEC)));
401 if (fd < 0) {
402 HDF_LOGE("WritePowerState error, fd < 0, errno=%{public}d", errno);
403 return HDF_FAILURE;
404 }
405
406 bool ret = SaveStringToFd(fd, HIBERNATE_STATE);
407 if (!ret) {
408 HDF_LOGE("WritePowerState fail");
409 return HDF_FAILURE;
410 }
411
412 HDF_LOGE("WritePowerState end");
413 return HDF_SUCCESS;
414 }
415
DoHibernate()416 int32_t Hibernate::DoHibernate()
417 {
418 InitSwap();
419 if (EnableSwap() != HDF_SUCCESS) {
420 return HDF_FAILURE;
421 }
422 int32_t ret = HDF_SUCCESS;
423 do {
424 if (WriteResume() != HDF_SUCCESS) {
425 ret = HDF_FAILURE;
426 break;
427 }
428 if (WriteOffset() != HDF_SUCCESS) {
429 ret = HDF_FAILURE;
430 break;
431 }
432 if (WritePowerState() != HDF_SUCCESS) {
433 ret = HDF_FAILURE;
434 break;
435 }
436 } while (0);
437 if (swapoff(SWAP_FILE_PATH) != 0) {
438 HDF_LOGE("swap off failed, errno=%{public}d", errno);
439 }
440 return ret;
441 }
442
GetResumeOffset(uint64_t & resumeOffset)443 int32_t Hibernate::GetResumeOffset(uint64_t &resumeOffset)
444 {
445 int fd = open(SWAP_FILE_PATH, O_RDONLY);
446 if (fd < 0) {
447 HDF_LOGE("open swap file failed, errno=%{public}d", errno);
448 return HDF_FAILURE;
449 }
450
451 struct stat fileStat;
452 int rc = stat(SWAP_FILE_PATH, &fileStat);
453 if (rc != 0) {
454 HDF_LOGE("stat swap file failed, errno=%{public}d", errno);
455 close(fd);
456 return HDF_FAILURE;
457 }
458
459 __u64 buf[FILE_MAP_BUF_LEN];
460 unsigned long flags = 0;
461 struct fiemap *swapFileFiemap = reinterpret_cast<struct fiemap *>(buf);
462 struct fiemap_extent *swapFileFmExt = &swapFileFiemap->fm_extents[0];
463 int count = (sizeof(buf) - sizeof(*swapFileFiemap)) / sizeof(struct fiemap_extent);
464
465 if (memset_s(swapFileFiemap, sizeof(buf), 0, sizeof(struct fiemap)) != EOK) {
466 close(fd);
467 return HDF_FAILURE;
468 }
469
470 swapFileFiemap->fm_length = ~0ULL;
471 swapFileFiemap->fm_flags = flags;
472 swapFileFiemap->fm_extent_count = count;
473
474 rc = ioctl(fd, FS_IOC_FIEMAP, reinterpret_cast<unsigned long>(swapFileFiemap));
475 if (rc != 0) {
476 HDF_LOGE("get swap file physical blk fail, rc=%{public}d", rc);
477 close(fd);
478 return HDF_FAILURE;
479 }
480
481 resumeOffset = swapFileFmExt[0].fe_physical >> UlongLen(fileStat.st_blksize);
482 HDF_LOGI("resume offset size: %{public}lld", static_cast<long long>(resumeOffset));
483 close(fd);
484 return HDF_SUCCESS;
485 }
486 } // namespace V1_2
487 } // namespace Power
488 } // namespace HDI
489 } // namespace OHOS
490