162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * Parser for TRX format partitions 462306a36Sopenharmony_ci * 562306a36Sopenharmony_ci * Copyright (C) 2012 - 2017 Rafał Miłecki <rafal@milecki.pl> 662306a36Sopenharmony_ci */ 762306a36Sopenharmony_ci 862306a36Sopenharmony_ci#include <linux/module.h> 962306a36Sopenharmony_ci#include <linux/slab.h> 1062306a36Sopenharmony_ci#include <linux/mtd/mtd.h> 1162306a36Sopenharmony_ci#include <linux/mtd/partitions.h> 1262306a36Sopenharmony_ci 1362306a36Sopenharmony_ci#define TRX_PARSER_MAX_PARTS 4 1462306a36Sopenharmony_ci 1562306a36Sopenharmony_ci/* Magics */ 1662306a36Sopenharmony_ci#define TRX_MAGIC 0x30524448 1762306a36Sopenharmony_ci#define UBI_EC_MAGIC 0x23494255 /* UBI# */ 1862306a36Sopenharmony_ci 1962306a36Sopenharmony_cistruct trx_header { 2062306a36Sopenharmony_ci uint32_t magic; 2162306a36Sopenharmony_ci uint32_t length; 2262306a36Sopenharmony_ci uint32_t crc32; 2362306a36Sopenharmony_ci uint16_t flags; 2462306a36Sopenharmony_ci uint16_t version; 2562306a36Sopenharmony_ci uint32_t offset[3]; 2662306a36Sopenharmony_ci} __packed; 2762306a36Sopenharmony_ci 2862306a36Sopenharmony_cistatic const char *parser_trx_data_part_name(struct mtd_info *master, 2962306a36Sopenharmony_ci size_t offset) 3062306a36Sopenharmony_ci{ 3162306a36Sopenharmony_ci uint32_t buf; 3262306a36Sopenharmony_ci size_t bytes_read; 3362306a36Sopenharmony_ci int err; 3462306a36Sopenharmony_ci 3562306a36Sopenharmony_ci err = mtd_read(master, offset, sizeof(buf), &bytes_read, 3662306a36Sopenharmony_ci (uint8_t *)&buf); 3762306a36Sopenharmony_ci if (err && !mtd_is_bitflip(err)) { 3862306a36Sopenharmony_ci pr_err("mtd_read error while parsing (offset: 0x%zX): %d\n", 3962306a36Sopenharmony_ci offset, err); 4062306a36Sopenharmony_ci goto out_default; 4162306a36Sopenharmony_ci } 4262306a36Sopenharmony_ci 4362306a36Sopenharmony_ci if (buf == UBI_EC_MAGIC) 4462306a36Sopenharmony_ci return "ubi"; 4562306a36Sopenharmony_ci 4662306a36Sopenharmony_ciout_default: 4762306a36Sopenharmony_ci return "rootfs"; 4862306a36Sopenharmony_ci} 4962306a36Sopenharmony_ci 5062306a36Sopenharmony_cistatic int parser_trx_parse(struct mtd_info *mtd, 5162306a36Sopenharmony_ci const struct mtd_partition **pparts, 5262306a36Sopenharmony_ci struct mtd_part_parser_data *data) 5362306a36Sopenharmony_ci{ 5462306a36Sopenharmony_ci struct device_node *np = mtd_get_of_node(mtd); 5562306a36Sopenharmony_ci struct mtd_partition *parts; 5662306a36Sopenharmony_ci struct mtd_partition *part; 5762306a36Sopenharmony_ci struct trx_header trx; 5862306a36Sopenharmony_ci size_t bytes_read; 5962306a36Sopenharmony_ci uint8_t curr_part = 0, i = 0; 6062306a36Sopenharmony_ci uint32_t trx_magic = TRX_MAGIC; 6162306a36Sopenharmony_ci int err; 6262306a36Sopenharmony_ci 6362306a36Sopenharmony_ci /* Get different magic from device tree if specified */ 6462306a36Sopenharmony_ci err = of_property_read_u32(np, "brcm,trx-magic", &trx_magic); 6562306a36Sopenharmony_ci if (err != 0 && err != -EINVAL) 6662306a36Sopenharmony_ci pr_err("failed to parse \"brcm,trx-magic\" DT attribute, using default: %d\n", err); 6762306a36Sopenharmony_ci 6862306a36Sopenharmony_ci parts = kcalloc(TRX_PARSER_MAX_PARTS, sizeof(struct mtd_partition), 6962306a36Sopenharmony_ci GFP_KERNEL); 7062306a36Sopenharmony_ci if (!parts) 7162306a36Sopenharmony_ci return -ENOMEM; 7262306a36Sopenharmony_ci 7362306a36Sopenharmony_ci err = mtd_read(mtd, 0, sizeof(trx), &bytes_read, (uint8_t *)&trx); 7462306a36Sopenharmony_ci if (err) { 7562306a36Sopenharmony_ci pr_err("MTD reading error: %d\n", err); 7662306a36Sopenharmony_ci kfree(parts); 7762306a36Sopenharmony_ci return err; 7862306a36Sopenharmony_ci } 7962306a36Sopenharmony_ci 8062306a36Sopenharmony_ci if (trx.magic != trx_magic) { 8162306a36Sopenharmony_ci kfree(parts); 8262306a36Sopenharmony_ci return -ENOENT; 8362306a36Sopenharmony_ci } 8462306a36Sopenharmony_ci 8562306a36Sopenharmony_ci /* We have LZMA loader if there is address in offset[2] */ 8662306a36Sopenharmony_ci if (trx.offset[2]) { 8762306a36Sopenharmony_ci part = &parts[curr_part++]; 8862306a36Sopenharmony_ci part->name = "loader"; 8962306a36Sopenharmony_ci part->offset = trx.offset[i]; 9062306a36Sopenharmony_ci i++; 9162306a36Sopenharmony_ci } 9262306a36Sopenharmony_ci 9362306a36Sopenharmony_ci if (trx.offset[i]) { 9462306a36Sopenharmony_ci part = &parts[curr_part++]; 9562306a36Sopenharmony_ci part->name = "linux"; 9662306a36Sopenharmony_ci part->offset = trx.offset[i]; 9762306a36Sopenharmony_ci i++; 9862306a36Sopenharmony_ci } 9962306a36Sopenharmony_ci 10062306a36Sopenharmony_ci if (trx.offset[i]) { 10162306a36Sopenharmony_ci part = &parts[curr_part++]; 10262306a36Sopenharmony_ci part->name = parser_trx_data_part_name(mtd, trx.offset[i]); 10362306a36Sopenharmony_ci part->offset = trx.offset[i]; 10462306a36Sopenharmony_ci i++; 10562306a36Sopenharmony_ci } 10662306a36Sopenharmony_ci 10762306a36Sopenharmony_ci /* 10862306a36Sopenharmony_ci * Assume that every partition ends at the beginning of the one it is 10962306a36Sopenharmony_ci * followed by. 11062306a36Sopenharmony_ci */ 11162306a36Sopenharmony_ci for (i = 0; i < curr_part; i++) { 11262306a36Sopenharmony_ci u64 next_part_offset = (i < curr_part - 1) ? 11362306a36Sopenharmony_ci parts[i + 1].offset : mtd->size; 11462306a36Sopenharmony_ci 11562306a36Sopenharmony_ci parts[i].size = next_part_offset - parts[i].offset; 11662306a36Sopenharmony_ci } 11762306a36Sopenharmony_ci 11862306a36Sopenharmony_ci *pparts = parts; 11962306a36Sopenharmony_ci return i; 12062306a36Sopenharmony_ci}; 12162306a36Sopenharmony_ci 12262306a36Sopenharmony_cistatic const struct of_device_id mtd_parser_trx_of_match_table[] = { 12362306a36Sopenharmony_ci { .compatible = "brcm,trx" }, 12462306a36Sopenharmony_ci {}, 12562306a36Sopenharmony_ci}; 12662306a36Sopenharmony_ciMODULE_DEVICE_TABLE(of, mtd_parser_trx_of_match_table); 12762306a36Sopenharmony_ci 12862306a36Sopenharmony_cistatic struct mtd_part_parser mtd_parser_trx = { 12962306a36Sopenharmony_ci .parse_fn = parser_trx_parse, 13062306a36Sopenharmony_ci .name = "trx", 13162306a36Sopenharmony_ci .of_match_table = mtd_parser_trx_of_match_table, 13262306a36Sopenharmony_ci}; 13362306a36Sopenharmony_cimodule_mtd_part_parser(mtd_parser_trx); 13462306a36Sopenharmony_ci 13562306a36Sopenharmony_ciMODULE_LICENSE("GPL v2"); 13662306a36Sopenharmony_ciMODULE_DESCRIPTION("Parser for TRX format partitions"); 137