Back to home page

OSCL-LXR

 
 

    


0001 // SPDX-License-Identifier: GPL-2.0-or-later
0002 /*
0003  *    drivers/mtd/scpart.c: Sercomm Partition Parser
0004  *
0005  *    Copyright (C) 2018 NOGUCHI Hiroshi
0006  *    Copyright (C) 2022 Mikhail Zhilkin
0007  */
0008 
0009 #include <linux/kernel.h>
0010 #include <linux/slab.h>
0011 #include <linux/mtd/mtd.h>
0012 #include <linux/mtd/partitions.h>
0013 #include <linux/module.h>
0014 
0015 #define MOD_NAME    "scpart"
0016 
0017 #ifdef pr_fmt
0018 #undef pr_fmt
0019 #endif
0020 
0021 #define pr_fmt(fmt) MOD_NAME ": " fmt
0022 
0023 #define ID_ALREADY_FOUND    0xffffffffUL
0024 
0025 #define MAP_OFFS_IN_BLK     0x800
0026 #define MAP_MIRROR_NUM      2
0027 
0028 static const char sc_part_magic[] = {
0029     'S', 'C', 'F', 'L', 'M', 'A', 'P', 'O', 'K', '\0',
0030 };
0031 #define PART_MAGIC_LEN      sizeof(sc_part_magic)
0032 
0033 /* assumes that all fields are set by CPU native endian */
0034 struct sc_part_desc {
0035     uint32_t    part_id;
0036     uint32_t    part_offs;
0037     uint32_t    part_bytes;
0038 };
0039 
0040 static uint32_t scpart_desc_is_valid(struct sc_part_desc *pdesc)
0041 {
0042     return ((pdesc->part_id != 0xffffffffUL) &&
0043         (pdesc->part_offs != 0xffffffffUL) &&
0044         (pdesc->part_bytes != 0xffffffffUL));
0045 }
0046 
0047 static int scpart_scan_partmap(struct mtd_info *master, loff_t partmap_offs,
0048                    struct sc_part_desc **ppdesc)
0049 {
0050     int cnt = 0;
0051     int res = 0;
0052     int res2;
0053     loff_t offs;
0054     size_t retlen;
0055     struct sc_part_desc *pdesc = NULL;
0056     struct sc_part_desc *tmpdesc;
0057     uint8_t *buf;
0058 
0059     buf = kzalloc(master->erasesize, GFP_KERNEL);
0060     if (!buf) {
0061         res = -ENOMEM;
0062         goto out;
0063     }
0064 
0065     res2 = mtd_read(master, partmap_offs, master->erasesize, &retlen, buf);
0066     if (res2 || retlen != master->erasesize) {
0067         res = -EIO;
0068         goto free;
0069     }
0070 
0071     for (offs = MAP_OFFS_IN_BLK;
0072          offs < master->erasesize - sizeof(*tmpdesc);
0073          offs += sizeof(*tmpdesc)) {
0074         tmpdesc = (struct sc_part_desc *)&buf[offs];
0075         if (!scpart_desc_is_valid(tmpdesc))
0076             break;
0077         cnt++;
0078     }
0079 
0080     if (cnt > 0) {
0081         int bytes = cnt * sizeof(*pdesc);
0082 
0083         pdesc = kcalloc(cnt, sizeof(*pdesc), GFP_KERNEL);
0084         if (!pdesc) {
0085             res = -ENOMEM;
0086             goto free;
0087         }
0088         memcpy(pdesc, &(buf[MAP_OFFS_IN_BLK]), bytes);
0089 
0090         *ppdesc = pdesc;
0091         res = cnt;
0092     }
0093 
0094 free:
0095     kfree(buf);
0096 
0097 out:
0098     return res;
0099 }
0100 
0101 static int scpart_find_partmap(struct mtd_info *master,
0102                    struct sc_part_desc **ppdesc)
0103 {
0104     int magic_found = 0;
0105     int res = 0;
0106     int res2;
0107     loff_t offs = 0;
0108     size_t retlen;
0109     uint8_t rdbuf[PART_MAGIC_LEN];
0110 
0111     while ((magic_found < MAP_MIRROR_NUM) &&
0112             (offs < master->size) &&
0113              !mtd_block_isbad(master, offs)) {
0114         res2 = mtd_read(master, offs, PART_MAGIC_LEN, &retlen, rdbuf);
0115         if (res2 || retlen != PART_MAGIC_LEN) {
0116             res = -EIO;
0117             goto out;
0118         }
0119         if (!memcmp(rdbuf, sc_part_magic, PART_MAGIC_LEN)) {
0120             pr_debug("Signature found at 0x%llx\n", offs);
0121             magic_found++;
0122             res = scpart_scan_partmap(master, offs, ppdesc);
0123             if (res > 0)
0124                 goto out;
0125         }
0126         offs += master->erasesize;
0127     }
0128 
0129 out:
0130     if (res > 0)
0131         pr_info("Valid 'SC PART MAP' (%d partitions) found at 0x%llx\n", res, offs);
0132     else
0133         pr_info("No valid 'SC PART MAP' was found\n");
0134 
0135     return res;
0136 }
0137 
0138 static int scpart_parse(struct mtd_info *master,
0139             const struct mtd_partition **pparts,
0140             struct mtd_part_parser_data *data)
0141 {
0142     const char *partname;
0143     int n;
0144     int nr_scparts;
0145     int nr_parts = 0;
0146     int res = 0;
0147     struct sc_part_desc *scpart_map = NULL;
0148     struct mtd_partition *parts = NULL;
0149     struct device_node *mtd_node;
0150     struct device_node *ofpart_node;
0151     struct device_node *pp;
0152 
0153     mtd_node = mtd_get_of_node(master);
0154     if (!mtd_node) {
0155         res = -ENOENT;
0156         goto out;
0157     }
0158 
0159     ofpart_node = of_get_child_by_name(mtd_node, "partitions");
0160     if (!ofpart_node) {
0161         pr_info("%s: 'partitions' subnode not found on %pOF.\n",
0162                 master->name, mtd_node);
0163         res = -ENOENT;
0164         goto out;
0165     }
0166 
0167     nr_scparts = scpart_find_partmap(master, &scpart_map);
0168     if (nr_scparts <= 0) {
0169         pr_info("No any partitions was found in 'SC PART MAP'.\n");
0170         res = -ENOENT;
0171         goto free;
0172     }
0173 
0174     parts = kcalloc(of_get_child_count(ofpart_node), sizeof(*parts),
0175         GFP_KERNEL);
0176     if (!parts) {
0177         res = -ENOMEM;
0178         goto free;
0179     }
0180 
0181     for_each_child_of_node(ofpart_node, pp) {
0182         u32 scpart_id;
0183 
0184         if (of_property_read_u32(pp, "sercomm,scpart-id", &scpart_id))
0185             continue;
0186 
0187         for (n = 0 ; n < nr_scparts ; n++)
0188             if ((scpart_map[n].part_id != ID_ALREADY_FOUND) &&
0189                     (scpart_id == scpart_map[n].part_id))
0190                 break;
0191         if (n >= nr_scparts)
0192             /* not match */
0193             continue;
0194 
0195         /* add the partition found in OF into MTD partition array */
0196         parts[nr_parts].offset = scpart_map[n].part_offs;
0197         parts[nr_parts].size = scpart_map[n].part_bytes;
0198         parts[nr_parts].of_node = pp;
0199 
0200         if (!of_property_read_string(pp, "label", &partname))
0201             parts[nr_parts].name = partname;
0202         if (of_property_read_bool(pp, "read-only"))
0203             parts[nr_parts].mask_flags |= MTD_WRITEABLE;
0204         if (of_property_read_bool(pp, "lock"))
0205             parts[nr_parts].mask_flags |= MTD_POWERUP_LOCK;
0206 
0207         /* mark as 'done' */
0208         scpart_map[n].part_id = ID_ALREADY_FOUND;
0209 
0210         nr_parts++;
0211     }
0212 
0213     if (nr_parts > 0) {
0214         *pparts = parts;
0215         res = nr_parts;
0216     } else
0217         pr_info("No partition in OF matches partition ID with 'SC PART MAP'.\n");
0218 
0219     of_node_put(pp);
0220 
0221 free:
0222     of_node_put(ofpart_node);
0223     kfree(scpart_map);
0224     if (res <= 0)
0225         kfree(parts);
0226 
0227 out:
0228     return res;
0229 }
0230 
0231 static const struct of_device_id scpart_parser_of_match_table[] = {
0232     { .compatible = "sercomm,sc-partitions" },
0233     {},
0234 };
0235 MODULE_DEVICE_TABLE(of, scpart_parser_of_match_table);
0236 
0237 static struct mtd_part_parser scpart_parser = {
0238     .parse_fn = scpart_parse,
0239     .name = "scpart",
0240     .of_match_table = scpart_parser_of_match_table,
0241 };
0242 module_mtd_part_parser(scpart_parser);
0243 
0244 /* mtd parsers will request the module by parser name */
0245 MODULE_ALIAS("scpart");
0246 MODULE_LICENSE("GPL");
0247 MODULE_AUTHOR("NOGUCHI Hiroshi <drvlabo@gmail.com>");
0248 MODULE_AUTHOR("Mikhail Zhilkin <csharper2005@gmail.com>");
0249 MODULE_DESCRIPTION("Sercomm partition parser");