1/* 2 * Copyright (c) 2022 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 "emmc_ptable.h" 17 18#include <algorithm> 19#include <fcntl.h> 20#include <sys/types.h> 21#include <unistd.h> 22 23#include "log/log.h" 24#include "securec.h" 25#include "updater/updater_const.h" 26 27namespace Updater { 28 29uint64_t EmmcPtable::GetDeviceCapacity() 30{ 31 uint64_t capacity = 0; 32 std::string capacityPath = std::string(MMC_SIZE_FILE); 33 GetCapacity(capacityPath, capacity); 34 return capacity; 35} 36 37void EmmcPtable::EmmcPatchGptHeader(EmmcPartitionDataInfo &ptnDataInfo, const uint32_t blockSize) 38{ 39 // mbr len + gptHeader len = 2 blockSize 40 if (blockSize == 0 || ptnDataInfo.writeDataLen < 2 * blockSize) { 41 LOG(ERROR) << "invaild argument"; 42 return; 43 } 44 45 uint64_t devDensity = GetDeviceCapacity(); 46 47 uint64_t devBlockSize = EMMC_BLOCK_SIZE; 48 uint64_t cardSizeSector = devDensity / devBlockSize; 49 50 // Patching primary header 51 uint8_t *primaryGptHeader = ptnDataInfo.data + blockSize; 52 PUT_LONG_LONG(primaryGptHeader + BACKUP_HEADER_OFFSET, (cardSizeSector - 1)); 53 PUT_LONG_LONG(primaryGptHeader + LAST_USABLE_LBA_OFFSET, (cardSizeSector - 1)); 54 // Find last partition 55 uint32_t totalPart = 0; 56 while (((GPT_PARTITION_SIZE - blockSize - blockSize) > totalPart * PARTITION_ENTRY_SIZE) && 57 (*(primaryGptHeader + blockSize + totalPart * PARTITION_ENTRY_SIZE) != 0)) { 58 totalPart++; 59 } 60 if (totalPart == 0) { 61 LOG(ERROR) << "no partition exist"; 62 return; 63 } 64 // Patching last partition 65 uint8_t *lastPartOffset = primaryGptHeader + blockSize + (totalPart - 1) * PARTITION_ENTRY_SIZE; 66 uint64_t lastLba = GET_LLWORD_FROM_BYTE(lastPartOffset + PARTITION_ENTRY_LAST_LBA); 67 uint64_t firstLba = GET_LLWORD_FROM_BYTE(lastPartOffset + FIRST_LBA_OFFSET); 68 // General algorithm : calculate partition size by lba 69 uint64_t partitionSize = (lastLba - firstLba + 1) * MIN_EMMC_WRITE_SIZE; 70 std::string partitionName; 71 uint8_t *nameOffset = lastPartOffset + GPT_PARTITION_NAME_OFFSET; 72 // 2 bytes for 1 charactor of partition name 73 ParsePartitionName(nameOffset, MAX_GPT_NAME_SIZE, partitionName, MAX_GPT_NAME_SIZE / 2); 74 if (partitionName == USERDATA_PARTITION || (totalPart == 1 && partitionSize == 0)) { 75 // patch userdata or only one partition 76 PUT_LONG_LONG(lastPartOffset + PARTITION_ENTRY_LAST_LBA, (cardSizeSector - 1)); 77 LOG(INFO) << "partitionSize=" << (cardSizeSector - 1) << ", partition_name:" << partitionName; 78 } 79 80 // Updating CRC of the Partition entry array in both headers 81 uint32_t partCount = GET_LWORD_FROM_BYTE(primaryGptHeader + PARTITION_COUNT_OFFSET); 82 uint32_t entrySize = GET_LWORD_FROM_BYTE(primaryGptHeader + PENTRY_SIZE_OFFSET); 83 // mbr len + gptHeader len = 2 blockSize 84 uint32_t crcValue = CalculateCrc32(ptnDataInfo.data + (blockSize * 2), partCount * entrySize); 85 PUT_LONG(primaryGptHeader + PARTITION_CRC_OFFSET, crcValue); 86 // Clearing CRC fields to calculate 87 PUT_LONG(primaryGptHeader + HEADER_CRC_OFFSET, 0); 88 crcValue = CalculateCrc32(primaryGptHeader, GPT_CRC_LEN); 89 PUT_LONG(primaryGptHeader + HEADER_CRC_OFFSET, crcValue); 90 return; 91} 92 93bool EmmcPtable::WritePartitionTable() 94{ 95 if (partitionInfo_.empty()) { 96 LOG(ERROR) << "emmcPtnDataInfo_ is empty, write failed!"; 97 return false; 98 } 99 100 if (!emmcPtnDataInfo_.isGptVaild) { 101 LOG(WARNING) << "invaild ptable, no need to update"; 102 return false; 103 } 104 105 std::string emmcNode = std::string(MMC_BLOCK_DEV_NAME); 106 if (!WriteBufferToPath(emmcNode, 0, emmcPtnDataInfo_.data, GPT_PARTITION_SIZE)) { 107 LOG(ERROR) << "write gpt fail"; 108 return false; 109 } 110 return true; 111} 112 113// blocksize is 4096, lbaLen is 512. Because in ptable.img block is 512 while in device block is 4096 114bool EmmcPtable::ParsePartitionFromBuffer(uint8_t *ptbImgBuffer, const uint32_t imgBufSize) 115{ 116 if (ptbImgBuffer == nullptr || imgBufSize < GPT_PARTITION_SIZE) { 117 LOG(ERROR) << "ptbImgBuffer == NULL || imgBufSize < GPT_PARTITION_SIZE"; 118 return false; 119 } 120 return UpdateCommInitializeGptPartition(ptbImgBuffer, imgBufSize); 121} 122 123bool EmmcPtable::ParseGptHeaderByEmmc(uint8_t *gptImage, const uint32_t len) 124{ 125 GPTHeaderInfo gptHeaderInfo; 126 uint32_t blockSize = EMMC_BLOCK_SIZE; 127 (void)memset_s(&gptHeaderInfo, sizeof(GPTHeaderInfo), 0, sizeof(GPTHeaderInfo)); 128 if (!GetPartitionGptHeaderInfo(gptImage + blockSize, blockSize, gptHeaderInfo)) { 129 LOG(ERROR) << "GetPartitionGptHeaderInfo fail"; 130 return false; 131 } 132#ifndef UPDATER_UT 133 uint64_t deviceSize = GetDeviceCapacity(); 134 uint32_t lbaNum = deviceSize / MIN_EMMC_WRITE_SIZE; 135#else 136 uint32_t lbaNum = 0xFFFFFFFF; // init to maximum 137#endif 138 return PartitionCheckGptHeader(gptImage, len, lbaNum, blockSize, gptHeaderInfo); 139} 140 141bool EmmcPtable::EmmcReadGpt(uint8_t *ptableData, uint32_t len) 142{ 143 uint32_t number = 0; 144 145 partitionInfo_.clear(); 146 if (!ParseGptHeaderByEmmc(emmcPtnDataInfo_.data, len)) { 147 LOG(ERROR) << "Primary signature invalid"; 148 return false; 149 } 150 for (uint32_t i = 0; i < MAX_PARTITION_NUM; i++) { 151 uint8_t *startLbaOffset = ptableData + GPT_PARTITION_START_LBA_OFFSET; 152 uint8_t *endLbaOffset = ptableData + GPT_PARTITION_START_LBA_OFFSET + GPT_PARTITION_END_LBA_OFFSET; 153 uint8_t *typeGuidOffset = ptableData + GPT_PARTITION_TYPE_GUID_OFFSET; 154 uint8_t *nameOffset = ptableData + GPT_PARTITION_NAME_OFFSET; 155 PtnInfo newPtnInfo; 156 (void)memset_s(&newPtnInfo, sizeof(newPtnInfo), 0, sizeof(newPtnInfo)); 157 ParsePartitionName(nameOffset, MAX_GPT_NAME_SIZE, newPtnInfo.dispName, GPT_DISP_NAME_LEN); 158 if (newPtnInfo.dispName[0] == 0) { 159 break; 160 } 161 uint64_t startLba = GET_LLWORD_FROM_BYTE(startLbaOffset); 162 uint64_t endLba = GET_LLWORD_FROM_BYTE(endLbaOffset); 163 if (memcpy_s(newPtnInfo.partitionTypeGuid, GPT_PARTITION_TYPE_GUID_LEN, 164 typeGuidOffset, GPT_PARTITION_TYPE_GUID_LEN) != EOK) { 165 LOG(ERROR) << "memcpy guid fail"; 166 } 167 newPtnInfo.startAddr = startLba * LBA_LENGTH; 168 newPtnInfo.writePath = MMC_BLOCK_DEV_NAME; 169 newPtnInfo.writeMode = "WRITE_RAW"; 170 /* General algorithm : calculate partition size by lba */ 171 newPtnInfo.partitionSize = (endLba - startLba + 1) * LBA_LENGTH; 172 ptableData += GPT_PARTITION_INFO_LENGTH; 173 partitionInfo_.push_back(newPtnInfo); 174 number++; 175 } 176 177 return number != 0; 178} 179 180bool EmmcPtable::UpdateCommInitializeGptPartition(uint8_t *gptImage, const uint32_t len) 181{ 182 if (gptImage == nullptr || len < GPT_PARTITION_SIZE) { 183 LOG(ERROR) << "input param invalid"; 184 return false; 185 } 186 uint32_t imgBlockSize = ptableData_.lbaLen; // 512 187 uint32_t deviceBlockSize = EMMC_BLOCK_SIZE; 188 189 uint8_t *gptHeaderStart = gptImage + imgBlockSize; 190 uint8_t *ptableData = gptImage + PROTECTIVE_MBR_SIZE + LBA_LENGTH; /* skip MBR and gpt header */ 191 192 if (!CheckProtectiveMbr(gptImage, imgBlockSize) || !CheckIfValidGpt(gptHeaderStart, imgBlockSize)) { 193 LOG(ERROR) << "check mbr or header fail"; 194 emmcPtnDataInfo_.isGptVaild = false; 195 return false; 196 } 197 198 // for hisi: change ptable.img(512 bytes/block) into format of device(4096 bytes/block) 199 if (memcpy_s(emmcPtnDataInfo_.data, GPT_PARTITION_SIZE, gptImage, imgBlockSize) != EOK) { 200 LOG(ERROR) << "memcpy_s mbr fail"; 201 return false; 202 } 203 if (memcpy_s(emmcPtnDataInfo_.data + deviceBlockSize, GPT_PARTITION_SIZE - deviceBlockSize, 204 gptHeaderStart, imgBlockSize) != EOK) { 205 LOG(ERROR) << "memcpy_s gpt header fail"; 206 return false; 207 } 208 // skip 2 lba length to set gpt entry 209 if (memcpy_s(emmcPtnDataInfo_.data + 2 * deviceBlockSize, GPT_PARTITION_SIZE - 2 * deviceBlockSize, 210 ptableData, GPT_PARTITION_SIZE - 2 * deviceBlockSize) != EOK) { 211 LOG(ERROR) << "memcpy_s gpt data fail"; 212 return false; 213 } 214 emmcPtnDataInfo_.writeDataLen = len; 215 EmmcPatchGptHeader(emmcPtnDataInfo_, deviceBlockSize); 216 emmcPtnDataInfo_.isGptVaild = true; 217 218 return EmmcReadGpt(emmcPtnDataInfo_.data + 2 * deviceBlockSize, len); // 2: skip 2 lba length to set gpt entry 219} 220 221bool EmmcPtable::ReadEmmcGptImageToRam() 222{ 223#ifndef UPDATER_UT 224 std::string emmcNode = std::string(MMC_BLOCK_DEV_NAME); 225#else 226 std::string emmcNode = "/data/updater_ext/ptable_parse/ptable.img"; 227#endif 228 int32_t imgLen = GPT_PARTITION_SIZE; 229 auto buf = std::make_unique<uint8_t[]>(imgLen); 230 uint8_t *buffer = buf.get(); 231 uint32_t deviceBlockSize = EMMC_BLOCK_SIZE; 232 if (buffer == nullptr) { 233 LOG(ERROR) << "new buffer failed!"; 234 return false; 235 } 236 if (!MemReadWithOffset(emmcNode, 0, buffer, imgLen)) { 237 LOG(ERROR) << "read " << imgLen << " bytes from emmcNode " << emmcNode << " failed!"; 238 return false; 239 } 240 241#ifdef UPDATER_UT 242 deviceBlockSize = ptableData_.lbaLen; 243#endif 244 uint8_t *gptHeaderStart = buffer + deviceBlockSize; // if image imgBlockSize, if device deviceBlockSize 245 if (!CheckProtectiveMbr(buffer, deviceBlockSize) || !CheckIfValidGpt(gptHeaderStart, deviceBlockSize)) { 246 LOG(ERROR) << "check mbr or header fail"; 247 return false; 248 } 249 250 (void)memset_s(emmcPtnDataInfo_.data, GPT_PARTITION_SIZE, 0, GPT_PARTITION_SIZE); 251 if (memcpy_s(emmcPtnDataInfo_.data, GPT_PARTITION_SIZE, buffer, imgLen) != EOK) { 252 LOG(ERROR) << "memcpy_s GPT fail"; 253 return false; 254 } 255 256 emmcPtnDataInfo_.isGptVaild = true; 257 uint8_t *ptableData = buffer + 2 * deviceBlockSize; /* skip MBR and gpt header */ 258 return EmmcReadGpt(ptableData, imgLen); 259} 260 261bool EmmcPtable::LoadPtableFromDevice() 262{ 263 if (!partitionInfo_.empty()) { 264 LOG(INFO) << "ptable is already loaded to ram"; 265 return true; 266 } 267 if (ReadEmmcGptImageToRam()) { 268 LOG(INFO) << "init ptable to ram ok"; 269 return true; 270 } 271 return false; 272} 273 274bool EmmcPtable::GetPtableImageBuffer(uint8_t *imageBuf, const uint32_t imgBufSize) 275{ 276 if (imageBuf == nullptr || imgBufSize == 0) { 277 LOG(ERROR) << "input invalid"; 278 return false; 279 } 280 281 if (!LoadPtableFromDevice()) { 282 LOG(ERROR) << "LoadPtableFromDevice failed"; 283 return false; 284 } 285 286 if (memcpy_s(imageBuf, imgBufSize, emmcPtnDataInfo_.data, GPT_PARTITION_SIZE) != EOK) { 287 LOG(ERROR) << "memcpy_s failed"; 288 return false; 289 } 290 return true; 291} 292 293bool EmmcPtable::EditPartitionBuf(uint8_t *imageBuf, uint64_t imgBufSize, std::vector<PtnInfo> &modifyList) 294{ 295 if (imageBuf == nullptr || imgBufSize == 0 || modifyList.empty()) { 296 LOG(ERROR) << "input invalid"; 297 return false; 298 } 299 uint8_t *gptImage = imageBuf; 300 uint32_t imgBlockSize = EMMC_BLOCK_SIZE; 301 uint8_t *gptHeader = gptImage + imgBlockSize; 302 uint32_t maxPtnCnt = GET_LWORD_FROM_BYTE(&gptHeader[PARTITION_COUNT_OFFSET]); 303 uint32_t ptnEntrySize = GET_LWORD_FROM_BYTE(&gptHeader[PENTRY_SIZE_OFFSET]); 304 uint64_t gptHeaderLen = EMMC_BLOCK_SIZE; 305 uint64_t gptSize = static_cast<uint64_t>(maxPtnCnt) * ptnEntrySize + imgBlockSize + gptHeaderLen; 306 uint64_t devDensity = GetDeviceCapacity(); 307 if (devDensity == 0) { 308 LOG(ERROR) << "get emmc capacity fail"; 309 return false; 310 } 311 uint32_t devBlockSize = EMMC_BLOCK_SIZE; 312 struct GptParseInfo gptInfo(imgBlockSize, devBlockSize, devDensity); 313 for (auto &t : modifyList) { 314 if (!ChangeGpt(gptImage, gptSize, gptInfo, t)) { 315 LOG(ERROR) << "ChangeGpt failed"; 316 return false; 317 } 318 } 319 EmmcPartitionDataInfo newEmmcPartitionDataInfo; 320 newEmmcPartitionDataInfo.writeDataLen = ptableData_.emmcGptDataLen; 321 (void)memset_s(newEmmcPartitionDataInfo.data, GPT_PARTITION_SIZE, 0, GPT_PARTITION_SIZE); 322 if (memcpy_s(newEmmcPartitionDataInfo.data, GPT_PARTITION_SIZE, imageBuf, imgBlockSize) != EOK) { 323 LOG(ERROR) << "memcpy_s failed"; 324 return false; 325 } 326 EmmcPatchGptHeader(newEmmcPartitionDataInfo, imgBlockSize); 327 return true; 328} 329}