1 | // SPDX-License-Identifier: GPL-2.0-only |
2 | /* |
3 | * Parser for TRX format partitions |
4 | * |
5 | * Copyright (C) 2012 - 2017 Rafał Miłecki <rafal@milecki.pl> |
6 | */ |
7 | |
8 | #include <linux/module.h> |
9 | #include <linux/slab.h> |
10 | #include <linux/mtd/mtd.h> |
11 | #include <linux/mtd/partitions.h> |
12 | |
13 | #define TRX_PARSER_MAX_PARTS 4 |
14 | |
15 | /* Magics */ |
16 | #define TRX_MAGIC 0x30524448 |
17 | #define UBI_EC_MAGIC 0x23494255 /* UBI# */ |
18 | |
19 | struct { |
20 | uint32_t ; |
21 | uint32_t ; |
22 | uint32_t ; |
23 | uint16_t ; |
24 | uint16_t ; |
25 | uint32_t [3]; |
26 | } __packed; |
27 | |
28 | static const char *parser_trx_data_part_name(struct mtd_info *master, |
29 | size_t offset) |
30 | { |
31 | uint32_t buf; |
32 | size_t bytes_read; |
33 | int err; |
34 | |
35 | err = mtd_read(mtd: master, from: offset, len: sizeof(buf), retlen: &bytes_read, |
36 | buf: (uint8_t *)&buf); |
37 | if (err && !mtd_is_bitflip(err)) { |
38 | pr_err("mtd_read error while parsing (offset: 0x%zX): %d\n" , |
39 | offset, err); |
40 | goto out_default; |
41 | } |
42 | |
43 | if (buf == UBI_EC_MAGIC) |
44 | return "ubi" ; |
45 | |
46 | out_default: |
47 | return "rootfs" ; |
48 | } |
49 | |
50 | static int parser_trx_parse(struct mtd_info *mtd, |
51 | const struct mtd_partition **pparts, |
52 | struct mtd_part_parser_data *data) |
53 | { |
54 | struct device_node *np = mtd_get_of_node(mtd); |
55 | struct mtd_partition *parts; |
56 | struct mtd_partition *part; |
57 | struct trx_header trx; |
58 | size_t bytes_read; |
59 | uint8_t curr_part = 0, i = 0; |
60 | uint32_t trx_magic = TRX_MAGIC; |
61 | int err; |
62 | |
63 | /* Get different magic from device tree if specified */ |
64 | err = of_property_read_u32(np, propname: "brcm,trx-magic" , out_value: &trx_magic); |
65 | if (err != 0 && err != -EINVAL) |
66 | pr_err("failed to parse \"brcm,trx-magic\" DT attribute, using default: %d\n" , err); |
67 | |
68 | parts = kcalloc(TRX_PARSER_MAX_PARTS, size: sizeof(struct mtd_partition), |
69 | GFP_KERNEL); |
70 | if (!parts) |
71 | return -ENOMEM; |
72 | |
73 | err = mtd_read(mtd, from: 0, len: sizeof(trx), retlen: &bytes_read, buf: (uint8_t *)&trx); |
74 | if (err) { |
75 | pr_err("MTD reading error: %d\n" , err); |
76 | kfree(objp: parts); |
77 | return err; |
78 | } |
79 | |
80 | if (trx.magic != trx_magic) { |
81 | kfree(objp: parts); |
82 | return -ENOENT; |
83 | } |
84 | |
85 | /* We have LZMA loader if there is address in offset[2] */ |
86 | if (trx.offset[2]) { |
87 | part = &parts[curr_part++]; |
88 | part->name = "loader" ; |
89 | part->offset = trx.offset[i]; |
90 | i++; |
91 | } |
92 | |
93 | if (trx.offset[i]) { |
94 | part = &parts[curr_part++]; |
95 | part->name = "linux" ; |
96 | part->offset = trx.offset[i]; |
97 | i++; |
98 | } |
99 | |
100 | if (trx.offset[i]) { |
101 | part = &parts[curr_part++]; |
102 | part->name = parser_trx_data_part_name(master: mtd, offset: trx.offset[i]); |
103 | part->offset = trx.offset[i]; |
104 | i++; |
105 | } |
106 | |
107 | /* |
108 | * Assume that every partition ends at the beginning of the one it is |
109 | * followed by. |
110 | */ |
111 | for (i = 0; i < curr_part; i++) { |
112 | u64 next_part_offset = (i < curr_part - 1) ? |
113 | parts[i + 1].offset : mtd->size; |
114 | |
115 | parts[i].size = next_part_offset - parts[i].offset; |
116 | } |
117 | |
118 | *pparts = parts; |
119 | return i; |
120 | }; |
121 | |
122 | static const struct of_device_id mtd_parser_trx_of_match_table[] = { |
123 | { .compatible = "brcm,trx" }, |
124 | {}, |
125 | }; |
126 | MODULE_DEVICE_TABLE(of, mtd_parser_trx_of_match_table); |
127 | |
128 | static struct mtd_part_parser mtd_parser_trx = { |
129 | .parse_fn = parser_trx_parse, |
130 | .name = "trx" , |
131 | .of_match_table = mtd_parser_trx_of_match_table, |
132 | }; |
133 | module_mtd_part_parser(mtd_parser_trx); |
134 | |
135 | MODULE_LICENSE("GPL v2" ); |
136 | MODULE_DESCRIPTION("Parser for TRX format partitions" ); |
137 | |