/* * Copyright (c) 2023 Huawei Device Co., Ltd. * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #include <sys/mman.h> /* mmap */ #include "securec.h" #include "pm_util.h" #include "pm_state_c.h" #include "pm_smartptr_util.h" #include "pm_log.h" #include "purgeable_ashmem.h" namespace OHOS { namespace PurgeableMem { #ifdef LOG_TAG #undef LOG_TAG #endif #define LOG_TAG "PurgeableMem" static inline size_t RoundUp(size_t val, size_t align) { if (val + align < val || val + align < align) { PM_HILOG_ERROR(LOG_CORE, "%{public}s: Addition overflow!", __func__); return val; } if (align == 0) { return val; } return ((val + align - 1) / align) * align; } PurgeableAshMem::PurgeableAshMem(std::unique_ptr<PurgeableMemBuilder> builder) { dataPtr_ = nullptr; builder_ = nullptr; ashmemFd_ = -1; buildDataCount_ = 0; isSupport_ = false; isChange_ = false; IF_NULL_LOG_ACTION(builder, "%{public}s: input builder nullptr", return); builder_ = std::move(builder); PM_HILOG_DEBUG(LOG_CORE, "%{public}s init succ. %{public}s", __func__, ToString().c_str()); } PurgeableAshMem::PurgeableAshMem(size_t dataSize, std::unique_ptr<PurgeableMemBuilder> builder) { dataPtr_ = nullptr; builder_ = nullptr; ashmemFd_ = -1; buildDataCount_ = 0; isSupport_ = false; isChange_ = false; if (dataSize == 0) { return; } dataSizeInput_ = dataSize; IF_NULL_LOG_ACTION(builder, "%{public}s: input builder nullptr", return); if (!CreatePurgeableData()) { PM_HILOG_DEBUG(LOG_CORE, "Failed to create purgeabledata"); return; } builder_ = std::move(builder); PM_HILOG_DEBUG(LOG_CORE, "%{public}s init succ. %{public}s", __func__, ToString().c_str()); } PurgeableAshMem::~PurgeableAshMem() { PM_HILOG_DEBUG(LOG_CORE, "%{public}s %{public}s", __func__, ToString().c_str()); if (!isChange_ && dataPtr_) { if (munmap(dataPtr_, RoundUp(dataSizeInput_, PAGE_SIZE)) != 0) { PM_HILOG_ERROR(LOG_CORE, "%{public}s: munmap dataPtr fail", __func__); } else { if (UxpteIsEnabled() && !IsPurged()) { PM_HILOG_ERROR(LOG_CORE, "%{public}s: munmap dataPtr succ, but uxpte present", __func__); } dataPtr_ = nullptr; close(ashmemFd_); } } builder_.reset(); } int PurgeableAshMem::GetAshmemFd() { return ashmemFd_; } bool PurgeableAshMem::IsPurged() { if (!isSupport_) { return false; } int ret = ioctl(ashmemFd_, PURGEABLE_ASHMEM_IS_PURGED); PM_HILOG_DEBUG(LOG_CORE, "%{public}s: IsPurged %{public}d", __func__, ret); return ret > 0 ? true : false; } bool PurgeableAshMem::CreatePurgeableData() { PM_HILOG_DEBUG(LOG_CORE, "%{public}s", __func__); if (dataSizeInput_ == 0) { return false; } size_t size = RoundUp(dataSizeInput_, PAGE_SIZE); int fd = AshmemCreate("PurgeableAshmem", size); if (fd < 0) { return false; } if (AshmemSetProt(fd, PROT_READ | PROT_WRITE) < 0) { close(fd); return false; } ashmemFd_ = fd; pin_ = { static_cast<uint32_t>(0), static_cast<uint32_t>(0) }; dataPtr_ = mmap(nullptr, size, PROT_READ | PROT_WRITE, MAP_SHARED, ashmemFd_, 0); if (dataPtr_ == MAP_FAILED) { PM_HILOG_ERROR(LOG_CORE, "%{public}s: mmap fail", __func__); dataPtr_ = nullptr; close(ashmemFd_); return false; } TEMP_FAILURE_RETRY(ioctl(ashmemFd_, ASHMEM_SET_PURGEABLE)); if (TEMP_FAILURE_RETRY(ioctl(ashmemFd_, ASHMEM_GET_PURGEABLE)) == 1) { isSupport_ = true; } Unpin(); return true; } bool PurgeableAshMem::Pin() { if (!isSupport_) { return true; } if (ashmemFd_ > 0) { TEMP_FAILURE_RETRY(ioctl(ashmemFd_, ASHMEM_PIN, &pin_)); PM_HILOG_DEBUG(LOG_CORE, "%{public}s: fd:%{public}d PURGEABLE_GET_PIN_STATE: %{public}d", __func__, ashmemFd_, ioctl(ashmemFd_, ASHMEM_GET_PIN_STATUS, &pin_)); } else { PM_HILOG_DEBUG(LOG_CORE, "ashmemFd_ not exist!!"); return false; } return true; } bool PurgeableAshMem::Unpin() { if (!isSupport_) { return true; } if (ashmemFd_ > 0) { TEMP_FAILURE_RETRY(ioctl(ashmemFd_, ASHMEM_UNPIN, &pin_)); PM_HILOG_DEBUG(LOG_CORE, "%{public}s: fd:%{public}d PURGEABLE_GET_PIN_STATE: %{public}d", __func__, ashmemFd_, ioctl(ashmemFd_, ASHMEM_GET_PIN_STATUS, &pin_)); } else { PM_HILOG_DEBUG(LOG_CORE, "ashmemFd_ not exist!!"); return false; } return true; } int PurgeableAshMem::GetPinStatus() const { int ret = 0; if (!isSupport_) { return ret; } ret = ioctl(ashmemFd_, ASHMEM_GET_PIN_STATUS, &pin_); PM_HILOG_DEBUG(LOG_CORE, "%{public}s: GetPinStatus %{public}d", __func__, ret); return ret; } void PurgeableAshMem::AfterRebuildSucc() { TEMP_FAILURE_RETRY(ioctl(ashmemFd_, PURGEABLE_ASHMEM_REBUILD_SUCCESS)); } void PurgeableAshMem::ResizeData(size_t newSize) { if (newSize <= 0 || newSize >= OHOS_MAXIMUM_PURGEABLE_MEMORY) { PM_HILOG_DEBUG(LOG_CORE, "Failed to apply for memory"); return; } if (dataPtr_) { if (munmap(dataPtr_, RoundUp(dataSizeInput_, PAGE_SIZE)) != 0) { PM_HILOG_ERROR(LOG_CORE, "%{public}s: munmap dataPtr fail", __func__); } else { dataPtr_ = nullptr; if (ashmemFd_ > 0) { close(ashmemFd_); } } } dataSizeInput_ = newSize; if (!CreatePurgeableData()) { PM_HILOG_DEBUG(LOG_CORE, "Failed to create purgeabledata"); return; } } bool PurgeableAshMem::ChangeAshmemData(size_t size, int fd, void *data) { if (size <= 0 || size >= OHOS_MAXIMUM_PURGEABLE_MEMORY) { PM_HILOG_DEBUG(LOG_CORE, "Failed to apply for memory"); return false; } if (dataPtr_) { if (munmap(dataPtr_, RoundUp(dataSizeInput_, PAGE_SIZE)) != 0) { PM_HILOG_ERROR(LOG_CORE, "%{public}s: munmap dataPtr fail", __func__); } else { dataPtr_ = nullptr; if (ashmemFd_ > 0) { close(ashmemFd_); } } } dataSizeInput_ = size; ashmemFd_ = fd; dataPtr_ = data; buildDataCount_++; isChange_ = true; TEMP_FAILURE_RETRY(ioctl(ashmemFd_, ASHMEM_SET_PURGEABLE)); if (TEMP_FAILURE_RETRY(ioctl(ashmemFd_, ASHMEM_GET_PURGEABLE)) == 1) { isSupport_ = true; } Unpin(); return true; } inline std::string PurgeableAshMem::ToString() const { return ""; } } /* namespace PurgeableMem */ } /* namespace OHOS */