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 "ptable.h"
17
18 #include <map>
19 #include <sys/stat.h>
20
21 #include "applypatch/data_writer.h"
22 #include "log/log.h"
23 #include "securec.h"
24
25 namespace Updater {
26 constexpr const char *PTABLE_CONFIG_PATH = "/etc/ptable_data.json";
27 constexpr const char *PTABLE_DATA_LABEL = "ptableData";
28 constexpr const char *EMMC_GPT_DATA_LEN_LABEL = "emmcGptDataLen";
29 constexpr const char *LBA_LEN_LABEL = "lbaLen";
30 constexpr const char *GPT_HEADER_LEN_LABEL = "gptHeaderLen";
31 constexpr const char *BLOCK_SIZE_LABEL = "blockSize";
32 constexpr const char *IMG_LUN_SIZE_LABEL = "imgLuSize";
33 constexpr const char *START_LUN_NUM_LABEL = "startLunNumber";
34 constexpr const char *WRITE_DEVICE_LUN_SIZE_LABEL = "writeDeviceLunSize";
35 constexpr const char *DEFAULT_LUN_NUM_LABEL = "defaultLunNum";
36
GetPtablePartitionInfo() const37 std::vector<Ptable::PtnInfo> Ptable::GetPtablePartitionInfo() const
38 {
39 return partitionInfo_;
40 }
41
GetPtablePartitionNum() const42 uint32_t Ptable::GetPtablePartitionNum() const
43 {
44 return partitionInfo_.size();
45 }
46
LoadPtnInfo(const std::vector<PtnInfo> & ptnInfo)47 bool Ptable::LoadPtnInfo(const std::vector<PtnInfo> &ptnInfo)
48 {
49 if (ptnInfo.empty()) {
50 LOG(ERROR) << "ptnInfo is empty";
51 return false;
52 }
53 partitionInfo_ = ptnInfo;
54 return true;
55 }
56
GetPtablePartitionInfoInstance()57 std::vector<Ptable::PtnInfo>& Ptable::GetPtablePartitionInfoInstance()
58 {
59 return partitionInfo_;
60 }
61
InitPtable()62 bool Ptable::InitPtable()
63 {
64 if (!partitionInfo_.empty()) {
65 std::vector<PtnInfo>().swap(partitionInfo_);
66 }
67 if (!ParsePtableData()) {
68 LOG(ERROR) << "parse PtableData from json file error";
69 return false;
70 }
71 return true;
72 }
73
ParsePtableDataNode(const JsonNode & ptableDataNode)74 bool Ptable::ParsePtableDataNode(const JsonNode &ptableDataNode)
75 {
76 std::map<std::string, uint32_t*> ptableDataVars = {
77 {EMMC_GPT_DATA_LEN_LABEL, &ptableData_.emmcGptDataLen},
78 {LBA_LEN_LABEL, &ptableData_.lbaLen},
79 {GPT_HEADER_LEN_LABEL, &ptableData_.gptHeaderLen},
80 {BLOCK_SIZE_LABEL, &ptableData_.blockSize},
81 {IMG_LUN_SIZE_LABEL, &ptableData_.imgLuSize},
82 {START_LUN_NUM_LABEL, &ptableData_.startLunNumber},
83 {WRITE_DEVICE_LUN_SIZE_LABEL, &ptableData_.writeDeviceLunSize},
84 {DEFAULT_LUN_NUM_LABEL, &ptableData_.defaultLunNum},
85 };
86
87 for (auto dataVar : ptableDataVars) {
88 auto dataValue = ptableDataNode[dataVar.first.c_str()].As<uint32_t>();
89 if (!dataValue) {
90 LOG(ERROR) << "parse json failed! " << dataVar.first << " is nullptr!";
91 return false;
92 }
93 *(dataVar.second) = *dataValue;
94 LOG(INFO) << "set " << dataVar.first << " : " << *dataValue;
95 }
96 return true;
97 }
98
ParsePtableData()99 bool Ptable::ParsePtableData()
100 {
101 (void)memset_s(&ptableData_, sizeof(ptableData_), 0, sizeof(ptableData_));
102 std::ifstream ifs(std::string {PTABLE_CONFIG_PATH});
103 if (!ifs.is_open()) {
104 LOG(ERROR) << PTABLE_CONFIG_PATH << " not exist";
105 return false;
106 }
107
108 // get root node
109 std::string content {std::istreambuf_iterator<char> {ifs}, {}};
110 cJSONPtr root(cJSON_Parse(content.c_str()), cJSON_Delete);
111 if (root == nullptr) {
112 LOG(ERROR) << PTABLE_CONFIG_PATH << " contained json invalid";
113 return false;
114 }
115
116 JsonNode node(root.get(), false);
117 const JsonNode &ptableDataNode = node[PTABLE_DATA_LABEL];
118 bool ret = ParsePtableDataNode(ptableDataNode);
119 ptableData_.dataValid = ret;
120 return ret;
121 }
122
GetDefaultImageSize() const123 uint32_t Ptable::GetDefaultImageSize() const
124 {
125 return ptableData_.emmcGptDataLen + ptableData_.defaultLunNum * ptableData_.imgLuSize;
126 }
127
CheckFileExist(const std::string & fileName)128 bool Ptable::CheckFileExist(const std::string &fileName)
129 {
130 struct stat buffers;
131 if (memset_s(&buffers, sizeof(buffers), 0, sizeof(buffers)) != EOK) {
132 LOG(WARNING) << "memset_s fail";
133 }
134 if (stat(fileName.c_str(), &buffers) == 0) {
135 LOG(INFO) << fileName << " is exist";
136 return true;
137 }
138 LOG(INFO) << fileName << " is not exist";
139 return false;
140 }
141
MemReadWithOffset(const std::string & filePath,const uint64_t offset,uint8_t * outData,const uint32_t dataSize)142 bool Ptable::MemReadWithOffset(const std::string &filePath, const uint64_t offset,
143 uint8_t *outData, const uint32_t dataSize)
144 {
145 if (filePath.length() == 0 || outData == nullptr || dataSize == 0) {
146 LOG(ERROR) << "invaild input";
147 return false;
148 }
149
150 std::ifstream fin(filePath, std::ios::in);
151 if (fin.fail()) {
152 LOG(ERROR) << "open " << filePath << " fail";
153 return false;
154 }
155
156 fin.seekg(offset, std::ios::beg);
157 if (fin.tellg() != static_cast<long long>(offset)) {
158 LOG(ERROR) << "seekp 0x" << std::hex << offset << " bytes in " << filePath <<
159 " failed. Now is in 0x" << std::hex << fin.tellg() << std::dec;
160 fin.close();
161 return false;
162 }
163
164 if (!fin.read(reinterpret_cast<char *>(outData), dataSize)) {
165 LOG(ERROR) << "read 0x" << std::hex << dataSize << " bytes in " << filePath <<
166 " failed. only read 0x" << std::hex << fin.gcount() << std::dec;
167 fin.close();
168 return false;
169 }
170 fin.close();
171 return true;
172 }
173
Reflect(uint32_t data,const uint32_t len)174 uint32_t Ptable::Reflect(uint32_t data, const uint32_t len)
175 {
176 uint32_t ref = 0;
177 for (uint32_t i = 0; i < len; i++) {
178 if (data & 0x1) {
179 ref |= (1 << ((len - 1) - i));
180 }
181 data = (data >> 1);
182 }
183 return ref;
184 }
185
CalculateCrc32(const uint8_t * buffer,const uint32_t len)186 uint32_t Ptable::CalculateCrc32(const uint8_t *buffer, const uint32_t len)
187 {
188 if (buffer == nullptr || len == 0) {
189 LOG(INFO) << "invaild input";
190 return 0;
191 }
192 const uint32_t byteLen = 8; // 8:length of unit (i.e. byte)
193 uint32_t msb;
194 const uint64_t polynomial = 0x104C11DB7LL; // IEEE 32bit polynomial
195 uint32_t regs = 0xFFFFFFFF; // init to all ones
196 const uint32_t regsMask = 0xFFFFFFFF; // ensure only 32 bit answer
197 uint32_t regsMsb;
198 for (uint32_t i = 0; i < len; i++) {
199 uint32_t dataByte = buffer[i];
200 dataByte = Reflect(dataByte, 8); // 8:length of unit (i.e. byte)
201 for (uint32_t j = 0; j < byteLen; j++) {
202 msb = dataByte >> (byteLen - 1); // get MSB
203 msb &= 1; // ensure just 1 bit
204 regsMsb = (regs >> 31) & 1; // 31:32-1, MSB of regs
205 regs = regs << 1; // shift regs for CRC-CCITT
206 if (regsMsb ^ msb) { // MSB is a 1
207 regs = regs ^ polynomial; // XOR with generator poly
208 }
209 regs = regs & regsMask; // Mask off excess upper bits
210 dataByte <<= 1; // get to next bit
211 }
212 }
213 regs = regs & regsMask;
214 uint32_t ret = Reflect(regs, 32) ^ 0xFFFFFFFF; // 32:32bit
215 return ret;
216 }
217
VerifyMbrMagicNum(const uint8_t * buffer,const uint32_t size)218 bool Ptable::VerifyMbrMagicNum(const uint8_t *buffer, const uint32_t size)
219 {
220 // avoid checking past end of buffer
221 if (size < (MBR_MAGIC_NUM_POS + 1)) {
222 LOG(ERROR) << "size < (TABLE_SIGNATURE + 1)";
223 return false;
224 }
225 // check to see if magic number(0x55AA) exists at pos 0x1FE
226 if ((buffer[MBR_MAGIC_NUM_POS] != MBR_MAGIC_NUM_0) ||
227 (buffer[MBR_MAGIC_NUM_POS + 1] != MBR_MAGIC_NUM_1)) {
228 LOG(ERROR) << "MBR magic number does not match, magic buffer is " << unsigned(*(buffer + MBR_MAGIC_NUM_POS));
229 return false;
230 }
231 return true;
232 }
233
CheckProtectiveMbr(const uint8_t * gptImage,const uint32_t imgLen)234 bool Ptable::CheckProtectiveMbr(const uint8_t *gptImage, const uint32_t imgLen)
235 {
236 if (!VerifyMbrMagicNum(gptImage, imgLen)) {
237 LOG(ERROR) << "MBR magic number verify failed!";
238 return false;
239 }
240
241 // process each of the four partitions in the MBR, find a Protective MBR(0xEE)
242 uint32_t type;
243 for (uint32_t i = 0; i < MBR_GPT_MAX_NUM; i++) {
244 // type 0xEE indicates the protective MBR and GPT partitions exist
245 if (MBR_GPT_ENTRY + i * MBR_GPT_ENTRY_SIZE + GPT_TYPE_SIGN_OFFSET >= imgLen) {
246 LOG(INFO) << "not find Protective MBR(type: 0xEE) in this partition";
247 return false;
248 }
249 type = gptImage[MBR_GPT_ENTRY + i * MBR_GPT_ENTRY_SIZE + GPT_TYPE_SIGN_OFFSET];
250 if (type == MBR_PROTECTIVE_GPT_TYPE) {
251 LOG(INFO) << "type is MBR_PROTECTIVE_GPT_TYPE(0xEE), GPT partitions exist";
252 return true;
253 }
254 LOG(INFO) << "the " << i << " main GPT's type=0x" << std::hex << type << std::dec;
255 }
256 LOG(INFO) << "not find Protective MBR(type: 0xEE) in this partition";
257 return false;
258 }
259
CheckIfValidGpt(const uint8_t * gptImage,const uint32_t gptImageLen)260 bool Ptable::CheckIfValidGpt(const uint8_t *gptImage, const uint32_t gptImageLen)
261 {
262 // 8 is the length of EFI_MAGIC_NUMBER
263 if (gptImageLen < 8) {
264 LOG(ERROR) << "gptImageLen is less than 8.";
265 return false;
266 }
267 // get magic number
268 uint64_t gptMagic = GET_LLWORD_FROM_BYTE(gptImage);
269 if (gptMagic != EFI_MAGIC_NUMBER) {
270 LOG(ERROR) << "invaild partiton with gptMagic:0x" << std::hex << gptMagic << std::dec;
271 return false;
272 }
273 return true;
274 }
275
GetCapacity(const std::string & filePath,uint64_t & lunCapacity)276 bool Ptable::GetCapacity(const std::string &filePath, uint64_t &lunCapacity)
277 {
278 if (filePath.empty()) {
279 LOG(ERROR) << "filePath is empty or lunCapacity is nullptr";
280 return false;
281 }
282 std::ifstream fin(filePath, std::ios::in);
283 if (!fin.is_open()) {
284 LOG(ERROR) << "open " << filePath << " fail";
285 return false;
286 }
287
288 uint64_t sector = 0;
289 fin >> sector;
290 if (sector == 0) {
291 LOG(ERROR) << "read data from " << filePath << " fail";
292 fin.close();
293 return false;
294 }
295
296 uint64_t capacity = sector * SECTOR_SIZE;
297 LOG(INFO) << "lun capacity = 0x" << std::hex << capacity << std::dec;
298 lunCapacity = capacity;
299 fin.close();
300 return true;
301 }
302
GetPartitionGptHeaderInfo(const uint8_t * buffer,const uint32_t bufferLen,GPTHeaderInfo & gptHeaderInfo)303 bool Ptable::GetPartitionGptHeaderInfo(const uint8_t *buffer, const uint32_t bufferLen, GPTHeaderInfo& gptHeaderInfo)
304 {
305 if (buffer == nullptr || bufferLen < LBA_LENGTH) {
306 LOG(ERROR) << "input invalid";
307 return false;
308 }
309
310 // Check GPT Signature
311 if (!CheckIfValidGpt(buffer, bufferLen)) {
312 LOG(ERROR) << "invaild partiton with gptMagic";
313 return false;
314 }
315 gptHeaderInfo.headerSize = GET_LWORD_FROM_BYTE(buffer + HEADER_SIZE_OFFSET);
316 gptHeaderInfo.firstUsableLba = GET_LLWORD_FROM_BYTE(buffer + FIRST_USABLE_LBA_OFFSET);
317 gptHeaderInfo.maxPartitionCount = GET_LWORD_FROM_BYTE(buffer + PARTITION_COUNT_OFFSET);
318 gptHeaderInfo.partitionEntrySize = GET_LWORD_FROM_BYTE(buffer + PENTRY_SIZE_OFFSET);
319 if (gptHeaderInfo.maxPartitionCount == 0 || gptHeaderInfo.partitionEntrySize == 0) {
320 LOG(ERROR) << "invalid gpt header info";
321 return false;
322 }
323 return true;
324 }
325
CheckGptHeader(uint8_t * buffer,const uint32_t bufferLen,const uint64_t lbaNum,const GPTHeaderInfo & gptHeaderInfo)326 bool Ptable::CheckGptHeader(uint8_t *buffer, const uint32_t bufferLen, const uint64_t lbaNum,
327 const GPTHeaderInfo& gptHeaderInfo)
328 {
329 if (bufferLen < LBA_LENGTH || lbaNum == 0) {
330 LOG(ERROR) << "bufferLen < LBA_LENGTH || lbaNum == 0";
331 return false;
332 }
333
334 if (gptHeaderInfo.headerSize < GPT_HEADER_SIZE || gptHeaderInfo.headerSize > bufferLen) {
335 LOG(ERROR) << "GPT Header size is invaild";
336 return false;
337 }
338 uint32_t orgCrcVal = GET_LWORD_FROM_BYTE(buffer + HEADER_CRC_OFFSET);
339 // Write CRC field to 0 before calculating the crc of the whole rest of GPT header
340 PUT_LONG(buffer + HEADER_CRC_OFFSET, 0);
341 uint32_t crcVal = CalculateCrc32(buffer, gptHeaderInfo.headerSize);
342 if (crcVal != orgCrcVal) {
343 LOG(ERROR) << "Header crc mismatch crcVal = " << std::hex << crcVal << " with orgCrcVal = " <<
344 orgCrcVal << std::dec;
345 return false;
346 }
347 PUT_LONG(buffer + HEADER_CRC_OFFSET, crcVal);
348
349 uint32_t currentLba = GET_LLWORD_FROM_BYTE(buffer + PRIMARY_HEADER_OFFSET);
350 uint32_t lastUsableLba = GET_LLWORD_FROM_BYTE(buffer + LAST_USABLE_LBA_OFFSET);
351 uint32_t partition0 = GET_LLWORD_FROM_BYTE(buffer + PARTITION_ENTRIES_OFFSET);
352
353 // check for first and last lba range
354 if (gptHeaderInfo.firstUsableLba > lbaNum || lastUsableLba > lbaNum) {
355 LOG(ERROR) << "invalid usable lba " << gptHeaderInfo.firstUsableLba << ", last is " << lastUsableLba <<
356 " lbaNum is " << lbaNum;
357 return false;
358 }
359 // check for partition entry size
360 if (gptHeaderInfo.partitionEntrySize != PARTITION_ENTRY_SIZE ||
361 gptHeaderInfo.maxPartitionCount > (MIN_PARTITION_ARRAY_SIZE / PARTITION_ENTRY_SIZE)) {
362 LOG(ERROR) << "invalid parition entry size or max count";
363 return false;
364 }
365 // GPT header should always be the 0x1 LBA, partition entry should always the 0x2 LBA
366 if (currentLba != 0x1 || partition0 != 0x2) {
367 LOG(ERROR) << "starting LBA mismatch";
368 return false;
369 }
370 LOG(INFO) << "ufs gpt header check ok";
371 return true;
372 }
373
PartitionCheckGptHeader(const uint8_t * gptImage,const uint32_t len,const uint64_t lbaNum,const uint32_t blockSize,GPTHeaderInfo & gptHeaderInfo)374 bool Ptable::PartitionCheckGptHeader(const uint8_t *gptImage, const uint32_t len, const uint64_t lbaNum,
375 const uint32_t blockSize, GPTHeaderInfo& gptHeaderInfo)
376 {
377 if (len < ptableData_.writeDeviceLunSize || lbaNum == 0) {
378 LOG(ERROR) << "len < ptableData_.writeDeviceLunSize || lbaNum == 0";
379 return false;
380 }
381
382 uint8_t *buffer = new(std::nothrow) uint8_t[blockSize]();
383 if (buffer == nullptr) {
384 LOG(ERROR) << "new buffer failed!";
385 return false;
386 }
387 if (memcpy_s(buffer, blockSize, gptImage + blockSize, blockSize) != EOK) {
388 LOG(ERROR) << "copy gpt header fail";
389 delete [] buffer;
390 return false;
391 }
392
393 if (!CheckGptHeader(buffer, blockSize, lbaNum, gptHeaderInfo)) {
394 LOG(ERROR) << "CheckGptHeader fail";
395 delete [] buffer;
396 return false;
397 }
398
399 uint32_t partition0 = GET_LLWORD_FROM_BYTE(&buffer[PARTITION_ENTRIES_OFFSET]);
400 uint32_t orgCrcVal = GET_LWORD_FROM_BYTE(&buffer[PARTITION_CRC_OFFSET]);
401 delete [] buffer;
402
403 uint32_t crcVal = CalculateCrc32(gptImage + partition0 * blockSize,
404 gptHeaderInfo.maxPartitionCount * gptHeaderInfo.partitionEntrySize);
405 if (crcVal != orgCrcVal) {
406 LOG(ERROR) << "partition entires crc mismatch crcVal =" << std::hex << crcVal << " with orgCrcVal =" <<
407 orgCrcVal << std::dec;
408 return false;
409 }
410 LOG(INFO) << "UfsPartitionCheckGptHeader ok";
411 return true;
412 }
413
PrintPtableInfo() const414 void Ptable::PrintPtableInfo() const
415 {
416 if (partitionInfo_.empty()) {
417 LOG(ERROR) << "ptable vector is empty!";
418 return;
419 }
420
421 LOG(INFO) << "ptnInfo : ===========================================";
422 LOG(INFO) << "partition count = " << std::dec << partitionInfo_.size();
423 for (size_t i = 0; i < partitionInfo_.size(); i++) {
424 LOG(INFO) << "ptable.entry[" << i << "].name=" << partitionInfo_[i].dispName.c_str() <<
425 ", startAddr=0x" << std::hex << partitionInfo_[i].startAddr << ", size=0x" <<
426 partitionInfo_[i].partitionSize << ", lun=" << std::dec << partitionInfo_[i].lun;
427 }
428 LOG(INFO) << "ptnInfo : ===========================================";
429 }
430
PrintPtableInfo(const std::vector<PtnInfo> & ptnInfo) const431 void Ptable::PrintPtableInfo(const std::vector<PtnInfo> &ptnInfo) const
432 {
433 if (ptnInfo.empty()) {
434 LOG(ERROR) << "ptable vector is empty!";
435 return;
436 }
437
438 LOG(INFO) << "ptnInfo : ===========================================";
439 LOG(INFO) << "partition count = " << std::dec << ptnInfo.size();
440 for (size_t i = 0; i < ptnInfo.size(); i++) {
441 LOG(INFO) << "ptable.entry[" << i << "].name=" << ptnInfo[i].dispName.c_str() << ", startAddr=0x" <<
442 std::hex << ptnInfo[i].startAddr << ", size=0x" << ptnInfo[i].partitionSize << ", lun=" <<
443 std::dec << ptnInfo[i].lun;
444 }
445 LOG(INFO) << "ptnInfo : ===========================================";
446 }
447
ParsePartitionName(const uint8_t * data,const uint32_t dataLen,std::string & name,const uint32_t nameLen)448 void Ptable::ParsePartitionName(const uint8_t *data, const uint32_t dataLen,
449 std::string &name, const uint32_t nameLen)
450 {
451 if (data == nullptr || dataLen == 0 || nameLen == 0) {
452 LOG(ERROR) << "dataLen == 0 || nameLen == 0";
453 return;
454 }
455 char utf16Name[MAX_GPT_NAME_SIZE] = {0};
456 if (memcpy_s(utf16Name, sizeof(utf16Name), data, dataLen) != EOK) {
457 LOG(ERROR) << "memcpy name fail";
458 return;
459 }
460
461 std::string outName;
462 // convert utf8 to utf16, 2 bytes for 1 charactor of partition name
463 for (uint32_t n = 0; n < nameLen && n < (MAX_GPT_NAME_SIZE / 2) && utf16Name[n * 2] != '\0'; n++) {
464 outName = outName + utf16Name[n * 2];
465 }
466 for (uint32_t i = 0; i < outName.size(); i++) {
467 outName[i] = static_cast<char>(toupper(outName[i]));
468 }
469 name = outName;
470 return;
471 }
472
WriteBufferToPath(const std::string & path,const uint64_t offset,const uint8_t * buffer,const uint32_t size)473 bool Ptable::WriteBufferToPath(const std::string &path, const uint64_t offset,
474 const uint8_t *buffer, const uint32_t size)
475 {
476 std::unique_ptr<DataWriter> writer = DataWriter::CreateDataWriter(WRITE_RAW, path, offset);
477 if (writer == nullptr) {
478 LOG(ERROR) << "create writer class failed!";
479 return false;
480 }
481 bool ret = writer->Write(buffer, size, nullptr);
482 if (!ret) {
483 LOG(ERROR) << "writer to " << path << " with offset " << offset << " failed ";
484 DataWriter::ReleaseDataWriter(writer);
485 return false;
486 }
487 DataWriter::ReleaseDataWriter(writer);
488 return true;
489 }
490
GetPartionInfoByName(const std::string & partitionName,PtnInfo & ptnInfo,int32_t & index)491 bool Ptable::GetPartionInfoByName(const std::string &partitionName, PtnInfo &ptnInfo, int32_t &index)
492 {
493 if (partitionInfo_.empty()) {
494 LOG(ERROR) << "get partition failed! partitionInfo_ is empty";
495 return false;
496 }
497 for (int32_t i = 0; i < static_cast<int32_t>(partitionInfo_.size()); i++) {
498 if (partitionInfo_[i].dispName.size() == partitionName.size() &&
499 strcasecmp(partitionInfo_[i].dispName.c_str(), partitionName.c_str()) == 0) {
500 index = i;
501 ptnInfo = partitionInfo_[i];
502 return true;
503 }
504 }
505 LOG(ERROR) << "get partition info failed! Not found partition:" << partitionName;
506 return false;
507 }
508 } // namespace Updater