xref: /openbmc/linux/drivers/mtd/parsers/tplink_safeloader.c (revision 9a87ffc99ec8eb8d35eed7c4f816d75f5cc9662e)
100a35880SRafał Miłecki // SPDX-License-Identifier: GPL-2.0-only
200a35880SRafał Miłecki /*
300a35880SRafał Miłecki  * Copyright © 2022 Rafał Miłecki <rafal@milecki.pl>
400a35880SRafał Miłecki  */
500a35880SRafał Miłecki 
600a35880SRafał Miłecki #include <linux/kernel.h>
700a35880SRafał Miłecki #include <linux/module.h>
800a35880SRafał Miłecki #include <linux/mtd/mtd.h>
900a35880SRafał Miłecki #include <linux/mtd/partitions.h>
1000a35880SRafał Miłecki #include <linux/of.h>
1100a35880SRafał Miłecki #include <linux/slab.h>
1200a35880SRafał Miłecki 
1300a35880SRafał Miłecki #define TPLINK_SAFELOADER_DATA_OFFSET		4
1400a35880SRafał Miłecki #define TPLINK_SAFELOADER_MAX_PARTS		32
1500a35880SRafał Miłecki 
1600a35880SRafał Miłecki struct safeloader_cmn_header {
1700a35880SRafał Miłecki 	__be32 size;
1800a35880SRafał Miłecki 	uint32_t unused;
1900a35880SRafał Miłecki } __packed;
2000a35880SRafał Miłecki 
mtd_parser_tplink_safeloader_read_table(struct mtd_info * mtd)2100a35880SRafał Miłecki static void *mtd_parser_tplink_safeloader_read_table(struct mtd_info *mtd)
2200a35880SRafał Miłecki {
2300a35880SRafał Miłecki 	struct safeloader_cmn_header hdr;
2400a35880SRafał Miłecki 	struct device_node *np;
2500a35880SRafał Miłecki 	size_t bytes_read;
2600a35880SRafał Miłecki 	size_t size;
276c0a15a3SDan Carpenter 	u32 offset;
2800a35880SRafał Miłecki 	char *buf;
2900a35880SRafał Miłecki 	int err;
3000a35880SRafał Miłecki 
3100a35880SRafał Miłecki 	np = mtd_get_of_node(mtd);
3200a35880SRafał Miłecki 	if (mtd_is_partition(mtd))
3300a35880SRafał Miłecki 		of_node_get(np);
3400a35880SRafał Miłecki 	else
3500a35880SRafał Miłecki 		np = of_get_child_by_name(np, "partitions");
3600a35880SRafał Miłecki 
376c0a15a3SDan Carpenter 	if (of_property_read_u32(np, "partitions-table-offset", &offset)) {
3800a35880SRafał Miłecki 		pr_err("Failed to get partitions table offset\n");
3900a35880SRafał Miłecki 		goto err_put;
4000a35880SRafał Miłecki 	}
4100a35880SRafał Miłecki 
4200a35880SRafał Miłecki 	err = mtd_read(mtd, offset, sizeof(hdr), &bytes_read, (uint8_t *)&hdr);
4300a35880SRafał Miłecki 	if (err && !mtd_is_bitflip(err)) {
446c0a15a3SDan Carpenter 		pr_err("Failed to read from %s at 0x%x\n", mtd->name, offset);
4500a35880SRafał Miłecki 		goto err_put;
4600a35880SRafał Miłecki 	}
4700a35880SRafał Miłecki 
4800a35880SRafał Miłecki 	size = be32_to_cpu(hdr.size);
4900a35880SRafał Miłecki 
5000a35880SRafał Miłecki 	buf = kmalloc(size + 1, GFP_KERNEL);
5100a35880SRafał Miłecki 	if (!buf)
5200a35880SRafał Miłecki 		goto err_put;
5300a35880SRafał Miłecki 
5400a35880SRafał Miłecki 	err = mtd_read(mtd, offset + sizeof(hdr), size, &bytes_read, buf);
5500a35880SRafał Miłecki 	if (err && !mtd_is_bitflip(err)) {
5600a35880SRafał Miłecki 		pr_err("Failed to read from %s at 0x%zx\n", mtd->name, offset + sizeof(hdr));
5700a35880SRafał Miłecki 		goto err_kfree;
5800a35880SRafał Miłecki 	}
5900a35880SRafał Miłecki 
6000a35880SRafał Miłecki 	buf[size] = '\0';
6100a35880SRafał Miłecki 
6200a35880SRafał Miłecki 	of_node_put(np);
6300a35880SRafał Miłecki 
6400a35880SRafał Miłecki 	return buf;
6500a35880SRafał Miłecki 
6600a35880SRafał Miłecki err_kfree:
6700a35880SRafał Miłecki 	kfree(buf);
6800a35880SRafał Miłecki err_put:
6900a35880SRafał Miłecki 	of_node_put(np);
7000a35880SRafał Miłecki 	return NULL;
7100a35880SRafał Miłecki }
7200a35880SRafał Miłecki 
mtd_parser_tplink_safeloader_parse(struct mtd_info * mtd,const struct mtd_partition ** pparts,struct mtd_part_parser_data * data)7300a35880SRafał Miłecki static int mtd_parser_tplink_safeloader_parse(struct mtd_info *mtd,
7400a35880SRafał Miłecki 					      const struct mtd_partition **pparts,
7500a35880SRafał Miłecki 					      struct mtd_part_parser_data *data)
7600a35880SRafał Miłecki {
7700a35880SRafał Miłecki 	struct mtd_partition *parts;
7800a35880SRafał Miłecki 	char name[65];
7900a35880SRafał Miłecki 	size_t offset;
8000a35880SRafał Miłecki 	size_t bytes;
8100a35880SRafał Miłecki 	char *buf;
8200a35880SRafał Miłecki 	int idx;
8300a35880SRafał Miłecki 	int err;
8400a35880SRafał Miłecki 
8500a35880SRafał Miłecki 	parts = kcalloc(TPLINK_SAFELOADER_MAX_PARTS, sizeof(*parts), GFP_KERNEL);
8600a35880SRafał Miłecki 	if (!parts) {
8700a35880SRafał Miłecki 		err = -ENOMEM;
8800a35880SRafał Miłecki 		goto err_out;
8900a35880SRafał Miłecki 	}
9000a35880SRafał Miłecki 
9100a35880SRafał Miłecki 	buf = mtd_parser_tplink_safeloader_read_table(mtd);
9200a35880SRafał Miłecki 	if (!buf) {
9300a35880SRafał Miłecki 		err = -ENOENT;
94*7adde5acSYuan Can 		goto err_free_parts;
9500a35880SRafał Miłecki 	}
9600a35880SRafał Miłecki 
9700a35880SRafał Miłecki 	for (idx = 0, offset = TPLINK_SAFELOADER_DATA_OFFSET;
9800a35880SRafał Miłecki 	     idx < TPLINK_SAFELOADER_MAX_PARTS &&
9900a35880SRafał Miłecki 	     sscanf(buf + offset, "partition %64s base 0x%llx size 0x%llx%zn\n",
10000a35880SRafał Miłecki 		    name, &parts[idx].offset, &parts[idx].size, &bytes) == 3;
10100a35880SRafał Miłecki 	     idx++, offset += bytes + 1) {
10200a35880SRafał Miłecki 		parts[idx].name = kstrdup(name, GFP_KERNEL);
10300a35880SRafał Miłecki 		if (!parts[idx].name) {
10400a35880SRafał Miłecki 			err = -ENOMEM;
10500a35880SRafał Miłecki 			goto err_free;
10600a35880SRafał Miłecki 		}
10700a35880SRafał Miłecki 	}
10800a35880SRafał Miłecki 
10900a35880SRafał Miłecki 	if (idx == TPLINK_SAFELOADER_MAX_PARTS)
11000a35880SRafał Miłecki 		pr_warn("Reached maximum number of partitions!\n");
11100a35880SRafał Miłecki 
11200a35880SRafał Miłecki 	kfree(buf);
11300a35880SRafał Miłecki 
11400a35880SRafał Miłecki 	*pparts = parts;
11500a35880SRafał Miłecki 
11600a35880SRafał Miłecki 	return idx;
11700a35880SRafał Miłecki 
11800a35880SRafał Miłecki err_free:
11900a35880SRafał Miłecki 	for (idx -= 1; idx >= 0; idx--)
12000a35880SRafał Miłecki 		kfree(parts[idx].name);
121*7adde5acSYuan Can err_free_parts:
122*7adde5acSYuan Can 	kfree(parts);
12300a35880SRafał Miłecki err_out:
12400a35880SRafał Miłecki 	return err;
12500a35880SRafał Miłecki };
12600a35880SRafał Miłecki 
mtd_parser_tplink_safeloader_cleanup(const struct mtd_partition * pparts,int nr_parts)12700a35880SRafał Miłecki static void mtd_parser_tplink_safeloader_cleanup(const struct mtd_partition *pparts,
12800a35880SRafał Miłecki 						 int nr_parts)
12900a35880SRafał Miłecki {
13000a35880SRafał Miłecki 	int i;
13100a35880SRafał Miłecki 
13200a35880SRafał Miłecki 	for (i = 0; i < nr_parts; i++)
13300a35880SRafał Miłecki 		kfree(pparts[i].name);
13400a35880SRafał Miłecki 
13500a35880SRafał Miłecki 	kfree(pparts);
13600a35880SRafał Miłecki }
13700a35880SRafał Miłecki 
13800a35880SRafał Miłecki static const struct of_device_id mtd_parser_tplink_safeloader_of_match_table[] = {
13900a35880SRafał Miłecki 	{ .compatible = "tplink,safeloader-partitions" },
14000a35880SRafał Miłecki 	{},
14100a35880SRafał Miłecki };
14200a35880SRafał Miłecki MODULE_DEVICE_TABLE(of, mtd_parser_tplink_safeloader_of_match_table);
14300a35880SRafał Miłecki 
14400a35880SRafał Miłecki static struct mtd_part_parser mtd_parser_tplink_safeloader = {
14500a35880SRafał Miłecki 	.parse_fn = mtd_parser_tplink_safeloader_parse,
14600a35880SRafał Miłecki 	.cleanup = mtd_parser_tplink_safeloader_cleanup,
14700a35880SRafał Miłecki 	.name = "tplink-safeloader",
14800a35880SRafał Miłecki 	.of_match_table = mtd_parser_tplink_safeloader_of_match_table,
14900a35880SRafał Miłecki };
15000a35880SRafał Miłecki module_mtd_part_parser(mtd_parser_tplink_safeloader);
15100a35880SRafał Miłecki 
15200a35880SRafał Miłecki MODULE_LICENSE("GPL");
153