brintos

brintos / linux-shallow public Read only

0
0
Text · 5.5 KiB · 6e5e11c Raw
250 lines · c
1// SPDX-License-Identifier: GPL-2.0-or-later2/*3 *    drivers/mtd/scpart.c: Sercomm Partition Parser4 *5 *    Copyright (C) 2018 NOGUCHI Hiroshi6 *    Copyright (C) 2022 Mikhail Zhilkin7 */8 9#include <linux/kernel.h>10#include <linux/slab.h>11#include <linux/mtd/mtd.h>12#include <linux/mtd/partitions.h>13#include <linux/module.h>14 15#define	MOD_NAME	"scpart"16 17#ifdef pr_fmt18#undef pr_fmt19#endif20 21#define pr_fmt(fmt) MOD_NAME ": " fmt22 23#define	ID_ALREADY_FOUND	0xffffffffUL24 25#define	MAP_OFFS_IN_BLK		0x80026#define	MAP_MIRROR_NUM		227 28static const char sc_part_magic[] = {29	'S', 'C', 'F', 'L', 'M', 'A', 'P', 'O', 'K', '\0',30};31#define	PART_MAGIC_LEN		sizeof(sc_part_magic)32 33/* assumes that all fields are set by CPU native endian */34struct sc_part_desc {35	uint32_t	part_id;36	uint32_t	part_offs;37	uint32_t	part_bytes;38};39 40static uint32_t scpart_desc_is_valid(struct sc_part_desc *pdesc)41{42	return ((pdesc->part_id != 0xffffffffUL) &&43		(pdesc->part_offs != 0xffffffffUL) &&44		(pdesc->part_bytes != 0xffffffffUL));45}46 47static int scpart_scan_partmap(struct mtd_info *master, loff_t partmap_offs,48			       struct sc_part_desc **ppdesc)49{50	int cnt = 0;51	int res = 0;52	int res2;53	uint32_t offs;54	size_t retlen;55	struct sc_part_desc *pdesc = NULL;56	struct sc_part_desc *tmpdesc;57	uint8_t *buf;58 59	buf = kzalloc(master->erasesize, GFP_KERNEL);60	if (!buf) {61		res = -ENOMEM;62		goto out;63	}64 65	res2 = mtd_read(master, partmap_offs, master->erasesize, &retlen, buf);66	if (res2 || retlen != master->erasesize) {67		res = -EIO;68		goto free;69	}70 71	for (offs = MAP_OFFS_IN_BLK;72	     offs < master->erasesize - sizeof(*tmpdesc);73	     offs += sizeof(*tmpdesc)) {74		tmpdesc = (struct sc_part_desc *)&buf[offs];75		if (!scpart_desc_is_valid(tmpdesc))76			break;77		cnt++;78	}79 80	if (cnt > 0) {81		int bytes = cnt * sizeof(*pdesc);82 83		pdesc = kcalloc(cnt, sizeof(*pdesc), GFP_KERNEL);84		if (!pdesc) {85			res = -ENOMEM;86			goto free;87		}88		memcpy(pdesc, &(buf[MAP_OFFS_IN_BLK]), bytes);89 90		*ppdesc = pdesc;91		res = cnt;92	}93 94free:95	kfree(buf);96 97out:98	return res;99}100 101static int scpart_find_partmap(struct mtd_info *master,102			       struct sc_part_desc **ppdesc)103{104	int magic_found = 0;105	int res = 0;106	int res2;107	loff_t offs = 0;108	size_t retlen;109	uint8_t rdbuf[PART_MAGIC_LEN];110 111	while ((magic_found < MAP_MIRROR_NUM) &&112			(offs < master->size) &&113			 !mtd_block_isbad(master, offs)) {114		res2 = mtd_read(master, offs, PART_MAGIC_LEN, &retlen, rdbuf);115		if (res2 || retlen != PART_MAGIC_LEN) {116			res = -EIO;117			goto out;118		}119		if (!memcmp(rdbuf, sc_part_magic, PART_MAGIC_LEN)) {120			pr_debug("Signature found at 0x%llx\n", offs);121			magic_found++;122			res = scpart_scan_partmap(master, offs, ppdesc);123			if (res > 0)124				goto out;125		}126		offs += master->erasesize;127	}128 129out:130	if (res > 0)131		pr_info("Valid 'SC PART MAP' (%d partitions) found at 0x%llx\n", res, offs);132	else133		pr_info("No valid 'SC PART MAP' was found\n");134 135	return res;136}137 138static int scpart_parse(struct mtd_info *master,139			const struct mtd_partition **pparts,140			struct mtd_part_parser_data *data)141{142	const char *partname;143	int n;144	int nr_scparts;145	int nr_parts = 0;146	int res = 0;147	struct sc_part_desc *scpart_map = NULL;148	struct mtd_partition *parts = NULL;149	struct device_node *mtd_node;150	struct device_node *ofpart_node;151	struct device_node *pp;152 153	mtd_node = mtd_get_of_node(master);154	if (!mtd_node) {155		res = -ENOENT;156		goto out;157	}158 159	ofpart_node = of_get_child_by_name(mtd_node, "partitions");160	if (!ofpart_node) {161		pr_info("%s: 'partitions' subnode not found on %pOF.\n",162				master->name, mtd_node);163		res = -ENOENT;164		goto out;165	}166 167	nr_scparts = scpart_find_partmap(master, &scpart_map);168	if (nr_scparts <= 0) {169		pr_info("No any partitions was found in 'SC PART MAP'.\n");170		res = -ENOENT;171		goto free;172	}173 174	parts = kcalloc(of_get_child_count(ofpart_node), sizeof(*parts),175		GFP_KERNEL);176	if (!parts) {177		res = -ENOMEM;178		goto free;179	}180 181	for_each_child_of_node(ofpart_node, pp) {182		u32 scpart_id;183 184		if (of_property_read_u32(pp, "sercomm,scpart-id", &scpart_id))185			continue;186 187		for (n = 0 ; n < nr_scparts ; n++)188			if ((scpart_map[n].part_id != ID_ALREADY_FOUND) &&189					(scpart_id == scpart_map[n].part_id))190				break;191		if (n >= nr_scparts)192			/* not match */193			continue;194 195		/* add the partition found in OF into MTD partition array */196		parts[nr_parts].offset = scpart_map[n].part_offs;197		parts[nr_parts].size = scpart_map[n].part_bytes;198		parts[nr_parts].of_node = pp;199 200		if (!of_property_read_string(pp, "label", &partname))201			parts[nr_parts].name = partname;202		if (of_property_read_bool(pp, "read-only"))203			parts[nr_parts].mask_flags |= MTD_WRITEABLE;204		if (of_property_read_bool(pp, "lock"))205			parts[nr_parts].mask_flags |= MTD_POWERUP_LOCK;206 207		/* mark as 'done' */208		scpart_map[n].part_id = ID_ALREADY_FOUND;209 210		nr_parts++;211	}212 213	if (nr_parts > 0) {214		*pparts = parts;215		res = nr_parts;216	} else217		pr_info("No partition in OF matches partition ID with 'SC PART MAP'.\n");218 219	of_node_put(pp);220 221free:222	of_node_put(ofpart_node);223	kfree(scpart_map);224	if (res <= 0)225		kfree(parts);226 227out:228	return res;229}230 231static const struct of_device_id scpart_parser_of_match_table[] = {232	{ .compatible = "sercomm,sc-partitions" },233	{},234};235MODULE_DEVICE_TABLE(of, scpart_parser_of_match_table);236 237static struct mtd_part_parser scpart_parser = {238	.parse_fn = scpart_parse,239	.name = "scpart",240	.of_match_table = scpart_parser_of_match_table,241};242module_mtd_part_parser(scpart_parser);243 244/* mtd parsers will request the module by parser name */245MODULE_ALIAS("scpart");246MODULE_LICENSE("GPL");247MODULE_AUTHOR("NOGUCHI Hiroshi <drvlabo@gmail.com>");248MODULE_AUTHOR("Mikhail Zhilkin <csharper2005@gmail.com>");249MODULE_DESCRIPTION("Sercomm partition parser");250