1 // SPDX-License-Identifier: GPL-2.0-or-later 2 /* 3 * drivers/mtd/scpart.c: Sercomm Partition Parser 4 * 5 * Copyright (C) 2018 NOGUCHI Hiroshi 6 * Copyright (C) 2022 Mikhail Zhilkin 7 */ 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_fmt 18 #undef pr_fmt 19 #endif 20 21 #define pr_fmt(fmt) MOD_NAME ": " fmt 22 23 #define ID_ALREADY_FOUND 0xffffffffUL 24 25 #define MAP_OFFS_IN_BLK 0x800 26 #define MAP_MIRROR_NUM 2 27 28 static 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 */ 34 struct sc_part_desc { 35 uint32_t part_id; 36 uint32_t part_offs; 37 uint32_t part_bytes; 38 }; 39 40 static 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 47 static 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 94 free: 95 kfree(buf); 96 97 out: 98 return res; 99 } 100 101 static 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 129 out: 130 if (res > 0) 131 pr_info("Valid 'SC PART MAP' (%d partitions) found at 0x%llx\n", res, offs); 132 else 133 pr_info("No valid 'SC PART MAP' was found\n"); 134 135 return res; 136 } 137 138 static 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 } else 217 pr_info("No partition in OF matches partition ID with 'SC PART MAP'.\n"); 218 219 of_node_put(pp); 220 221 free: 222 of_node_put(ofpart_node); 223 kfree(scpart_map); 224 if (res <= 0) 225 kfree(parts); 226 227 out: 228 return res; 229 } 230 231 static const struct of_device_id scpart_parser_of_match_table[] = { 232 { .compatible = "sercomm,sc-partitions" }, 233 {}, 234 }; 235 MODULE_DEVICE_TABLE(of, scpart_parser_of_match_table); 236 237 static struct mtd_part_parser scpart_parser = { 238 .parse_fn = scpart_parse, 239 .name = "scpart", 240 .of_match_table = scpart_parser_of_match_table, 241 }; 242 module_mtd_part_parser(scpart_parser); 243 244 /* mtd parsers will request the module by parser name */ 245 MODULE_ALIAS("scpart"); 246 MODULE_LICENSE("GPL"); 247 MODULE_AUTHOR("NOGUCHI Hiroshi <drvlabo@gmail.com>"); 248 MODULE_AUTHOR("Mikhail Zhilkin <csharper2005@gmail.com>"); 249 MODULE_DESCRIPTION("Sercomm partition parser"); 250