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 43namespace OHOS { 44namespace HDI { 45namespace Power { 46namespace 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 54constexpr int32_t SWAP_HEADER_INFO_VERSION = 1; 55constexpr int32_t SWAP_HEADER_INFO_UUID_OFFSET = 3; 56constexpr int32_t SWAP_HEADER_MAGIC_SIZE = 10; 57constexpr int32_t SWAP_HEADER_SIZE = 129; 58constexpr int32_t SWAP_HEADER_INFO_BOOTBITS_SIZE = 1024; 59constexpr int32_t SWAP_HEADER_BUF_LEN = 1024; 60constexpr int32_t FILE_MAP_BUF_LEN = 2048; 61// The swap file size, which can be configured in subsequent version. 62constexpr uint64_t SWAP_FILE_SIZE = 17179869184; // 16G 63constexpr uint32_t SWAP_FILE_MODE = 0600; 64 65constexpr int32_t UUID_VERSION_OFFSET = 6; 66constexpr int32_t UUID_CLOCK_OFFSET = 8; 67constexpr int32_t UUID_BUF_LEN = 16; 68constexpr unsigned char UUID_VERSION_OPERAND1 = 0x0F; 69constexpr unsigned char UUID_VERSION_OPERAND2 = 0x40; 70constexpr unsigned char UUID_CLOCK_OPERAND1 = 0x3F; 71constexpr unsigned char UUID_CLOCK_OPERAND2 = 0x80; 72 73constexpr const char * const SWAP_FILE_PATH = "/data/power/swapfile"; 74constexpr const char * const SWAP_DIR_PATH = "/data/power"; 75constexpr const char * const HIBERNATE_RESUME = "/sys/hibernate/resume"; 76constexpr const char * const SYS_POWER_RESUME = "/sys/power/resume"; 77constexpr const char * const SYS_POWER_RESUME_OFFSET = "/sys/power/resume_offset"; 78constexpr const char * const HIBERNATE_STATE_PATH = "/sys/power/state"; 79constexpr const char * const HIBERNATE_STATE = "disk"; 80 81struct SwapfileCfg { 82 unsigned long long len; 83}; 84 85static 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 96void Hibernate::Init() 97{ 98 HDF_LOGI("hibernate init begin."); 99 auto myThread = std::thread([this] { this->InitSwap(); }); 100 myThread.detach(); 101} 102 103int32_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 136void 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 169int32_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 / static_cast<unsigned int>(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 222int32_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 245bool Hibernate::IsSwapFileExist() 246{ 247 return access(SWAP_FILE_PATH, F_OK) == 0; 248} 249 250int32_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 270int32_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 297int32_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 312int32_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 324int32_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 351int32_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 375int32_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 398int32_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 416int32_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 443int32_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 unsigned 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