| // SPDX-License-Identifier: GPL-2.0 |
| /* |
| * Copyright (C) ASR Microelectronics 2023 |
| */ |
| |
| #include <linux/module.h> |
| #include <linux/init.h> |
| #include <linux/of.h> |
| #include <linux/mtd/mtd.h> |
| #include <linux/slab.h> |
| #include <linux/mtd/partitions.h> |
| |
| #include "../mtdsplit/mtdsplit.h" |
| |
| #define ASR_SDTIM_PARTS 1 |
| #define ASR_TIMH_OFFSET 0x4 |
| #define ASR_SDTIM_OFFSET 0x38 |
| #define ASR_IMGID_OFFSET 0x98 |
| #define ASR_TIMH_MAGIC 0x54494D48 // TIMH |
| #define ASR_SDTIM_MAGIC 0x5354494D // STIM |
| |
| static int check_asr_fs_with_sdtim(struct mtd_info *master, |
| const struct mtd_partition *part) |
| { |
| void *buf; |
| size_t retlen; |
| int err; |
| u32 magic_tim, magic_sdtim; |
| |
| #ifdef CONFIG_ASR_SDTIM |
| /* |
| * For filesystem partition with sdtim header, force to create |
| * "-sdtim" and "-mount" to make sure A/B fs mount succeed even |
| * one of sdtim header and fs area destroied, by adding property |
| * "sdtim-fs" to correspond partition node. |
| */ |
| if (of_get_property(part->of_node, "sdtim-fs", NULL)) |
| return ASR_SDTIM_PARTS; |
| #endif |
| |
| buf = kzalloc(master->writesize, GFP_KERNEL); |
| if (!buf) |
| return -ENOMEM; |
| |
| err = mtd_read(master, part->offset, master->writesize, &retlen, buf); |
| if (err && err != -EUCLEAN) |
| goto free_buf; |
| |
| if (retlen != master->writesize) { |
| err = -EIO; |
| goto free_buf; |
| } |
| |
| magic_tim = *((u32 *)(buf + ASR_TIMH_OFFSET)); |
| magic_sdtim = *((u32 *)(buf + ASR_SDTIM_OFFSET)); |
| if (magic_tim != ASR_TIMH_MAGIC || magic_sdtim != ASR_SDTIM_MAGIC) { |
| err = 0; |
| goto free_buf; |
| } |
| |
| if (part->size <= master->erasesize) { |
| err = 0; |
| goto free_buf; |
| } |
| |
| /* check if this part may exist filesystem, read from next block */ |
| err = mtd_check_rootfs_magic(master, part->offset + master->erasesize, NULL); |
| if (err) { |
| err = 0; |
| goto free_buf; |
| } |
| |
| kfree(buf); |
| return ASR_SDTIM_PARTS; |
| |
| free_buf: |
| kfree(buf); |
| return err; |
| } |
| |
| int asr_sdtim_partitions_post_parse(struct mtd_info *mtd, |
| struct mtd_partition **pparts, int nr_parts) |
| { |
| struct mtd_partition *parts = *pparts; |
| struct mtd_partition *nparts; |
| int new_nr = 0; |
| int i; |
| |
| nparts = kcalloc(nr_parts * 2 + 1, sizeof(*parts), GFP_KERNEL); |
| if (!nparts) |
| return -ENOMEM; |
| |
| for (i = 0; i < nr_parts; i++) { |
| #ifdef CONFIG_AB_SYSTEM |
| /* add "-sdtim" and "-mount" for sdtim img with filesystem */ |
| if (check_asr_fs_with_sdtim(mtd, parts + i) > 0) { |
| /* add -sdtim partition */ |
| nparts[new_nr].name = kstrdup(parts[i].name, GFP_KERNEL); |
| strcat((char *)nparts[new_nr].name, "-sdtim"); |
| nparts[new_nr].offset = parts[i].offset; |
| nparts[new_nr].size = mtd->erasesize; |
| nparts[new_nr].mask_flags = parts[i].mask_flags; |
| nparts[new_nr].types = parts[i].types; |
| nparts[new_nr].of_node = parts[i].of_node; |
| new_nr++; |
| |
| /* add -mount partition */ |
| nparts[new_nr].name = kmemdup(parts[i].name, 128, GFP_KERNEL); |
| strcat((char *)nparts[new_nr].name, "-mount"); |
| nparts[new_nr].offset = parts[i].offset + mtd->erasesize; |
| nparts[new_nr].size = parts[i].size - mtd->erasesize; |
| nparts[new_nr].mask_flags = parts[i].mask_flags; |
| nparts[new_nr].types = parts[i].types; |
| nparts[new_nr].of_node = parts[i].of_node; |
| new_nr++; |
| } |
| |
| /* copy original partition */ |
| nparts[new_nr].name = kstrdup(parts[i].name, GFP_KERNEL); |
| nparts[new_nr].offset = parts[i].offset; |
| nparts[new_nr].size = parts[i].size; |
| nparts[new_nr].mask_flags = parts[i].mask_flags; |
| nparts[new_nr].types = parts[i].types; |
| nparts[new_nr].of_node = parts[i].of_node; |
| new_nr++; |
| #else |
| if (check_asr_fs_with_sdtim(mtd, parts + i) > 0) { |
| /* add -sdtim partition */ |
| nparts[new_nr].name = kstrdup(parts[i].name, GFP_KERNEL); |
| strcat((char *)nparts[new_nr].name, "-sdtim"); |
| nparts[new_nr].offset = parts[i].offset; |
| nparts[new_nr].size = mtd->erasesize; |
| nparts[new_nr].mask_flags = parts[i].mask_flags; |
| nparts[new_nr].types = parts[i].types; |
| nparts[new_nr].of_node = parts[i].of_node; |
| new_nr++; |
| |
| nparts[new_nr].name = kstrdup(parts[i].name, GFP_KERNEL); |
| nparts[new_nr].offset = parts[i].offset + mtd->erasesize; |
| nparts[new_nr].size = parts[i].size - mtd->erasesize; |
| nparts[new_nr].mask_flags = parts[i].mask_flags; |
| nparts[new_nr].types = parts[i].types; |
| nparts[new_nr].of_node = parts[i].of_node; |
| new_nr++; |
| } else { |
| /* copy original partition */ |
| nparts[new_nr].name = kstrdup(parts[i].name, GFP_KERNEL); |
| nparts[new_nr].offset = parts[i].offset; |
| nparts[new_nr].size = parts[i].size; |
| nparts[new_nr].mask_flags = parts[i].mask_flags; |
| nparts[new_nr].types = parts[i].types; |
| nparts[new_nr].of_node = parts[i].of_node; |
| new_nr++; |
| } |
| #endif |
| } |
| |
| kfree(parts); |
| *pparts = nparts; |
| return new_nr; |
| } |