|
@@ -0,0 +1,229 @@
|
|
|
+From aec4d5f5ffd0f0092bd9dc21ea90e0bc237d4b74 Mon Sep 17 00:00:00 2001
|
|
|
+From: =?UTF-8?q?Rafa=C5=82=20Mi=C5=82ecki?= <[email protected]>
|
|
|
+Date: Sat, 15 Oct 2022 11:29:50 +0200
|
|
|
+Subject: [PATCH] mtd: parsers: add TP-Link SafeLoader partitions table parser
|
|
|
+MIME-Version: 1.0
|
|
|
+Content-Type: text/plain; charset=UTF-8
|
|
|
+Content-Transfer-Encoding: 8bit
|
|
|
+
|
|
|
+This parser deals with most TP-Link home routers. It reads info about
|
|
|
+partitions and registers them in the MTD subsystem.
|
|
|
+
|
|
|
+Example from TP-Link Archer C5 V2:
|
|
|
+
|
|
|
+spi-nor spi0.0: s25fl128s1 (16384 Kbytes)
|
|
|
+15 tplink-safeloader partitions found on MTD device spi0.0
|
|
|
+Creating 15 MTD partitions on "spi0.0":
|
|
|
+0x000000000000-0x000000040000 : "fs-uboot"
|
|
|
+0x000000040000-0x000000440000 : "os-image"
|
|
|
+0x000000440000-0x000000e40000 : "rootfs"
|
|
|
+0x000000e40000-0x000000e40200 : "default-mac"
|
|
|
+0x000000e40200-0x000000e40400 : "pin"
|
|
|
+0x000000e40400-0x000000e40600 : "product-info"
|
|
|
+0x000000e50000-0x000000e60000 : "partition-table"
|
|
|
+0x000000e60000-0x000000e60200 : "soft-version"
|
|
|
+0x000000e61000-0x000000e70000 : "support-list"
|
|
|
+0x000000e70000-0x000000e80000 : "profile"
|
|
|
+0x000000e80000-0x000000e90000 : "default-config"
|
|
|
+0x000000e90000-0x000000ee0000 : "user-config"
|
|
|
+0x000000ee0000-0x000000fe0000 : "log"
|
|
|
+0x000000fe0000-0x000000ff0000 : "radio_bk"
|
|
|
+0x000000ff0000-0x000001000000 : "radio"
|
|
|
+
|
|
|
+Signed-off-by: Rafał Miłecki <[email protected]>
|
|
|
+Signed-off-by: Miquel Raynal <[email protected]>
|
|
|
+Link: https://lore.kernel.org/linux-mtd/[email protected]
|
|
|
+---
|
|
|
+ drivers/mtd/parsers/Kconfig | 15 +++
|
|
|
+ drivers/mtd/parsers/Makefile | 1 +
|
|
|
+ drivers/mtd/parsers/tplink_safeloader.c | 150 ++++++++++++++++++++++++
|
|
|
+ 3 files changed, 166 insertions(+)
|
|
|
+ create mode 100644 drivers/mtd/parsers/tplink_safeloader.c
|
|
|
+
|
|
|
+--- a/drivers/mtd/parsers/Kconfig
|
|
|
++++ b/drivers/mtd/parsers/Kconfig
|
|
|
+@@ -113,6 +113,21 @@ config MTD_AFS_PARTS
|
|
|
+ for your particular device. It won't happen automatically. The
|
|
|
+ 'physmap' map driver (CONFIG_MTD_PHYSMAP) does this, for example.
|
|
|
+
|
|
|
++config MTD_PARSER_TPLINK_SAFELOADER
|
|
|
++ tristate "TP-Link Safeloader partitions parser"
|
|
|
++ depends on MTD && (ARCH_BCM_5301X || ATH79 || SOC_MT7620 || SOC_MT7621 || COMPILE_TEST)
|
|
|
++ help
|
|
|
++ TP-Link home routers use flash partitions to store various data. Info
|
|
|
++ about flash space layout is stored in a partitions table using a
|
|
|
++ custom ASCII-based format.
|
|
|
++
|
|
|
++ That format was first found in devices with SafeLoader bootloader and
|
|
|
++ was named after it. Later it was adapted to CFE and U-Boot
|
|
|
++ bootloaders.
|
|
|
++
|
|
|
++ This driver reads partitions table, parses it and creates MTD
|
|
|
++ partitions.
|
|
|
++
|
|
|
+ config MTD_PARSER_TRX
|
|
|
+ tristate "Parser for TRX format partitions"
|
|
|
+ depends on MTD && (BCM47XX || ARCH_BCM_5301X || ARCH_MEDIATEK || RALINK || COMPILE_TEST)
|
|
|
+--- a/drivers/mtd/parsers/Makefile
|
|
|
++++ b/drivers/mtd/parsers/Makefile
|
|
|
+@@ -9,6 +9,7 @@ ofpart-$(CONFIG_MTD_OF_PARTS_BCM4908) +=
|
|
|
+ ofpart-$(CONFIG_MTD_OF_PARTS_LINKSYS_NS)+= ofpart_linksys_ns.o
|
|
|
+ obj-$(CONFIG_MTD_PARSER_IMAGETAG) += parser_imagetag.o
|
|
|
+ obj-$(CONFIG_MTD_AFS_PARTS) += afs.o
|
|
|
++obj-$(CONFIG_MTD_PARSER_TPLINK_SAFELOADER) += tplink_safeloader.o
|
|
|
+ obj-$(CONFIG_MTD_PARSER_TRX) += parser_trx.o
|
|
|
+ obj-$(CONFIG_MTD_SERCOMM_PARTS) += scpart.o
|
|
|
+ obj-$(CONFIG_MTD_SHARPSL_PARTS) += sharpslpart.o
|
|
|
+--- /dev/null
|
|
|
++++ b/drivers/mtd/parsers/tplink_safeloader.c
|
|
|
+@@ -0,0 +1,150 @@
|
|
|
++// SPDX-License-Identifier: GPL-2.0-only
|
|
|
++/*
|
|
|
++ * Copyright © 2022 Rafał Miłecki <[email protected]>
|
|
|
++ */
|
|
|
++
|
|
|
++#include <linux/kernel.h>
|
|
|
++#include <linux/module.h>
|
|
|
++#include <linux/mtd/mtd.h>
|
|
|
++#include <linux/mtd/partitions.h>
|
|
|
++#include <linux/of.h>
|
|
|
++#include <linux/slab.h>
|
|
|
++
|
|
|
++#define TPLINK_SAFELOADER_DATA_OFFSET 4
|
|
|
++#define TPLINK_SAFELOADER_MAX_PARTS 32
|
|
|
++
|
|
|
++struct safeloader_cmn_header {
|
|
|
++ __be32 size;
|
|
|
++ uint32_t unused;
|
|
|
++} __packed;
|
|
|
++
|
|
|
++static void *mtd_parser_tplink_safeloader_read_table(struct mtd_info *mtd)
|
|
|
++{
|
|
|
++ struct safeloader_cmn_header hdr;
|
|
|
++ struct device_node *np;
|
|
|
++ size_t bytes_read;
|
|
|
++ size_t offset;
|
|
|
++ size_t size;
|
|
|
++ char *buf;
|
|
|
++ int err;
|
|
|
++
|
|
|
++ np = mtd_get_of_node(mtd);
|
|
|
++ if (mtd_is_partition(mtd))
|
|
|
++ of_node_get(np);
|
|
|
++ else
|
|
|
++ np = of_get_child_by_name(np, "partitions");
|
|
|
++
|
|
|
++ if (of_property_read_u32(np, "partitions-table-offset", (u32 *)&offset)) {
|
|
|
++ pr_err("Failed to get partitions table offset\n");
|
|
|
++ goto err_put;
|
|
|
++ }
|
|
|
++
|
|
|
++ err = mtd_read(mtd, offset, sizeof(hdr), &bytes_read, (uint8_t *)&hdr);
|
|
|
++ if (err && !mtd_is_bitflip(err)) {
|
|
|
++ pr_err("Failed to read from %s at 0x%zx\n", mtd->name, offset);
|
|
|
++ goto err_put;
|
|
|
++ }
|
|
|
++
|
|
|
++ size = be32_to_cpu(hdr.size);
|
|
|
++
|
|
|
++ buf = kmalloc(size + 1, GFP_KERNEL);
|
|
|
++ if (!buf)
|
|
|
++ goto err_put;
|
|
|
++
|
|
|
++ err = mtd_read(mtd, offset + sizeof(hdr), size, &bytes_read, buf);
|
|
|
++ if (err && !mtd_is_bitflip(err)) {
|
|
|
++ pr_err("Failed to read from %s at 0x%zx\n", mtd->name, offset + sizeof(hdr));
|
|
|
++ goto err_kfree;
|
|
|
++ }
|
|
|
++
|
|
|
++ buf[size] = '\0';
|
|
|
++
|
|
|
++ of_node_put(np);
|
|
|
++
|
|
|
++ return buf;
|
|
|
++
|
|
|
++err_kfree:
|
|
|
++ kfree(buf);
|
|
|
++err_put:
|
|
|
++ of_node_put(np);
|
|
|
++ return NULL;
|
|
|
++}
|
|
|
++
|
|
|
++static int mtd_parser_tplink_safeloader_parse(struct mtd_info *mtd,
|
|
|
++ const struct mtd_partition **pparts,
|
|
|
++ struct mtd_part_parser_data *data)
|
|
|
++{
|
|
|
++ struct mtd_partition *parts;
|
|
|
++ char name[65];
|
|
|
++ size_t offset;
|
|
|
++ size_t bytes;
|
|
|
++ char *buf;
|
|
|
++ int idx;
|
|
|
++ int err;
|
|
|
++
|
|
|
++ parts = kcalloc(TPLINK_SAFELOADER_MAX_PARTS, sizeof(*parts), GFP_KERNEL);
|
|
|
++ if (!parts) {
|
|
|
++ err = -ENOMEM;
|
|
|
++ goto err_out;
|
|
|
++ }
|
|
|
++
|
|
|
++ buf = mtd_parser_tplink_safeloader_read_table(mtd);
|
|
|
++ if (!buf) {
|
|
|
++ err = -ENOENT;
|
|
|
++ goto err_out;
|
|
|
++ }
|
|
|
++
|
|
|
++ for (idx = 0, offset = TPLINK_SAFELOADER_DATA_OFFSET;
|
|
|
++ idx < TPLINK_SAFELOADER_MAX_PARTS &&
|
|
|
++ sscanf(buf + offset, "partition %64s base 0x%llx size 0x%llx%zn\n",
|
|
|
++ name, &parts[idx].offset, &parts[idx].size, &bytes) == 3;
|
|
|
++ idx++, offset += bytes + 1) {
|
|
|
++ parts[idx].name = kstrdup(name, GFP_KERNEL);
|
|
|
++ if (!parts[idx].name) {
|
|
|
++ err = -ENOMEM;
|
|
|
++ goto err_free;
|
|
|
++ }
|
|
|
++ }
|
|
|
++
|
|
|
++ if (idx == TPLINK_SAFELOADER_MAX_PARTS)
|
|
|
++ pr_warn("Reached maximum number of partitions!\n");
|
|
|
++
|
|
|
++ kfree(buf);
|
|
|
++
|
|
|
++ *pparts = parts;
|
|
|
++
|
|
|
++ return idx;
|
|
|
++
|
|
|
++err_free:
|
|
|
++ for (idx -= 1; idx >= 0; idx--)
|
|
|
++ kfree(parts[idx].name);
|
|
|
++err_out:
|
|
|
++ return err;
|
|
|
++};
|
|
|
++
|
|
|
++static void mtd_parser_tplink_safeloader_cleanup(const struct mtd_partition *pparts,
|
|
|
++ int nr_parts)
|
|
|
++{
|
|
|
++ int i;
|
|
|
++
|
|
|
++ for (i = 0; i < nr_parts; i++)
|
|
|
++ kfree(pparts[i].name);
|
|
|
++
|
|
|
++ kfree(pparts);
|
|
|
++}
|
|
|
++
|
|
|
++static const struct of_device_id mtd_parser_tplink_safeloader_of_match_table[] = {
|
|
|
++ { .compatible = "tplink,safeloader-partitions" },
|
|
|
++ {},
|
|
|
++};
|
|
|
++MODULE_DEVICE_TABLE(of, mtd_parser_tplink_safeloader_of_match_table);
|
|
|
++
|
|
|
++static struct mtd_part_parser mtd_parser_tplink_safeloader = {
|
|
|
++ .parse_fn = mtd_parser_tplink_safeloader_parse,
|
|
|
++ .cleanup = mtd_parser_tplink_safeloader_cleanup,
|
|
|
++ .name = "tplink-safeloader",
|
|
|
++ .of_match_table = mtd_parser_tplink_safeloader_of_match_table,
|
|
|
++};
|
|
|
++module_mtd_part_parser(mtd_parser_tplink_safeloader);
|
|
|
++
|
|
|
++MODULE_LICENSE("GPL");
|