blob: 9602dde9e59876059aa57f44e4393ccd38b00bab [file] [log] [blame]
xjb04a4022021-11-25 15:01:52 +08001// SPDX-License-Identifier: GPL-2.0
2/*
3 * Copyright (c) 2019 MediaTek Inc.
4 * Author: Guochun Mao <guochun.mao@mediatek.com>
5 * Xiaolei Li <xiaolei.li@mediatek.com>
6 */
7
8#include <linux/module.h>
9#include <linux/kernel.h>
10#include <linux/slab.h>
11#include <linux/mtd/mtd.h>
12#include <linux/mtd/partitions.h>
13#include <asm/div64.h>
14
15/* GPT Signature should be 0x5452415020494645 */
16#define GPT_SIGNATURE_1 0x54524150U
17#define GPT_SIGNATURE_2 0x20494645U
18
19/* GPT Offsets */
20#define HEADER_SIZE_OFFSET 12
21#define HEADER_CRC_OFFSET 16
22#define PRIMARY_HEADER_OFFSET 24
23#define BACKUP_HEADER_OFFSET 32
24#define FIRST_USABLE_LBA_OFFSET 40
25#define LAST_USABLE_LBA_OFFSET 48
26#define PARTITION_ENTRIES_OFFSET 72
27#define PARTITION_COUNT_OFFSET 80
28#define PENTRY_SIZE_OFFSET 84
29#define PARTITION_CRC_OFFSET 88
30
31#define ENTRY_SIZE 0x80
32
33#define UNIQUE_GUID_OFFSET 16
34#define FIRST_LBA_OFFSET 32
35#define LAST_LBA_OFFSET 40
36#define ATTRIBUTE_FLAG_OFFSET 48
37#define PARTITION_NAME_OFFSET 56
38
39#define MAX_GPT_NAME_SIZE 72
40#define PARTITION_TYPE_GUID_SIZE 16
41#define UNIQUE_PARTITION_GUID_SIZE 16
42#define NUM_PARTITIONS 128
43
44#define GET_LWORD_FROM_BYTE(ptr) (*(uint32_t *)((unsigned long)(ptr)))
45#define GET_LLWORD_FROM_BYTE(ptr) (*(uint64_t *)((unsigned long)(ptr)))
46
47struct chs {
48 uint8_t c;
49 uint8_t h;
50 uint8_t s;
51};
52
53struct mbr_part {
54 uint8_t status;
55 struct chs start;
56 uint8_t type;
57 struct chs end;
58 uint32_t lba_start;
59 uint32_t lba_length;
60};
61
62struct gpt_info {
63 uint64_t first_usable_lba;
64 uint64_t backup_header_lba;
65 uint32_t partition_entry_size;
66 uint32_t header_size;
67 uint32_t max_partition_count;
68};
69
70static int validate_mbr_partition(struct mtd_info *master,
71 const struct mbr_part *part)
72{
73 u32 lba_size, lba_number;
74 uint64_t temp;
75
76 if (mtd_type_is_nand(master) != 0)
77 lba_size = master->writesize;
78 else
79 lba_size = 512;
80
81 /* check for invalid types */
82 if ((int8_t)part->type == 0)
83 return -1;
84 /* check for invalid status */
85 if (part->status != (uint8_t)0x80 &&
86 part->status != (uint8_t)0x00)
87 return -1;
88
89 /* make sure the range fits within the device */
90 temp = (uint64_t)master->size;
91 do_div(temp, lba_size);
92 lba_number = temp;
93 if (part->lba_start >= lba_number)
94 return -1;
95 if ((part->lba_start + part->lba_length) > lba_number)
96 return -1;
97
98 return 0;
99}
100
101/*
102 * Parse the gpt header and get the required header fields
103 * Return 0 on valid signature
104 */
105static int partition_parse_gpt_header(unsigned char *buffer,
106 struct gpt_info *info)
107{
108 /* Check GPT Signature */
109 if (GET_LWORD_FROM_BYTE(&buffer[0]) != GPT_SIGNATURE_2 ||
110 GET_LWORD_FROM_BYTE(&buffer[4]) != GPT_SIGNATURE_1)
111 return 1;
112
113 info->header_size = GET_LWORD_FROM_BYTE(&buffer[HEADER_SIZE_OFFSET]);
114 info->backup_header_lba =
115 GET_LLWORD_FROM_BYTE(&buffer[BACKUP_HEADER_OFFSET]);
116 info->first_usable_lba =
117 GET_LLWORD_FROM_BYTE(&buffer[FIRST_USABLE_LBA_OFFSET]);
118 info->max_partition_count =
119 GET_LWORD_FROM_BYTE(&buffer[PARTITION_COUNT_OFFSET]);
120 info->partition_entry_size =
121 GET_LWORD_FROM_BYTE(&buffer[PENTRY_SIZE_OFFSET]);
122
123 return 0;
124}
125
126static void gpt_add_part(struct mtd_partition *part, char *name,
127 u64 offset, uint32_t mask_flags, uint64_t size)
128{
129 part->name = name;
130 part->offset = offset;
131 part->mask_flags = mask_flags;
132 part->size = size;
133}
134
135#define BBT_RESERVED_BLOCK 4
136#define SGPT_RESERVED_BLOCK 4
137#define PGPT_RESERVED_BLOCK 2
138/*
139 * @master: master mtd device description object pointer
140 * @ofs: the start address
141 */
142static int find_next_good_gpt_block(struct mtd_info *master, loff_t *ofs)
143{
144 loff_t addr = *ofs;
145 u32 count;
146 /* the block search order. 0: forward; otherwise: backward */
147 int order;
148 int i = 0;
149
150 /* PGPT start from address 0 */
151 if (addr < master->erasesize) {
152 count = PGPT_RESERVED_BLOCK;
153 order = 0;
154 } else {
155 count = SGPT_RESERVED_BLOCK;
156 order = 1;
157 }
158 while (i < count) {
159 if (!mtd_block_isbad(master, addr)) {
160 if (i)
161 dev_err(&master->dev, "GPT: map from 0x%llx to 0x%llx\n",
162 *ofs, addr);
163 *ofs = addr;
164 return 0;
165 }
166 addr = order ? (addr - master->erasesize) :
167 (addr + master->erasesize);
168 i++;
169 }
170
171 dev_err(&master->dev,
172 "all bad blocks are bad, addr:0x%llx, count:%d\n",
173 addr, count);
174
175 return -ENOMEM;
176}
177
178static int mtd_read_gpt(struct mtd_info *master, loff_t from, size_t len,
179 size_t *retlen, u_char *buf)
180{
181 loff_t addr = from;
182 int ret;
183
184 if (mtd_type_is_nand(master) != 0) {
185 ret = find_next_good_gpt_block(master, &addr);
186 if (ret)
187 return ret;
188 }
189
190 return mtd_read(master, addr, len, retlen, buf);
191}
192
193static int gpt_parse(struct mtd_info *master,
194 const struct mtd_partition **pparts,
195 struct mtd_part_parser_data *data)
196{
197 struct mtd_partition *parts;
198 int curr_part = 0;
199 int err;
200 u_char *buf, *temp_buf;
201 size_t bytes_read = 0;
202 int i, j, n, part_entry_cnt, gpt_entries;
203 int gpt_partitions_exist = 0;
204 struct mbr_part part[4];
205 struct gpt_info gptinfo = {0, 0, 0, 0, 0};
206 loff_t partition_0, sgpt_addr = master->size;
207 u32 lba_size;
208 int tmp;
209
210 dev_dbg(&master->dev, "GPT: enter gpt parser...\n");
211
212 if (mtd_type_is_nand(master) != 0) {
213 lba_size = master->writesize;
214 sgpt_addr -= BBT_RESERVED_BLOCK * master->erasesize;
215 } else {
216 lba_size = 512;
217 sgpt_addr -= lba_size;
218 }
219
220 buf = kzalloc(lba_size, GFP_KERNEL);
221 if (buf == NULL)
222 return -ENOMEM;
223
224 err = mtd_read_gpt(master, 0, lba_size, &bytes_read, buf);
225 if (err < 0)
226 goto freebuf;
227
228 /* look for the aa55 tag */
229 if (buf[510] != (u_char)0x55 || buf[511] != (u_char)0xaa) {
230 dev_err(&master->dev, "GPT: not find aa55 @ 510,511\n");
231 goto freebuf;
232 }
233
234 /* see if a partition table makes sense here */
235 temp_buf = (u_char *)&part[0];
236 memcpy(temp_buf, &buf[446], sizeof(part));
237
238 /* validate each of the partition entries */
239 for (i = 0; i < 4; i++) {
240 if (validate_mbr_partition(master, &part[i]) >= 0) {
241 /*
242 * Type 0xEE indicates end of MBR
243 * and GPT partitions exist
244 */
245 if (part[i].type == (uint8_t)0xee) {
246 gpt_partitions_exist = 1;
247 break;
248 }
249 }
250 }
251
252 if (gpt_partitions_exist == 0) {
253 dev_err(&master->dev, "GPT: not find GPT\n");
254 goto freebuf;
255 }
256
257 err = mtd_read_gpt(master, (loff_t)lba_size, lba_size, &bytes_read,
258 buf);
259 if (err < 0)
260 goto freebuf;
261
262 err = partition_parse_gpt_header(buf, &gptinfo);
263 if (err != 0) {
264 dev_warn(&master->dev, "GPT: Read GPT header fail, try to check the backup gpt.\n");
265 err = mtd_read(master, sgpt_addr - (loff_t)lba_size,
266 lba_size, &bytes_read, buf);
267 if (err < 0) {
268 dev_err(&master->dev, "GPT: Could not read backup gpt.\n");
269 goto freebuf;
270 }
271
272 err = partition_parse_gpt_header(buf, &gptinfo);
273 if (err != 0) {
274 dev_err(&master->dev, "GPT: Primary and backup signatures invalid.\n");
275 goto freebuf;
276 }
277 }
278
279 parts = kcalloc(gptinfo.max_partition_count,
280 sizeof(struct mtd_partition), GFP_KERNEL);
281 if (parts == NULL)
282 goto freebuf;
283
284 part_entry_cnt = (int)lba_size / ENTRY_SIZE;
285 partition_0 =
286 (loff_t)GET_LLWORD_FROM_BYTE(&buf[PARTITION_ENTRIES_OFFSET]);
287
288 /* Read GPT Entries */
289 gpt_entries = roundup((int)gptinfo.max_partition_count, part_entry_cnt);
290 gpt_entries /= part_entry_cnt;
291 for (i = 0; i < gpt_entries; i++) {
292 err = mtd_read_gpt(master,
293 ((partition_0 + (loff_t)i) * (loff_t)lba_size),
294 lba_size, &bytes_read, buf);
295 if (err < 0) {
296 dev_err(&master->dev, "GPT: read failed reading partition entries.\n");
297 goto freeparts;
298 }
299
300 for (j = 0; j < part_entry_cnt; j++) {
301 int8_t type_guid[PARTITION_TYPE_GUID_SIZE];
302 char *name = kzalloc(MAX_GPT_NAME_SIZE,
303 GFP_KERNEL);
304 char UTF16_name[MAX_GPT_NAME_SIZE];
305 uint64_t first_lba, last_lba, size;
306
307 temp_buf = (u_char *)&type_guid[0];
308 memcpy(temp_buf,
309 &buf[(u32)j * gptinfo.partition_entry_size],
310 PARTITION_TYPE_GUID_SIZE);
311 if (type_guid[0] == 0 && type_guid[1] == 0) {
312 kfree(name);
313 goto parsedone;
314 }
315
316 tmp = j * (int)gptinfo.partition_entry_size;
317 tmp += FIRST_LBA_OFFSET;
318 first_lba = GET_LLWORD_FROM_BYTE(&buf[tmp]);
319 tmp = j * (int)gptinfo.partition_entry_size;
320 tmp += LAST_LBA_OFFSET;
321 last_lba = GET_LLWORD_FROM_BYTE(&buf[tmp]);
322 size = last_lba - first_lba + 1ULL;
323
324 memset(&UTF16_name[0], 0x00, MAX_GPT_NAME_SIZE);
325 temp_buf = (u_char *)&UTF16_name[0];
326 memcpy(temp_buf,
327 &buf[(u32)j * gptinfo.partition_entry_size +
328 (uint32_t)PARTITION_NAME_OFFSET],
329 MAX_GPT_NAME_SIZE);
330
331 /*
332 * Currently partition names in *.xml are UTF-8 and
333 * lowercase only supporting english for now so removing
334 * 2nd byte of UTF-16
335 */
336 for (n = 0; n < MAX_GPT_NAME_SIZE / 2; n++)
337 name[n] = UTF16_name[n * 2];
338
339 dev_dbg(&master->dev, "partition(%s) first_lba(%llu), last_lba(%llu), size(%llu)\n",
340 name, first_lba, last_lba, size);
341
342 gpt_add_part(&parts[curr_part++], name,
343 first_lba * lba_size, 0,
344 (last_lba - first_lba + 1ULL) * lba_size);
345
346 dev_dbg(&master->dev, "gpt there are <%d> parititons.\n",
347 curr_part);
348 }
349 }
350
351parsedone:
352 *pparts = parts;
353 kfree(buf);
354 return curr_part;
355
356freeparts:
357 kfree(parts);
358freebuf:
359 kfree(buf);
360 return 0;
361};
362
363static struct mtd_part_parser gpt_parser = {
364 .owner = THIS_MODULE,
365 .parse_fn = gpt_parse,
366 .name = "gptpart",
367};
368
369static int __init gptpart_init(void)
370{
371 return register_mtd_parser(&gpt_parser);
372}
373
374static void __exit gptpart_exit(void)
375{
376 deregister_mtd_parser(&gpt_parser);
377}
378
379module_init(gptpart_init);
380module_exit(gptpart_exit);
381
382MODULE_LICENSE("GPL v2");
383MODULE_DESCRIPTION("GPT partitioning for flash memories");