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