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}