linux/drivers/mtd/parsers/qcomsmempart.c
<<
>>
Prefs
   1// SPDX-License-Identifier: GPL-2.0-only
   2/*
   3 * Qualcomm SMEM NAND flash partition parser
   4 *
   5 * Copyright (C) 2020, Linaro Ltd.
   6 */
   7
   8#include <linux/ctype.h>
   9#include <linux/module.h>
  10#include <linux/mtd/mtd.h>
  11#include <linux/mtd/partitions.h>
  12#include <linux/slab.h>
  13#include <linux/soc/qcom/smem.h>
  14
  15#define SMEM_AARM_PARTITION_TABLE       9
  16#define SMEM_APPS                       0
  17
  18#define SMEM_FLASH_PART_MAGIC1          0x55ee73aa
  19#define SMEM_FLASH_PART_MAGIC2          0xe35ebddb
  20#define SMEM_FLASH_PTABLE_V3            3
  21#define SMEM_FLASH_PTABLE_V4            4
  22#define SMEM_FLASH_PTABLE_MAX_PARTS_V3  16
  23#define SMEM_FLASH_PTABLE_MAX_PARTS_V4  48
  24#define SMEM_FLASH_PTABLE_HDR_LEN       (4 * sizeof(u32))
  25#define SMEM_FLASH_PTABLE_NAME_SIZE     16
  26
  27/**
  28 * struct smem_flash_pentry - SMEM Flash partition entry
  29 * @name: Name of the partition
  30 * @offset: Offset in blocks
  31 * @length: Length of the partition in blocks
  32 * @attr: Flags for this partition
  33 */
  34struct smem_flash_pentry {
  35        char name[SMEM_FLASH_PTABLE_NAME_SIZE];
  36        __le32 offset;
  37        __le32 length;
  38        u8 attr;
  39} __packed __aligned(4);
  40
  41/**
  42 * struct smem_flash_ptable - SMEM Flash partition table
  43 * @magic1: Partition table Magic 1
  44 * @magic2: Partition table Magic 2
  45 * @version: Partition table version
  46 * @numparts: Number of partitions in this ptable
  47 * @pentry: Flash partition entries belonging to this ptable
  48 */
  49struct smem_flash_ptable {
  50        __le32 magic1;
  51        __le32 magic2;
  52        __le32 version;
  53        __le32 numparts;
  54        struct smem_flash_pentry pentry[SMEM_FLASH_PTABLE_MAX_PARTS_V4];
  55} __packed __aligned(4);
  56
  57static int parse_qcomsmem_part(struct mtd_info *mtd,
  58                               const struct mtd_partition **pparts,
  59                               struct mtd_part_parser_data *data)
  60{
  61        struct smem_flash_pentry *pentry;
  62        struct smem_flash_ptable *ptable;
  63        size_t len = SMEM_FLASH_PTABLE_HDR_LEN;
  64        struct mtd_partition *parts;
  65        int ret, i, numparts;
  66        char *name, *c;
  67
  68        if (IS_ENABLED(CONFIG_MTD_SPI_NOR_USE_4K_SECTORS)
  69                        && mtd->type == MTD_NORFLASH) {
  70                pr_err("%s: SMEM partition parser is incompatible with 4K sectors\n",
  71                                mtd->name);
  72                return -EINVAL;
  73        }
  74
  75        pr_debug("Parsing partition table info from SMEM\n");
  76        ptable = qcom_smem_get(SMEM_APPS, SMEM_AARM_PARTITION_TABLE, &len);
  77        if (IS_ERR(ptable)) {
  78                pr_err("Error reading partition table header\n");
  79                return PTR_ERR(ptable);
  80        }
  81
  82        /* Verify ptable magic */
  83        if (le32_to_cpu(ptable->magic1) != SMEM_FLASH_PART_MAGIC1 ||
  84            le32_to_cpu(ptable->magic2) != SMEM_FLASH_PART_MAGIC2) {
  85                pr_err("Partition table magic verification failed\n");
  86                return -EINVAL;
  87        }
  88
  89        /* Ensure that # of partitions is less than the max we have allocated */
  90        numparts = le32_to_cpu(ptable->numparts);
  91        if (numparts > SMEM_FLASH_PTABLE_MAX_PARTS_V4) {
  92                pr_err("Partition numbers exceed the max limit\n");
  93                return -EINVAL;
  94        }
  95
  96        /* Find out length of partition data based on table version */
  97        if (le32_to_cpu(ptable->version) <= SMEM_FLASH_PTABLE_V3) {
  98                len = SMEM_FLASH_PTABLE_HDR_LEN + SMEM_FLASH_PTABLE_MAX_PARTS_V3 *
  99                        sizeof(struct smem_flash_pentry);
 100        } else if (le32_to_cpu(ptable->version) == SMEM_FLASH_PTABLE_V4) {
 101                len = SMEM_FLASH_PTABLE_HDR_LEN + SMEM_FLASH_PTABLE_MAX_PARTS_V4 *
 102                        sizeof(struct smem_flash_pentry);
 103        } else {
 104                pr_err("Unknown ptable version (%d)", le32_to_cpu(ptable->version));
 105                return -EINVAL;
 106        }
 107
 108        /*
 109         * Now that the partition table header has been parsed, verified
 110         * and the length of the partition table calculated, read the
 111         * complete partition table
 112         */
 113        ptable = qcom_smem_get(SMEM_APPS, SMEM_AARM_PARTITION_TABLE, &len);
 114        if (IS_ERR(ptable)) {
 115                pr_err("Error reading partition table\n");
 116                return PTR_ERR(ptable);
 117        }
 118
 119        parts = kcalloc(numparts, sizeof(*parts), GFP_KERNEL);
 120        if (!parts)
 121                return -ENOMEM;
 122
 123        for (i = 0; i < numparts; i++) {
 124                pentry = &ptable->pentry[i];
 125                if (pentry->name[0] == '\0')
 126                        continue;
 127
 128                name = kstrdup(pentry->name, GFP_KERNEL);
 129                if (!name) {
 130                        ret = -ENOMEM;
 131                        goto out_free_parts;
 132                }
 133
 134                /* Convert name to lower case */
 135                for (c = name; *c != '\0'; c++)
 136                        *c = tolower(*c);
 137
 138                parts[i].name = name;
 139                parts[i].offset = le32_to_cpu(pentry->offset) * mtd->erasesize;
 140                parts[i].mask_flags = pentry->attr;
 141                parts[i].size = le32_to_cpu(pentry->length) * mtd->erasesize;
 142                pr_debug("%d: %s offs=0x%08x size=0x%08x attr:0x%08x\n",
 143                         i, pentry->name, le32_to_cpu(pentry->offset),
 144                         le32_to_cpu(pentry->length), pentry->attr);
 145        }
 146
 147        pr_debug("SMEM partition table found: ver: %d len: %d\n",
 148                 le32_to_cpu(ptable->version), numparts);
 149        *pparts = parts;
 150
 151        return numparts;
 152
 153out_free_parts:
 154        while (--i >= 0)
 155                kfree(parts[i].name);
 156        kfree(parts);
 157        *pparts = NULL;
 158
 159        return ret;
 160}
 161
 162static void parse_qcomsmem_cleanup(const struct mtd_partition *pparts,
 163                                   int nr_parts)
 164{
 165        int i;
 166
 167        for (i = 0; i < nr_parts; i++)
 168                kfree(pparts[i].name);
 169}
 170
 171static const struct of_device_id qcomsmem_of_match_table[] = {
 172        { .compatible = "qcom,smem-part" },
 173        {},
 174};
 175MODULE_DEVICE_TABLE(of, qcomsmem_of_match_table);
 176
 177static struct mtd_part_parser mtd_parser_qcomsmem = {
 178        .parse_fn = parse_qcomsmem_part,
 179        .cleanup = parse_qcomsmem_cleanup,
 180        .name = "qcomsmem",
 181        .of_match_table = qcomsmem_of_match_table,
 182};
 183module_mtd_part_parser(mtd_parser_qcomsmem);
 184
 185MODULE_LICENSE("GPL v2");
 186MODULE_AUTHOR("Manivannan Sadhasivam <manivannan.sadhasivam@linaro.org>");
 187MODULE_DESCRIPTION("Qualcomm SMEM NAND flash partition parser");
 188