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