* Copyright (c) 2022 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 "ufs_ptable.h"
#include <algorithm>
#include <fcntl.h>
#include <sys/types.h>
#include <unistd.h>
#include "fs_manager.h"
#include "log/log.h"
#include "securec.h"
#include "updater/updater_const.h"
#include "utils.h"
namespace Updater {
constexpr const uint32_t LUN_FOR_SLOT_A = 3;
constexpr const uint32_t LUN_FOR_SLOT_B = 4;
constexpr const uint32_t IMG_BLOCK_SIZE = 512;
constexpr const uint32_t DEVICE_BLOCK_SIZE = 4096;
constexpr const uint32_t DEFAULT_ENTRY_OFFSET = 2;
uint32_t UfsPtable::GetDeviceLunNum()
{
return deviceLunNum_;
}
uint32_t UfsPtable::GetDefaultImageSize()
{
SetDeviceLunNum();
return ptableData_.emmcGptDataLen + deviceLunNum_ * ptableData_.imgLuSize + GetPtableExtraOffset();
}
uint64_t UfsPtable::GetDeviceLunCapacity(const uint32_t lunIndex)
{
char lunIndexName = 'a' + lunIndex;
std::string capacityPath = std::string(PREFIX_SYS_CLASS_BLOCK) + lunIndexName + "/size";
uint64_t capacity = 0;
GetCapacity(capacityPath, capacity);
return capacity;
}
uint32_t UfsPtable::GetPtableExtraOffset(void)
{
return 0;
}
bool UfsPtable::MatchSuffix(std::string &suffix, const UfsPartitionDataInfo &ufsPtnInfo)
{
std::transform(suffix.begin(), suffix.end(), suffix.begin(), ::toupper);
uint32_t deviceBlockSize = GetDeviceBlockSize();
const uint8_t *nameOffset = ufsPtnInfo.data + 2 * deviceBlockSize + GPT_PARTITION_NAME_OFFSET;
std::string dispName;
ParsePartitionName(nameOffset, MAX_GPT_NAME_SIZE, dispName, MAX_GPT_NAME_SIZE / 2);
std::string dispSuffix = dispName.substr(dispName.size() - PARTITION_AB_SUFFIX_SIZE, PARTITION_AB_SUFFIX_SIZE);
if (dispSuffix == suffix) {
LOG(INFO) << "dispSuffix is " << dispSuffix << ", suffix is " << suffix;
return true;
}
return false;
}
void UfsPtable::SetLunPtnDataInfoNeedWrite(UfsPartitionDataInfo &ufsPtnInfo)
{
if (!isVabDevice_) {
ufsPtnInfo.needWrite = true;
LOG(WARNING) << "force set needWrite to true for non vab device";
return;
}
ufsPtnInfo.needWrite = isUpdaterMode_;
if (ufsPtnInfo.lunIndex < 2) {
return;
}
int updateSlot = Utils::GetUpdateSlot();
int curSlot = GetCurrentSlot();
if ((updateSlot != SLOT_A && updateSlot != SLOT_B) || (curSlot == updateSlot)) {
LOG(ERROR) << "Invalid slot: " << updateSlot << ", curSlot is " << curSlot;
return;
}
std::string suffix = isUpdaterMode_ ? ((updateSlot == SLOT_A ? PARTITION_B_SUFFIX : PARTITION_A_SUFFIX)) :
((updateSlot == SLOT_A ? PARTITION_A_SUFFIX : PARTITION_B_SUFFIX));
ufsPtnInfo.needWrite = isUpdaterMode_ ? !MatchSuffix(suffix, ufsPtnInfo) : MatchSuffix(suffix, ufsPtnInfo);
return;
}
bool UfsPtable::IsUsbPath(const uint32_t lunIndex)
{
constexpr uint32_t minRemoveableStartIdx = 3;
if (lunIndex <= minRemoveableStartIdx) {
return false;
}
char lunIndexName = 'a' + lunIndex;
const char* targetUsbString = "usb";
const char* targetXhciString = "xhci";
char linkBuf[READ_LINK_BUFFER_LENTH] = {0};
std::string filePath = std::string(PREFIX_SYS_CLASS_BLOCK) + lunIndexName;
ssize_t retSize = readlink(filePath.c_str(), linkBuf, READ_LINK_BUFFER_LENTH - 1);
LOG(INFO) << "readlink " << filePath << " retSzie " << retSize << ", linkBuf is: " << linkBuf;
if (retSize > 0 && (strstr(linkBuf, targetUsbString) != nullptr || strstr(linkBuf, targetXhciString) != nullptr)) {
return true;
}
return false;
}
bool UfsPtable::CheckDeviceLunRemoveable(const uint32_t lunIndex)
{
constexpr uint32_t minRemoveableStartIdx = 3;
if (lunIndex <= minRemoveableStartIdx) {
return false;
}
char lunIndexName = 'a' + lunIndex;
std::string removableNode = std::string(PREFIX_SYS_CLASS_BLOCK) + lunIndexName + "/removable";
std::string removableResult {};
std::ifstream fin(removableNode, std::ios::in);
if (!fin.is_open()) {
LOG(ERROR) << "open " << removableNode << " failed";
return false;
}
fin >> removableResult;
LOG(INFO) << "lun " << lunIndex << " removable result is : " << removableResult;
return removableResult == "1";
}
uint32_t UfsPtable::GetDeviceBlockSize(void)
{
return ptableData_.blockSize;
}
std::string UfsPtable::GetDeviceLunNodePath(const uint32_t lun)
{
char lunIndexName = 'a' + lun;
return std::string(PREFIX_UFS_NODE) + lunIndexName;
}
void UfsPtable::SetDeviceLunNum()
{
if (deviceLunNum_ > 0) {
return;
}
uint32_t lunIndex;
for (lunIndex = 0; lunIndex < MAX_LUN_NUMBERS; lunIndex++) {
std::string ufsNode = GetDeviceLunNodePath(lunIndex);
if (!CheckFileExist(ufsNode)) {
LOG(ERROR) << "file " << ufsNode << " is not exist";
break;
}
#ifndef UPDATER_UT
if (CheckDeviceLunRemoveable(lunIndex) || IsUsbPath(lunIndex)) {
LOG(ERROR) << "device " << ufsNode << " is removable, may be a u disk";
break;
}
#endif
}
deviceLunNum_ = lunIndex;
LOG(INFO) << "device lun num is " << deviceLunNum_;
return;
}
bool UfsPtable::ParseGptHeaderByUfsLun(const uint8_t *gptImage, const uint32_t len,
const uint32_t lun, const uint32_t blockSize)
{
GPTHeaderInfo gptHeaderInfo;
(void)memset_s(&gptHeaderInfo, sizeof(GPTHeaderInfo), 0, sizeof(GPTHeaderInfo));
if (!GetPartitionGptHeaderInfo(gptImage + blockSize, blockSize, gptHeaderInfo)) {
LOG(ERROR) << "GetPartitionGptHeaderInfo fail";
return false;
}
uint32_t deviceBlockSize = GetDeviceBlockSize();
if (deviceBlockSize == 0) {
LOG(ERROR) << "block device size invalid " << deviceBlockSize;
return false;
}
uint64_t lunDeviceSize = GetDeviceLunCapacity(lun);
uint32_t lunLbaNum = lunDeviceSize / deviceBlockSize;
HeaderCheckInputs inputs = { .gptImage = gptImage, .len = len, .lbaNum = lunLbaNum, .lunNum = lun };
return PartitionCheckGptHeader(inputs, blockSize, gptHeaderInfo);
}
bool UfsPtable::UfsReadGpt(const uint8_t *gptImage, const uint32_t len,
const uint32_t lun, const uint32_t blockSize)
{
if (gptImage == nullptr || len < ptableData_.writeDeviceLunSize || lun >= MAX_LUN_NUMBERS || blockSize == 0 ||
len < 2 * blockSize) {
LOG(ERROR) << "invaild input";
return false;
}
if (!ParseGptHeaderByUfsLun(gptImage, len, lun, blockSize)) {
LOG(ERROR) << "Primary signature invalid";
return false;
}
auto startIter = partitionInfo_.end();
for (auto it = partitionInfo_.begin(); it != partitionInfo_.end();) {
if ((*it).lun == lun) {
it = partitionInfo_.erase(it);
startIter = it;
continue;
}
it++;
}
UfsReadGptEntry(gptImage, lun, blockSize, startIter);
return true;
}
void UfsPtable::UfsReadGptEntry(const uint8_t *gptImage, const uint32_t lun,
const uint32_t blockSize, std::vector<PtnInfo>::iterator startIter)
{
uint32_t partEntryCnt = blockSize / PARTITION_ENTRY_SIZE;
uint32_t count = 0;
const uint8_t *data = nullptr;
bool tailPartFlag = false;
for (uint32_t i = 0; i < (MAX_PARTITION_NUM / partEntryCnt) && count < MAX_PARTITION_NUM; i++) {
data = gptImage + (DEFAULT_ENTRY_OFFSET + i) * blockSize;
for (uint32_t j = 0; j < partEntryCnt; j++) {
uint8_t typeGuid[GPT_PARTITION_TYPE_GUID_LEN] = {0};
if (memcpy_s(typeGuid, sizeof(typeGuid), &data[(j * PARTITION_ENTRY_SIZE)],
GPT_PARTITION_TYPE_GUID_LEN) != EOK) {
LOG(ERROR) << "memcpy guid fail";
}
if (typeGuid[0] == 0x00 && typeGuid[1] == 0x00) {
i = MAX_PARTITION_NUM / partEntryCnt;
break;
}
uint64_t firstLba = GET_LLWORD_FROM_BYTE(&data[(j * PARTITION_ENTRY_SIZE) + FIRST_LBA_OFFSET]);
uint64_t lastLba = GET_LLWORD_FROM_BYTE(&data[(j * PARTITION_ENTRY_SIZE) + LAST_LBA_OFFSET]);
PtnInfo newPtnInfo = {};
newPtnInfo.startAddr = firstLba * static_cast<uint64_t>(GetDeviceBlockSize());
newPtnInfo.writePath = GetDeviceLunNodePath(lun);
newPtnInfo.partitionSize = (lastLba - firstLba + 1) * static_cast<uint64_t>(GetDeviceBlockSize());
const uint8_t *nameOffset = data + (j * PARTITION_ENTRY_SIZE + GPT_PARTITION_NAME_OFFSET);
ParsePartitionName(nameOffset, MAX_GPT_NAME_SIZE, newPtnInfo.dispName, MAX_GPT_NAME_SIZE / 2);
SetPartitionType(newPtnInfo.dispName, newPtnInfo);
(void)memcpy_s(newPtnInfo.partitionTypeGuid, sizeof(newPtnInfo.partitionTypeGuid),
typeGuid, GPT_PARTITION_TYPE_GUID_LEN);
newPtnInfo.isTailPart = tailPartFlag;
newPtnInfo.lun = lun;
newPtnInfo.gptEntryBufOffset = static_cast<int>((DEFAULT_ENTRY_OFFSET + i) * blockSize +
j * PARTITION_ENTRY_SIZE - 2 * blockSize);
if (newPtnInfo.dispName == USERDATA_PARTITION) {
tailPartFlag = true;
usrDataPtnIndex_ = std::distance(partitionInfo_.begin(), startIter);
}
startIter = ++(partitionInfo_.insert(startIter, newPtnInfo));
count++;
}
}
if (tailPartFlag) {
endPtnIndex_ = static_cast<int>(std::distance(partitionInfo_.begin(), startIter)) - 1;
startPtnIndex_ = endPtnIndex_ + 1 - static_cast<int>(count);
hasTailpart_ = partitionInfo_[endPtnIndex_].isTailPart;
}
return;
}
void UfsPtable::UfsPatchGptHeader(UfsPartitionDataInfo &ptnDataInfo, const uint32_t blockSize)
{
uint32_t deviceBlockSize = GetDeviceBlockSize();
if (blockSize == 0 || ptnDataInfo.writeDataLen < 2 * blockSize || ptnDataInfo.lunSize == 0 ||
deviceBlockSize == 0) {
LOG(ERROR) << "invaild argument";
return;
}
uint64_t cardSizeSector = ptnDataInfo.lunSize / deviceBlockSize;
if (cardSizeSector == 0) {
cardSizeSector = DEFAULT_SECTOR_NUM;
}
uint8_t *primaryGptHeader = ptnDataInfo.data + blockSize;
uint64_t lastUsableSector = cardSizeSector - 1 - (hasBackupPtable_ ? GPT_PTABLE_BACKUP_SIZE : 0);
if (reservedSize_ != 0 && lastUsableSector > reservedSize_) {
LOG(INFO) << "reserve " << reservedSize_ << "block for " << GetDeviceLunNodePath(ptnDataInfo.lunIndex);
lastUsableSector -= reservedSize_;
}
LOG(INFO) << "cardSizeSector " << cardSizeSector << ", lastUsableSector " << lastUsableSector;
PUT_LONG_LONG(primaryGptHeader + BACKUP_HEADER_OFFSET, (cardSizeSector - 1));
PUT_LONG_LONG(primaryGptHeader + LAST_USABLE_LBA_OFFSET, lastUsableSector);
uint32_t totalPart = 0;
while (((TMP_DATA_SIZE - blockSize - blockSize) > totalPart * PARTITION_ENTRY_SIZE) &&
(*(primaryGptHeader + blockSize + totalPart * PARTITION_ENTRY_SIZE) != 0)) {
totalPart++;
}
if (totalPart == 0) {
LOG(ERROR) << "no partition exist";
return;
}
uint8_t *lastPartOffset = primaryGptHeader + blockSize + (totalPart - 1) * PARTITION_ENTRY_SIZE;
uint64_t lastLba = GET_LLWORD_FROM_BYTE(lastPartOffset + PARTITION_ENTRY_LAST_LBA);
uint64_t firstLba = GET_LLWORD_FROM_BYTE(lastPartOffset + FIRST_LBA_OFFSET);
uint64_t partitionSize = (lastLba - firstLba + 1) * deviceBlockSize;
std::string partitionName;
uint8_t *nameOffset = lastPartOffset + GPT_PARTITION_NAME_OFFSET;
ParsePartitionName(nameOffset, MAX_GPT_NAME_SIZE, partitionName, MAX_GPT_NAME_SIZE / 2);
if (partitionName == USERDATA_PARTITION || (totalPart == 1 && partitionSize == 0)) {
PUT_LONG_LONG(lastPartOffset + PARTITION_ENTRY_LAST_LBA, lastUsableSector);
LOG(INFO) << "partitionSize=" << partitionSize << ", partition_name:" << partitionName;
}
uint32_t partCount = GET_LWORD_FROM_BYTE(primaryGptHeader + PARTITION_COUNT_OFFSET);
uint32_t entrySize = GET_LWORD_FROM_BYTE(primaryGptHeader + PENTRY_SIZE_OFFSET);
uint32_t crcValue = CalculateCrc32(ptnDataInfo.data + (blockSize * 2), partCount * entrySize);
PUT_LONG(primaryGptHeader + PARTITION_CRC_OFFSET, crcValue);
PUT_LONG(primaryGptHeader + HEADER_CRC_OFFSET, 0);
crcValue = CalculateCrc32(primaryGptHeader, GPT_CRC_LEN);
PUT_LONG(primaryGptHeader + HEADER_CRC_OFFSET, crcValue);
return;
}
void UfsPtable::AddUfsPtnDataInfo(UfsPartitionDataInfo &newLunPtnDataInfo, uint32_t deviceBlockSize, uint32_t lunIndex)
{
newLunPtnDataInfo.writeDataLen = ptableData_.writeDeviceLunSize;
newLunPtnDataInfo.lunIndex = lunIndex + ptableData_.startLunNumber;
newLunPtnDataInfo.lunSize = GetDeviceLunCapacity(newLunPtnDataInfo.lunIndex);
UfsPatchGptHeader(newLunPtnDataInfo, deviceBlockSize);
newLunPtnDataInfo.isGptVaild = true;
SetLunPtnDataInfoNeedWrite(newLunPtnDataInfo);
ufsPtnDataInfo_.push_back(newLunPtnDataInfo);
}
bool UfsPtable::ParsePartitionFromBuffer(uint8_t *ptbImgBuffer, const uint32_t imgBufSize)
{
if (ptbImgBuffer == nullptr || (imgBufSize < ptableData_.emmcGptDataLen + ptableData_.imgLuSize +
GetPtableExtraOffset())) {
LOG(ERROR) << "input param invalid";
return false;
}
uint32_t imgBlockSize = ptableData_.lbaLen;
uint32_t deviceBlockSize = GetDeviceBlockSize();
if (deviceBlockSize != IMG_BLOCK_SIZE && deviceBlockSize != DEVICE_BLOCK_SIZE) {
LOG(ERROR) << "deviceBlockSize fail:" << deviceBlockSize;
return false;
}
SetDeviceLunNum();
LOG(INFO) << "lun number of ptable:" << deviceLunNum_;
ufsPtnDataInfo_.clear();
for (uint32_t i = 0; i < deviceLunNum_; i++) {
UfsPartitionDataInfo newLunPtnDataInfo;
(void)memset_s(newLunPtnDataInfo.data, TMP_DATA_SIZE, 0, TMP_DATA_SIZE);
uint8_t *lunStart = GetPtableImageUfsLunPmbrStart(ptbImgBuffer, i);
uint8_t *gptHeaderStart = GetPtableImageUfsLunGptHeaderStart(ptbImgBuffer, i);
if (!CheckProtectiveMbr(lunStart, imgBlockSize) || !CheckIfValidGpt(gptHeaderStart, imgBlockSize)) {
newLunPtnDataInfo.isGptVaild = false;
newLunPtnDataInfo.needWrite = false;
ufsPtnDataInfo_.push_back(newLunPtnDataInfo);
continue;
}
if (memcpy_s(newLunPtnDataInfo.data, TMP_DATA_SIZE, lunStart, imgBlockSize) != EOK) {
LOG(WARNING) << "memcpy_s pmbr fail";
}
if (memcpy_s(newLunPtnDataInfo.data + deviceBlockSize, TMP_DATA_SIZE - deviceBlockSize,
gptHeaderStart, imgBlockSize) != EOK) {
LOG(WARNING) << "memcpy_s gpt header fail";
}
if (memcpy_s(newLunPtnDataInfo.data + 2 * deviceBlockSize, TMP_DATA_SIZE - 2 * deviceBlockSize,
GetPtableImageUfsLunEntryStart(ptbImgBuffer, i), GPT_ENTRYS_SIZE) != EOK) {
LOG(WARNING) << "memcpy_s gpt data fail";
}
AddUfsPtnDataInfo(newLunPtnDataInfo, deviceBlockSize, i);
if (!UfsReadGpt(newLunPtnDataInfo.data, newLunPtnDataInfo.writeDataLen,
newLunPtnDataInfo.lunIndex, deviceBlockSize)) {
LOG(ERROR) << "parse ufs gpt fail";
return false;
}
}
return true;
}
bool UfsPtable::ReadAndCheckMbr(const uint32_t lunIndex, const uint32_t blockSize)
{
if (blockSize <= 0 || lunIndex < 0 || lunIndex > deviceLunNum_) {
LOG(ERROR) << "blockSize <= 0";
return false;
}
uint8_t *buffer = new(std::nothrow) uint8_t[blockSize]();
if (buffer == nullptr) {
LOG(ERROR) << "new buffer failed!";
return false;
}
std::string ufsNode = GetDeviceLunNodePath(lunIndex);
if (!MemReadWithOffset(ufsNode, 0, buffer, blockSize)) {
LOG(ERROR) << "read " << blockSize << " bytes from ufsNode " << ufsNode << " failed!";
delete [] buffer;
return false;
}
bool result = CheckProtectiveMbr(buffer, blockSize);
delete [] buffer;
return result;
}
int32_t UfsPtable::GetLunNumFromNode(const std::string &ufsNode)
{
if (std::char_traits<char>::length(PREFIX_UFS_NODE) + 1 != ufsNode.length()) {
LOG(ERROR) << "ufsNode length is " << ufsNode.length() << ", \
not equal to PREFIX_UFS_NODE(" << std::char_traits<char>::length(PREFIX_UFS_NODE) << ") + 1";
return -1;
}
char ufsLunIndex = ufsNode.back();
return (ufsLunIndex - 'a');
}
bool UfsPtable::LoadPartitionInfoFromLun(const uint32_t lunIndex, const uint32_t imgLen)
{
if (imgLen == 0 || lunIndex < 0 || lunIndex > deviceLunNum_) {
LOG(ERROR) << "imgLen or lunIndex is invaild " << imgLen << " " << lunIndex;
return false;
}
std::string ufsNode = GetDeviceLunNodePath(lunIndex);
uint8_t *buffer = new(std::nothrow) uint8_t[imgLen]();
if (buffer == nullptr) {
LOG(ERROR) << "new buffer failed!";
return false;
}
if (!MemReadWithOffset(ufsNode, lunOffset_[lunIndex], buffer, imgLen)) {
LOG(ERROR) << "read " << imgLen << " bytes from ufsNode " << ufsNode << " failed!";
delete [] buffer;
return false;
}
UfsPartitionDataInfo newLunPtnDataInfo;
newLunPtnDataInfo.isGptVaild = true;
newLunPtnDataInfo.lunIndex = lunIndex;
newLunPtnDataInfo.lunSize = imgLen;
newLunPtnDataInfo.writeDataLen = imgLen;
(void)memset_s(newLunPtnDataInfo.data, TMP_DATA_SIZE, 0, TMP_DATA_SIZE);
if (memcpy_s(newLunPtnDataInfo.data, TMP_DATA_SIZE, buffer, imgLen) != EOK) {
LOG(WARNING) << "memcpy_s mbr fail";
}
ufsPtnDataInfo_.push_back(newLunPtnDataInfo);
int32_t result = UfsReadGpt(buffer, imgLen, lunIndex, GetDeviceBlockSize());
delete [] buffer;
return result;
}
uint32_t UfsPtable::LoadAllLunPartitions()
{
uint32_t lunIndex;
for (lunIndex = 0; lunIndex < deviceLunNum_; lunIndex++) {
if (ReadAndCheckMbr(lunIndex, GetDeviceBlockSize())) {
LoadPartitionInfoFromLun(lunIndex, ptableData_.writeDeviceLunSize);
}
}
return lunIndex;
}
bool UfsPtable::LoadPtableFromDevice()
{
if (!partitionInfo_.empty()) {
LOG(INFO) << "ptable is already loaded to ram";
return true;
}
SetDeviceLunNum();
if (LoadAllLunPartitions() == 0) {
LOG(ERROR) << "init ptable to ram fail";
return false;
}
LOG(INFO) << "init ptable to ram ok";
return true;
}
bool UfsPtable::WritePartitionTable()
{
if (ufsPtnDataInfo_.empty()) {
LOG(ERROR) << "ufsPtnDataInfo_ is empty, write failed!";
return false;
}
for (uint32_t i = 0; i < ufsPtnDataInfo_.size(); i++) {
uint32_t currLunIndex = ufsPtnDataInfo_[i].lunIndex;
if (currLunIndex >= MAX_LUN_NUMBERS) {
LOG(ERROR) << "invalid curr lun index " << currLunIndex;
return false;
}
if (!ufsPtnDataInfo_[i].needWrite && lunOffset_[currLunIndex] == 0) {
continue;
}
uint64_t writeDataLen = ufsPtnDataInfo_[i].writeDataLen;
std::string ufsNode = GetDeviceLunNodePath(currLunIndex);
LOG(INFO) << "ufs node name:" << ufsNode << ", writeDataLen = " << writeDataLen <<
", offset: " << lunOffset_[currLunIndex];
if (!ufsPtnDataInfo_[i].isGptVaild) {
LOG(WARNING) << "invaild ptable, no need to update";
continue;
}
if (!WriteBufferToPath(ufsNode, lunOffset_[currLunIndex], ufsPtnDataInfo_[i].data, writeDataLen)) {
LOG(ERROR) << "write first gpt fail";
return false;
}
#ifndef UPDATER_UT
if (hasBackupPtable_) {
LOG(INFO) << "should write back up ptable to device";
uint64_t lunSize = GetDeviceLunCapacity(ufsPtnDataInfo_[i].lunIndex);
WriteBackupPartitionTable(ufsPtnDataInfo_[i].lunIndex, lunSize);
}
#endif
}
return true;
}
bool UfsPtable::WriteBackupPartitionTable(uint32_t lunIdx, uint64_t lunSize)
{
if (lunIdx >= ufsPtnDataInfo_.size()) {
LOG(ERROR) << "lunIdx invalid , lunIdx = " << lunIdx << ", ufsPtnDataInfo size = " << ufsPtnDataInfo_.size();
return false;
}
std::string ufsNode = GetDeviceLunNodePath(lunIdx);
uint32_t deviceBlockSize = GetDeviceBlockSize();
if (lunSize == 0 || lunSize <= GPT_PTABLE_BACKUP_SIZE * deviceBlockSize) {
LOG(ERROR) << "lun size invalid, lun size = " << lunSize;
return false;
}
if (deviceBlockSize == 0) {
LOG(ERROR) << "deviceBlockSize is invalid";
return false;
}
uint64_t deviceBackGptEntryOffset = lunSize - GPT_PTABLE_BACKUP_SIZE * deviceBlockSize;
uint64_t deviceBackGptHeaderOffset = lunSize - deviceBlockSize;
std::unique_ptr<uint8_t[]> backUpHeader = std::make_unique<uint8_t[]>(deviceBlockSize);
if (memcpy_s(backUpHeader.get(), deviceBlockSize, ufsPtnDataInfo_[lunIdx].data +
deviceBlockSize, deviceBlockSize) != EOK) {
LOG(ERROR) << "memcpy error, deviceBlockSize:" << deviceBlockSize;
return false;
}
PatchBackUpGptHeader(backUpHeader.get(), deviceBlockSize, deviceBackGptEntryOffset / deviceBlockSize);
if (!WriteBufferToPath(ufsNode, deviceBackGptHeaderOffset, backUpHeader.get(), deviceBlockSize)) {
LOG(ERROR) << "write back up gpt header failed, deviceBackGptHeaderOffset = " << deviceBackGptHeaderOffset
<< ", deviceBlockSize = " << deviceBlockSize;
return false;
}
if (!WriteBufferToPath(ufsNode, deviceBackGptEntryOffset, ufsPtnDataInfo_[lunIdx].data +
deviceBlockSize * 2, (GPT_PTABLE_BACKUP_SIZE - 1) * deviceBlockSize)) {
LOG(ERROR) << "write back up gpt entries failed, deviceBackGptEntryOffset = " << deviceBackGptEntryOffset
<< ", deviceBlockSize = " << deviceBlockSize;
return false;
}
LOG(INFO) << "write backup partition table successful";
return true;
}
uint8_t *UfsPtable::GetPtableImageUfsLunPmbrStart(uint8_t *imageBuf, const uint32_t lunIndex)
{
uint32_t pmbrStart = ptableData_.emmcGptDataLen + GetPtableExtraOffset() + lunIndex * ptableData_.imgLuSize;
LOG(INFO) << "UfsLunPmbr : " << std::hex << pmbrStart << std::dec;
return imageBuf + pmbrStart;
}
uint8_t *UfsPtable::GetPtableImageUfsLunGptHeaderStart(uint8_t *imageBuf, const uint32_t lunIndex)
{
uint32_t gptHeaderStart = ptableData_.emmcGptDataLen + GetPtableExtraOffset() + lunIndex * ptableData_.imgLuSize +
ptableData_.lbaLen;
LOG(INFO) << "UfsLunGptHeader : " << std::hex << gptHeaderStart << std::dec;
return imageBuf + gptHeaderStart;
}
uint8_t *UfsPtable::GetPtableImageUfsLunEntryStart(uint8_t *imageBuf, const uint32_t lunIndex)
{
uint32_t entryStart = ptableData_.emmcGptDataLen + GetPtableExtraOffset() + lunIndex * ptableData_.imgLuSize +
ptableData_.lbaLen + ptableData_.gptHeaderLen;
LOG(INFO) << "UfsLunEntry : " << std::hex << entryStart << std::dec;
return imageBuf + entryStart;
}
bool UfsPtable::CorrectBufByPtnList(uint8_t *imageBuf, uint64_t imgBufSize, const std::vector<PtnInfo> &srcInfo,
const std::vector<PtnInfo> &dstInfo)
{
int srcSize = static_cast<int>(srcInfo.size());
int dstSize = static_cast<int>(dstInfo.size());
if (imageBuf == nullptr || imgBufSize == 0 || srcSize != dstSize) {
LOG(ERROR) << "invalid input. imgBufSize : " << imgBufSize << " srcInfo.size: " << srcSize
<< " dstInfo.size:" << dstSize;
return false;
}
if (usrDataPtnIndex_ < 0 || endPtnIndex_ < 0 || usrDataPtnIndex_ >= dstSize ||
endPtnIndex_ >= dstSize) {
LOG(ERROR) << "invaild dst ptn info list";
return false;
}
uint8_t* ufsLunEntryStart = GetPtableImageUfsLunEntryStart(imageBuf, dstInfo[usrDataPtnIndex_].lun);
const uint32_t editLen = PARTITION_ENTRY_SIZE * MAX_PARTITION_NUM;
std::vector<uint8_t> newBuf(ufsLunEntryStart, ufsLunEntryStart + editLen);
for (int i = startPtnIndex_; i <= endPtnIndex_; i++) {
if (srcInfo[i].startAddr == dstInfo[i].startAddr && srcInfo[i].partitionSize == dstInfo[i].partitionSize
&& srcInfo[i].dispName == dstInfo[i].dispName) {
continue;
}
LOG(INFO) << srcInfo[i].dispName << "should adjust";
std::vector<uint8_t> newEntryBuf(ufsLunEntryStart + dstInfo[i].gptEntryBufOffset,
ufsLunEntryStart + dstInfo[i].gptEntryBufOffset + PARTITION_ENTRY_SIZE);
PUT_LONG_LONG(newEntryBuf.data() + FIRST_LBA_OFFSET, dstInfo[i].startAddr / GetDeviceBlockSize());
PUT_LONG_LONG(newEntryBuf.data() + LAST_LBA_OFFSET,
(dstInfo[i].startAddr + dstInfo[i].partitionSize) / GetDeviceBlockSize() - 1);
if (srcInfo[i].gptEntryBufOffset > static_cast<int>(editLen - PARTITION_ENTRY_SIZE)) {
LOG(ERROR) << "srcInfo[" << i << "] error. gptEntryBufOffset = " << srcInfo[i].gptEntryBufOffset;
return false;
}
std::copy(newEntryBuf.begin(), newEntryBuf.end(), newBuf.begin() + srcInfo[i].gptEntryBufOffset);
}
uint64_t offset = static_cast<uint64_t>(ufsLunEntryStart - imageBuf);
if (imageBuf > ufsLunEntryStart || offset >= imgBufSize) {
LOG(ERROR) << "memcpy size fail imageBuf" << imageBuf << "ufsLunEntryStart" << ufsLunEntryStart
<< "imgBufSize" << imgBufSize;
return false;
}
if (memcpy_s(ufsLunEntryStart, imgBufSize - offset, newBuf.data(), editLen) != 0) {
LOG(ERROR) << "memcpy fail. destSize :" << imgBufSize - (ufsLunEntryStart - imageBuf);
return false;
}
return true;
}
bool UfsPtable::EditPartitionBuf(uint8_t *imageBuf, uint64_t imgBufSize, std::vector<PtnInfo> &modifyList)
{
if (imageBuf == nullptr || imgBufSize == 0 || modifyList.empty() || ptableData_.blockSize == 0) {
LOG(ERROR) << "input invalid";
return false;
}
if (imgBufSize < ptableData_.emmcGptDataLen || deviceLunNum_ == 0) {
LOG(ERROR) << "can not get offset, imgBufsize =" << imgBufSize << ",emmcGptDataLen ="
<< ptableData_.emmcGptDataLen << ", deviceLunNum = " << deviceLunNum_;
return false;
}
uint32_t gptSize = ptableData_.imgLuSize;
uint32_t imgBlockSize = ptableData_.lbaLen;
uint32_t deviceBlockSize = GetDeviceBlockSize();
uint32_t startLu = ptableData_.startLunNumber;
for (uint32_t i = 0; i < deviceLunNum_; ++i) {
UfsPartitionDataInfo newLunPtnDataInfo;
(void)memset_s(newLunPtnDataInfo.data, TMP_DATA_SIZE, 0, TMP_DATA_SIZE);
std::string ufsNode = GetDeviceLunNodePath(i + startLu);
newLunPtnDataInfo.lunSize = GetDeviceLunCapacity(i + startLu);
if (newLunPtnDataInfo.lunSize == 0) {
LOG(ERROR) << "get devDenisity failed in " << ufsNode;
return false;
}
uint8_t *curGptBuf = GetPtableImageUfsLunPmbrStart(imageBuf, i + startLu);
if (!ufsPtnDataInfo_[i].isGptVaild) {
continue;
}
struct GptParseInfo gptInfo(imgBlockSize, deviceBlockSize, newLunPtnDataInfo.lunSize -
(hasBackupPtable_ ? (GPT_PTABLE_BACKUP_SIZE * deviceBlockSize) : 0));
for (auto &t : modifyList) {
if (static_cast<uint32_t>(t.lun) == i + startLu && !ChangeGpt(curGptBuf, gptSize, gptInfo, t)) {
LOG(ERROR) << "ChangeGpt failed";
return false;
}
}
if (memcpy_s(newLunPtnDataInfo.data, TMP_DATA_SIZE, curGptBuf, imgBlockSize) != EOK) {
LOG(WARNING) << "memcpy_s fail";
}
newLunPtnDataInfo.writeDataLen = ptableData_.writeDeviceLunSize;
UfsPatchGptHeader(newLunPtnDataInfo, imgBlockSize);
}
return true;
}
bool UfsPtable::GetPtableImageBuffer(uint8_t *imageBuf, const uint32_t imgBufSize)
{
uint32_t imgBlockSize = ptableData_.lbaLen;
uint32_t deviceBlockSize = GetDeviceBlockSize();
SetDeviceLunNum();
if (imageBuf == nullptr || imgBufSize == 0 ||
imgBufSize < ptableData_.emmcGptDataLen + GetPtableExtraOffset() + ptableData_.imgLuSize * deviceLunNum_) {
LOG(ERROR) << "input param invalid";
return false;
}
for (uint32_t i = 0; i < deviceLunNum_; ++i) {
uint32_t curImgOffset = 0;
uint32_t curDevOffset = 0;
uint32_t imgOffset = ptableData_.emmcGptDataLen + GetPtableExtraOffset() + ptableData_.imgLuSize * i;
std::string ufsNode = GetDeviceLunNodePath(i + ptableData_.startLunNumber);
if (!CheckFileExist(ufsNode)) {
LOG(ERROR) << "file " << ufsNode << " is not exist";
return false;
}
if (!MemReadWithOffset(ufsNode, curDevOffset, imageBuf + curImgOffset + imgOffset, imgBlockSize)) {
LOG(ERROR) << "MemReadWithOffset " << ufsNode << " error";
return false;
}
bool isGptExist = CheckProtectiveMbr(imageBuf + curImgOffset + imgOffset, imgBlockSize);
curImgOffset += imgBlockSize;
curDevOffset += deviceBlockSize;
if (!isGptExist) {
continue;
}
if (!MemReadWithOffset(ufsNode, curDevOffset, imageBuf + curImgOffset + imgOffset, imgBlockSize)) {
LOG(ERROR) << "MemReadWithOffset " << ufsNode << " error";
return false;
}
uint32_t maxPartCount = GET_LWORD_FROM_BYTE(&imageBuf[imgOffset + curImgOffset + PARTITION_COUNT_OFFSET]);
uint32_t entrySize = GET_LWORD_FROM_BYTE(&imageBuf[imgOffset + curImgOffset + PENTRY_SIZE_OFFSET]);
curImgOffset += imgBlockSize;
curDevOffset += deviceBlockSize;
uint32_t gptInfoLen = maxPartCount * entrySize;
if (!MemReadWithOffset(ufsNode, curDevOffset, imageBuf + curImgOffset + imgOffset, gptInfoLen)) {
LOG(ERROR) << "MemReadWithOffset " << ufsNode << " error" << gptInfoLen;
return false;
}
}
return true;
}
void UfsPtable::GetTgtPartitionName(std::string &name, const int sourceSlot)
{
if (name.size() < PARTITION_AB_SUFFIX_SIZE) {
name += (sourceSlot == SLOT_A ? PARTITION_B_SUFFIX : PARTITION_A_SUFFIX);
return;
}
std::string suffix = name.substr(name.size() - PARTITION_AB_SUFFIX_SIZE, PARTITION_AB_SUFFIX_SIZE);
std::string partitionName = name.substr(0, name.size() - PARTITION_AB_SUFFIX_SIZE);
if (strcasecmp(suffix.c_str(), PARTITION_A_SUFFIX) == 0) {
name = partitionName + PARTITION_B_SUFFIX;
} else if (strcasecmp(suffix.c_str(), PARTITION_B_SUFFIX) == 0) {
name = partitionName + PARTITION_A_SUFFIX;
} else {
name += (sourceSlot == SLOT_A ? PARTITION_B_SUFFIX : PARTITION_A_SUFFIX);
}
return;
}
bool UfsPtable::EditABPartition(uint8_t *gptImage, const uint32_t blockSize, const int sourceSlot)
{
if (gptImage == nullptr) {
LOG(ERROR) << "invalid gptImage";
return false;
}
uint32_t partEntryCnt = blockSize / PARTITION_ENTRY_SIZE;
if (partEntryCnt == 0) {
LOG(ERROR) << "invalid partEntryCnt";
return false;
}
uint32_t count = 0;
uint8_t *data = nullptr;
LOG(INFO) << "Write Partition begin";
for (uint32_t i = 0; i < (MAX_PARTITION_NUM / partEntryCnt) && count < MAX_PARTITION_NUM; i++) {
data = gptImage + (DEFAULT_ENTRY_OFFSET + i) * blockSize;
for (uint32_t j = 0; j < partEntryCnt; j++) {
uint8_t typeGuid[GPT_PARTITION_TYPE_GUID_LEN] = {0};
if (memcpy_s(typeGuid, sizeof(typeGuid), &data[(j * PARTITION_ENTRY_SIZE)], sizeof(typeGuid)) != EOK) {
LOG(ERROR) << "memcpy guid fail";
}
if (typeGuid[0] == 0x00 && typeGuid[1] == 0x00) {
i = MAX_PARTITION_NUM / partEntryCnt;
break;
}
uint8_t *nameOffset = data + (j * PARTITION_ENTRY_SIZE + GPT_PARTITION_NAME_OFFSET);
std::string sourceName;
ParsePartitionName(nameOffset, MAX_GPT_NAME_SIZE, sourceName, MAX_GPT_NAME_SIZE / 2);
std::string targetName(sourceName);
GetTgtPartitionName(targetName, sourceSlot);
if (!WritePartitionName(targetName, targetName.length(), nameOffset, MAX_GPT_NAME_SIZE)) {
LOG(ERROR) << "Write Partition failed, source: " << sourceName << ", target: " << targetName;
return false;
}
LOG_LITE(INFO) << "Partition source: " << sourceName << ", target: " << targetName;
count++;
}
}
LOG(INFO) << "Write Partition success. Start to Calculate Crc32";
uint32_t partCount = GET_LWORD_FROM_BYTE(gptImage + blockSize + PARTITION_COUNT_OFFSET);
uint32_t entrySize = GET_LWORD_FROM_BYTE(gptImage + blockSize + PENTRY_SIZE_OFFSET);
uint32_t crcValue = CalculateCrc32(gptImage + (blockSize * 2), partCount * entrySize);
PUT_LONG(gptImage + blockSize + PARTITION_CRC_OFFSET, crcValue);
PUT_LONG(gptImage + blockSize + HEADER_CRC_OFFSET, 0);
crcValue = CalculateCrc32(gptImage + blockSize, GPT_CRC_LEN);
PUT_LONG(gptImage + blockSize + HEADER_CRC_OFFSET, crcValue);
return true;
}
bool UfsPtable::ModifyBufferPartitionName(uint8_t *buffer, const uint32_t bufferSize, const int sourceSlot)
{
LOG(INFO) << "start to modify partition name in buffer";
if (buffer == nullptr || bufferSize == 0) {
LOG(ERROR) << "invaild input";
return false;
}
uint32_t blockSize = GetDeviceBlockSize();
if (blockSize == 0) {
LOG(ERROR) << "invaild blockSize: " << blockSize;
return false;
}
if (!EditABPartition(buffer, blockSize, sourceSlot)) {
LOG(ERROR) << "Edit AB PartitionName failed";
return false;
}
LOG(INFO) << "modify partition name in buffer finished";
return true;
}
bool UfsPtable::SyncABLunPtableDevice(const int sourceSlot)
{
LOG(INFO) << "start to sync ABLun Ptable in Device";
if (sourceSlot != SLOT_A && sourceSlot != SLOT_B) {
LOG(ERROR) << "Invalid sourceSlot: " << sourceSlot;
return false;
}
std::string srcNodePath = GetDeviceLunNodePath(sourceSlot == SLOT_A ? LUN_FOR_SLOT_A : LUN_FOR_SLOT_B);
uint32_t len = ptableData_.writeDeviceLunSize;
constexpr uint32_t maxBufSize = 1 * 1024 * 1024;
if (len == 0 || len > maxBufSize) {
LOG(ERROR) << "Invalid len: " << len;
return false;
}
std::unique_ptr<uint8_t[]> buffer = std::make_unique<uint8_t[]>(len);
if (buffer == nullptr) {
LOG(ERROR) << "new buffer failed!";
return false;
}
if (!MemReadWithOffset(srcNodePath, 0, buffer.get(), len)) {
LOG(ERROR) << "read ptable from " << srcNodePath << " failed!";
return false;
}
if (!ModifyBufferPartitionName(buffer.get(), len, sourceSlot)) {
LOG(ERROR) << "Modify PartitionName failed!";
return false;
}
std::string tgtNodePath = GetDeviceLunNodePath(sourceSlot == SLOT_A ? LUN_FOR_SLOT_B : LUN_FOR_SLOT_A);
if (!WriteBufferToPath(tgtNodePath, 0, buffer.get(), len)) {
LOG(ERROR) << "write first gpt fail";
return false;
}
LOG(INFO) << "Sync ABLun Ptable in Device success";
return true;
}
bool UfsPtable::GetABLunPartitionInfo(const int sourceSlot, std::string &srcNode,
std::string &tgtNode, uint32_t &offset)
{
if (sourceSlot != SLOT_A && sourceSlot != SLOT_B) {
LOG(ERROR) << "Invalid sourceSlot: " << sourceSlot;
return false;
}
srcNode = GetDeviceLunNodePath(sourceSlot == SLOT_A ? LUN_FOR_SLOT_A : LUN_FOR_SLOT_B);
tgtNode = GetDeviceLunNodePath(sourceSlot == SLOT_A ? LUN_FOR_SLOT_B : LUN_FOR_SLOT_A);
offset = ptableData_.writeDeviceLunSize;
if (offset == 0) {
LOG(ERROR) << "invalid offset";
return false;
}
if (!CheckFileExist(srcNode) || !CheckFileExist(tgtNode)) {
LOG(ERROR) << "Node is not exist. srcNode:" << srcNode << " tgtNode:" << (tgtNode);
return false;
}
return true;
}
bool UfsPtable::WritePtableLunOffset(uint32_t lunIndex, uint64_t offset)
{
if (lunIndex >= MAX_LUN_NUMBERS) {
LOG(ERROR) << "invalid lun index " << lunIndex;
return false;
}
lunOffset_[lunIndex] = offset;
LOG(INFO) << "write lun offset success, index " << lunIndex << " offset " << offset;
return true;
}
uint32_t UfsPtable::GetGptEntryCrc(const HeaderCheckInputs &inputs, uint64_t offset, uint32_t crcLen)
{
if (offset == DEFAULT_ENTRY_OFFSET * GetDeviceBlockSize()) {
return Ptable::GetGptEntryCrc(inputs, offset, crcLen);
}
if (inputs.lunNum >= MAX_LUN_NUMBERS) {
LOG(ERROR) << "invalid lun index " << inputs.lunNum;
return 0;
}
std::string ufsNode = GetDeviceLunNodePath(inputs.lunNum);
auto data = std::make_unique<uint8_t[]>(crcLen);
if (data == nullptr) {
LOG(ERROR) << "new buffer failed";
return 0;
}
if (!MemReadWithOffset(ufsNode, offset, data.get(), crcLen)) {
LOG(ERROR) << "read ufs node failed " << ufsNode;
return 0;
}
LOG(INFO) << "get entry crc with offset " << offset;
return CalculateCrc32(data.get(), crcLen);
}
}