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