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
27 namespace Updater {
28
GetDeviceCapacity()29 uint64_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
EmmcPatchGptHeader(EmmcPartitionDataInfo & ptnDataInfo,const uint32_t blockSize)37 void 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
WritePartitionTable()93 bool 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
ParsePartitionFromBuffer(uint8_t * ptbImgBuffer,const uint32_t imgBufSize)114 bool 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
ParseGptHeaderByEmmc(uint8_t * gptImage,const uint32_t len)123 bool EmmcPtable::ParseGptHeaderByEmmc(uint8_t *gptImage, const uint32_t len)
124 {
125 GPTHeaderInfo gptHeaderInfo;
126 uint32_t blockSize = ptableData_.blockSize; // 4096
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
EmmcReadGpt(uint8_t * ptableData)141 bool EmmcPtable::EmmcReadGpt(uint8_t *ptableData)
142 {
143 uint32_t number = 0;
144 for (uint32_t i = 0; i < MAX_PARTITION_NUM; i++) {
145 uint8_t *startLbaOffset = ptableData + GPT_PARTITION_START_LBA_OFFSET;
146 uint8_t *endLbaOffset = ptableData + GPT_PARTITION_START_LBA_OFFSET + GPT_PARTITION_END_LBA_OFFSET;
147 uint8_t *typeGuidOffset = ptableData + GPT_PARTITION_TYPE_GUID_OFFSET;
148 uint8_t *nameOffset = ptableData + GPT_PARTITION_NAME_OFFSET;
149 PtnInfo newPtnInfo;
150 (void)memset_s(&newPtnInfo, sizeof(newPtnInfo), 0, sizeof(newPtnInfo));
151 ParsePartitionName(nameOffset, MAX_GPT_NAME_SIZE, newPtnInfo.dispName, GPT_DISP_NAME_LEN);
152 if (newPtnInfo.dispName[0] == 0) {
153 break;
154 }
155 uint64_t startLba = GET_LLWORD_FROM_BYTE(startLbaOffset);
156 uint64_t endLba = GET_LLWORD_FROM_BYTE(endLbaOffset);
157 if (memcpy_s(newPtnInfo.partitionTypeGuid, GPT_PARTITION_TYPE_GUID_LEN,
158 typeGuidOffset, GPT_PARTITION_TYPE_GUID_LEN) != EOK) {
159 LOG(ERROR) << "memcpy guid fail";
160 }
161 newPtnInfo.startAddr = startLba * LBA_LENGTH;
162 /* General algorithm : calculate partition size by lba */
163 newPtnInfo.partitionSize = (endLba - startLba + 1) * LBA_LENGTH;
164 ptableData += GPT_PARTITION_INFO_LENGTH;
165 partitionInfo_.push_back(newPtnInfo);
166 number++;
167 }
168
169 return number != 0;
170 }
171
UpdateCommInitializeGptPartition(uint8_t * gptImage,const uint32_t len)172 bool EmmcPtable::UpdateCommInitializeGptPartition(uint8_t *gptImage, const uint32_t len)
173 {
174 if (gptImage == nullptr || len < GPT_PARTITION_SIZE) {
175 LOG(ERROR) << "input param invalid";
176 return false;
177 }
178 uint32_t imgBlockSize = ptableData_.lbaLen; // 512
179 uint32_t deviceBlockSize = ptableData_.blockSize; // 4096
180
181 uint8_t *gptHeaderStart = gptImage + imgBlockSize;
182 uint8_t *ptableData = gptImage + PROTECTIVE_MBR_SIZE + LBA_LENGTH; /* skip MBR and gpt header */
183
184 if (!CheckProtectiveMbr(gptImage, imgBlockSize) || !CheckIfValidGpt(gptHeaderStart, imgBlockSize)) {
185 LOG(ERROR) << "check mbr or header fail";
186 emmcPtnDataInfo_.isGptVaild = false;
187 return false;
188 }
189
190 // for hisi: change ptable.img(512 bytes/block) into format of device(4096 bytes/block)
191 if (memcpy_s(emmcPtnDataInfo_.data, GPT_PARTITION_SIZE, gptImage, imgBlockSize) != EOK) {
192 LOG(ERROR) << "memcpy_s mbr fail";
193 return false;
194 }
195 if (memcpy_s(emmcPtnDataInfo_.data + deviceBlockSize, GPT_PARTITION_SIZE - deviceBlockSize,
196 gptHeaderStart, imgBlockSize) != EOK) {
197 LOG(ERROR) << "memcpy_s gpt header fail";
198 return false;
199 }
200 // skip 2 lba length to set gpt entry
201 if (memcpy_s(emmcPtnDataInfo_.data + 2 * deviceBlockSize, GPT_PARTITION_SIZE - 2 * deviceBlockSize,
202 ptableData, GPT_PARTITION_SIZE - 2 * deviceBlockSize) != EOK) {
203 LOG(ERROR) << "memcpy_s gpt data fail";
204 return false;
205 }
206 emmcPtnDataInfo_.writeDataLen = len;
207 if (!ParseGptHeaderByEmmc(emmcPtnDataInfo_.data, len)) {
208 LOG(ERROR) << "Primary signature invalid";
209 return false;
210 }
211 EmmcPatchGptHeader(emmcPtnDataInfo_, deviceBlockSize);
212 emmcPtnDataInfo_.isGptVaild = true;
213
214 return EmmcReadGpt(ptableData);
215 }
216
ReadEmmcGptImageToRam()217 bool EmmcPtable::ReadEmmcGptImageToRam()
218 {
219 #ifndef UPDATER_UT
220 std::string emmcNode = std::string(MMC_BLOCK_DEV_NAME);
221 #else
222 std::string emmcNode = "/data/updater_ext/ptable_parse/ptable.img";
223 #endif
224 int32_t imgLen = GPT_PARTITION_SIZE;
225 auto buf = std::make_unique<uint8_t[]>(imgLen);
226 uint8_t *buffer = buf.get();
227 uint32_t deviceBlockSize = ptableData_.blockSize; // 4096
228 if (buffer == nullptr) {
229 LOG(ERROR) << "new buffer failed!";
230 return false;
231 }
232 if (!MemReadWithOffset(emmcNode, 0, buffer, imgLen)) {
233 LOG(ERROR) << "read " << imgLen << " bytes from emmcNode " << emmcNode << " failed!";
234 return false;
235 }
236
237 #ifdef UPDATER_UT
238 deviceBlockSize = ptableData_.lbaLen;
239 #endif
240 uint8_t *gptHeaderStart = buffer + deviceBlockSize; // if image imgBlockSize, if device deviceBlockSize
241 if (!CheckProtectiveMbr(buffer, deviceBlockSize) || !CheckIfValidGpt(gptHeaderStart, deviceBlockSize)) {
242 LOG(ERROR) << "check mbr or header fail";
243 return false;
244 }
245
246 (void)memset_s(emmcPtnDataInfo_.data, GPT_PARTITION_SIZE, 0, GPT_PARTITION_SIZE);
247 if (memcpy_s(emmcPtnDataInfo_.data, GPT_PARTITION_SIZE, buffer, imgLen) != EOK) {
248 LOG(ERROR) << "memcpy_s GPT fail";
249 return false;
250 }
251
252 emmcPtnDataInfo_.isGptVaild = true;
253 uint8_t *ptableData = buffer + 2 * deviceBlockSize; /* skip MBR and gpt header */
254 return EmmcReadGpt(ptableData);
255 }
256
LoadPtableFromDevice()257 bool EmmcPtable::LoadPtableFromDevice()
258 {
259 if (!partitionInfo_.empty()) {
260 LOG(INFO) << "ptable is already loaded to ram";
261 return true;
262 }
263 if (ReadEmmcGptImageToRam()) {
264 LOG(INFO) << "init ptable to ram ok";
265 return true;
266 }
267 return false;
268 }
269
GetPtableImageBuffer(uint8_t * imageBuf,const uint32_t imgBufSize)270 bool EmmcPtable::GetPtableImageBuffer(uint8_t *imageBuf, const uint32_t imgBufSize)
271 {
272 if (imageBuf == nullptr || imgBufSize == 0) {
273 LOG(ERROR) << "input invalid";
274 return false;
275 }
276 if (memcpy_s(imageBuf, imgBufSize, emmcPtnDataInfo_.data, GPT_PARTITION_SIZE) != EOK) {
277 LOG(ERROR) << "memcpy_s failed";
278 return false;
279 }
280 return true;
281 }
282
EditPartitionBuf(uint8_t * imageBuf,uint64_t imgBufSize,std::vector<PtnInfo> & modifyList)283 bool EmmcPtable::EditPartitionBuf(uint8_t *imageBuf, uint64_t imgBufSize, std::vector<PtnInfo> &modifyList)
284 {
285 if (imageBuf == nullptr || imgBufSize == 0 || modifyList.empty() == 0) {
286 LOG(ERROR) << "input invalid";
287 return false;
288 }
289 uint8_t *gptImage = imageBuf;
290 uint32_t imgBlockSize = EMMC_BLOCK_SIZE;
291 uint8_t *gptHeader = gptImage + imgBlockSize;
292 uint32_t maxPtnCnt = GET_LWORD_FROM_BYTE(&gptHeader[PARTITION_COUNT_OFFSET]);
293 uint32_t ptnEntrySize = GET_LWORD_FROM_BYTE(&gptHeader[PENTRY_SIZE_OFFSET]);
294 uint32_t gptHeaderLen = EMMC_BLOCK_SIZE;
295 uint32_t gptSize = static_cast<uint64_t>(maxPtnCnt) * ptnEntrySize + imgBlockSize + gptHeaderLen;
296 uint32_t devDensity = GetDeviceCapacity();
297 if (devDensity == 0) {
298 LOG(ERROR) << "get emmc capacity fail";
299 return false;
300 }
301 uint32_t devBlockSize = EMMC_BLOCK_SIZE;
302 struct GptParseInfo gptInfo(imgBlockSize, devBlockSize, devDensity);
303 for (auto &t : modifyList) {
304 if (!ChangeGpt(gptImage, gptSize, gptInfo, t)) {
305 LOG(ERROR) << "ChangeGpt failed";
306 return false;
307 }
308 }
309 EmmcPartitionDataInfo newEmmcPartitionDataInfo;
310 newEmmcPartitionDataInfo.writeDataLen = ptableData_.emmcGptDataLen;
311 (void)memset_s(newEmmcPartitionDataInfo.data, GPT_PARTITION_SIZE, 0, GPT_PARTITION_SIZE);
312 if (memcpy_s(newEmmcPartitionDataInfo.data, GPT_PARTITION_SIZE, imageBuf, imgBlockSize) != EOK) {
313 LOG(ERROR) << "memcpy_s failed";
314 return false;
315 }
316 EmmcPatchGptHeader(newEmmcPartitionDataInfo, imgBlockSize);
317 return true;
318 }
319 }