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