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