18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * Parser for TRX format partitions 48c2ecf20Sopenharmony_ci * 58c2ecf20Sopenharmony_ci * Copyright (C) 2012 - 2017 Rafał Miłecki <rafal@milecki.pl> 68c2ecf20Sopenharmony_ci */ 78c2ecf20Sopenharmony_ci 88c2ecf20Sopenharmony_ci#include <linux/module.h> 98c2ecf20Sopenharmony_ci#include <linux/slab.h> 108c2ecf20Sopenharmony_ci#include <linux/mtd/mtd.h> 118c2ecf20Sopenharmony_ci#include <linux/mtd/partitions.h> 128c2ecf20Sopenharmony_ci 138c2ecf20Sopenharmony_ci#define TRX_PARSER_MAX_PARTS 4 148c2ecf20Sopenharmony_ci 158c2ecf20Sopenharmony_ci/* Magics */ 168c2ecf20Sopenharmony_ci#define TRX_MAGIC 0x30524448 178c2ecf20Sopenharmony_ci#define UBI_EC_MAGIC 0x23494255 /* UBI# */ 188c2ecf20Sopenharmony_ci 198c2ecf20Sopenharmony_cistruct trx_header { 208c2ecf20Sopenharmony_ci uint32_t magic; 218c2ecf20Sopenharmony_ci uint32_t length; 228c2ecf20Sopenharmony_ci uint32_t crc32; 238c2ecf20Sopenharmony_ci uint16_t flags; 248c2ecf20Sopenharmony_ci uint16_t version; 258c2ecf20Sopenharmony_ci uint32_t offset[3]; 268c2ecf20Sopenharmony_ci} __packed; 278c2ecf20Sopenharmony_ci 288c2ecf20Sopenharmony_cistatic const char *parser_trx_data_part_name(struct mtd_info *master, 298c2ecf20Sopenharmony_ci size_t offset) 308c2ecf20Sopenharmony_ci{ 318c2ecf20Sopenharmony_ci uint32_t buf; 328c2ecf20Sopenharmony_ci size_t bytes_read; 338c2ecf20Sopenharmony_ci int err; 348c2ecf20Sopenharmony_ci 358c2ecf20Sopenharmony_ci err = mtd_read(master, offset, sizeof(buf), &bytes_read, 368c2ecf20Sopenharmony_ci (uint8_t *)&buf); 378c2ecf20Sopenharmony_ci if (err && !mtd_is_bitflip(err)) { 388c2ecf20Sopenharmony_ci pr_err("mtd_read error while parsing (offset: 0x%zX): %d\n", 398c2ecf20Sopenharmony_ci offset, err); 408c2ecf20Sopenharmony_ci goto out_default; 418c2ecf20Sopenharmony_ci } 428c2ecf20Sopenharmony_ci 438c2ecf20Sopenharmony_ci if (buf == UBI_EC_MAGIC) 448c2ecf20Sopenharmony_ci return "ubi"; 458c2ecf20Sopenharmony_ci 468c2ecf20Sopenharmony_ciout_default: 478c2ecf20Sopenharmony_ci return "rootfs"; 488c2ecf20Sopenharmony_ci} 498c2ecf20Sopenharmony_ci 508c2ecf20Sopenharmony_cistatic int parser_trx_parse(struct mtd_info *mtd, 518c2ecf20Sopenharmony_ci const struct mtd_partition **pparts, 528c2ecf20Sopenharmony_ci struct mtd_part_parser_data *data) 538c2ecf20Sopenharmony_ci{ 548c2ecf20Sopenharmony_ci struct mtd_partition *parts; 558c2ecf20Sopenharmony_ci struct mtd_partition *part; 568c2ecf20Sopenharmony_ci struct trx_header trx; 578c2ecf20Sopenharmony_ci size_t bytes_read; 588c2ecf20Sopenharmony_ci uint8_t curr_part = 0, i = 0; 598c2ecf20Sopenharmony_ci int err; 608c2ecf20Sopenharmony_ci 618c2ecf20Sopenharmony_ci parts = kcalloc(TRX_PARSER_MAX_PARTS, sizeof(struct mtd_partition), 628c2ecf20Sopenharmony_ci GFP_KERNEL); 638c2ecf20Sopenharmony_ci if (!parts) 648c2ecf20Sopenharmony_ci return -ENOMEM; 658c2ecf20Sopenharmony_ci 668c2ecf20Sopenharmony_ci err = mtd_read(mtd, 0, sizeof(trx), &bytes_read, (uint8_t *)&trx); 678c2ecf20Sopenharmony_ci if (err) { 688c2ecf20Sopenharmony_ci pr_err("MTD reading error: %d\n", err); 698c2ecf20Sopenharmony_ci kfree(parts); 708c2ecf20Sopenharmony_ci return err; 718c2ecf20Sopenharmony_ci } 728c2ecf20Sopenharmony_ci 738c2ecf20Sopenharmony_ci if (trx.magic != TRX_MAGIC) { 748c2ecf20Sopenharmony_ci kfree(parts); 758c2ecf20Sopenharmony_ci return -ENOENT; 768c2ecf20Sopenharmony_ci } 778c2ecf20Sopenharmony_ci 788c2ecf20Sopenharmony_ci /* We have LZMA loader if there is address in offset[2] */ 798c2ecf20Sopenharmony_ci if (trx.offset[2]) { 808c2ecf20Sopenharmony_ci part = &parts[curr_part++]; 818c2ecf20Sopenharmony_ci part->name = "loader"; 828c2ecf20Sopenharmony_ci part->offset = trx.offset[i]; 838c2ecf20Sopenharmony_ci i++; 848c2ecf20Sopenharmony_ci } 858c2ecf20Sopenharmony_ci 868c2ecf20Sopenharmony_ci if (trx.offset[i]) { 878c2ecf20Sopenharmony_ci part = &parts[curr_part++]; 888c2ecf20Sopenharmony_ci part->name = "linux"; 898c2ecf20Sopenharmony_ci part->offset = trx.offset[i]; 908c2ecf20Sopenharmony_ci i++; 918c2ecf20Sopenharmony_ci } 928c2ecf20Sopenharmony_ci 938c2ecf20Sopenharmony_ci if (trx.offset[i]) { 948c2ecf20Sopenharmony_ci part = &parts[curr_part++]; 958c2ecf20Sopenharmony_ci part->name = parser_trx_data_part_name(mtd, trx.offset[i]); 968c2ecf20Sopenharmony_ci part->offset = trx.offset[i]; 978c2ecf20Sopenharmony_ci i++; 988c2ecf20Sopenharmony_ci } 998c2ecf20Sopenharmony_ci 1008c2ecf20Sopenharmony_ci /* 1018c2ecf20Sopenharmony_ci * Assume that every partition ends at the beginning of the one it is 1028c2ecf20Sopenharmony_ci * followed by. 1038c2ecf20Sopenharmony_ci */ 1048c2ecf20Sopenharmony_ci for (i = 0; i < curr_part; i++) { 1058c2ecf20Sopenharmony_ci u64 next_part_offset = (i < curr_part - 1) ? 1068c2ecf20Sopenharmony_ci parts[i + 1].offset : mtd->size; 1078c2ecf20Sopenharmony_ci 1088c2ecf20Sopenharmony_ci parts[i].size = next_part_offset - parts[i].offset; 1098c2ecf20Sopenharmony_ci } 1108c2ecf20Sopenharmony_ci 1118c2ecf20Sopenharmony_ci *pparts = parts; 1128c2ecf20Sopenharmony_ci return i; 1138c2ecf20Sopenharmony_ci}; 1148c2ecf20Sopenharmony_ci 1158c2ecf20Sopenharmony_cistatic const struct of_device_id mtd_parser_trx_of_match_table[] = { 1168c2ecf20Sopenharmony_ci { .compatible = "brcm,trx" }, 1178c2ecf20Sopenharmony_ci {}, 1188c2ecf20Sopenharmony_ci}; 1198c2ecf20Sopenharmony_ciMODULE_DEVICE_TABLE(of, mtd_parser_trx_of_match_table); 1208c2ecf20Sopenharmony_ci 1218c2ecf20Sopenharmony_cistatic struct mtd_part_parser mtd_parser_trx = { 1228c2ecf20Sopenharmony_ci .parse_fn = parser_trx_parse, 1238c2ecf20Sopenharmony_ci .name = "trx", 1248c2ecf20Sopenharmony_ci .of_match_table = mtd_parser_trx_of_match_table, 1258c2ecf20Sopenharmony_ci}; 1268c2ecf20Sopenharmony_cimodule_mtd_part_parser(mtd_parser_trx); 1278c2ecf20Sopenharmony_ci 1288c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL v2"); 1298c2ecf20Sopenharmony_ciMODULE_DESCRIPTION("Parser for TRX format partitions"); 130