2022-10-15 11:29:50 +02:00
// SPDX-License-Identifier: GPL-2.0-only
/*
* Copyright © 2022 Rafał Miłecki < rafal @ milecki . pl >
*/
# 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 size ;
2022-10-25 18:34:24 +03:00
u32 offset ;
2022-10-15 11:29:50 +02:00
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 " ) ;
2022-10-25 18:34:24 +03:00
if ( of_property_read_u32 ( np , " partitions-table-offset " , & offset ) ) {
2022-10-15 11:29:50 +02:00
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 ) ) {
2022-10-25 18:34:24 +03:00
pr_err ( " Failed to read from %s at 0x%x \n " , mtd - > name , offset ) ;
2022-10-15 11:29:50 +02:00
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 ;
2022-12-08 11:36:20 +00:00
goto err_free_parts ;
2022-10-15 11:29:50 +02:00
}
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 ) ;
2022-12-08 11:36:20 +00:00
err_free_parts :
kfree ( parts ) ;
2022-10-15 11:29:50 +02:00
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 " ) ;