1 // SPDX-License-Identifier: GPL-2.0+
2 /*
3  * (C) Copyright 2001
4  * Raymond Lo, lo@routefree.com
5  * Wolfgang Denk, DENX Software Engineering, wd@denx.de.
6  */
7 
8 /*
9  * Support for harddisk partitions.
10  *
11  * To be compatible with LinuxPPC and Apple we use the standard Apple
12  * SCSI disk partitioning scheme. For more information see:
13  * http://developer.apple.com/techpubs/mac/Devices/Devices-126.html#MARKER-14-92
14  */
15 
16 #include <blk.h>
17 #include <command.h>
18 #include <memalign.h>
19 #include <vsprintf.h>
20 #include <asm/unaligned.h>
21 #include <linux/compiler.h>
22 #include "part_dos.h"
23 #include <part.h>
24 
25 #define DOS_PART_DEFAULT_SECTOR 512
26 
27 /* should this be configurable? It looks like it's not very common at all
28  * to use large numbers of partitions */
29 #define MAX_EXT_PARTS 256
30 
is_extended(int part_type)31 static inline int is_extended(int part_type)
32 {
33     return (part_type == DOS_PART_TYPE_EXTENDED ||
34 	    part_type == DOS_PART_TYPE_EXTENDED_LBA ||
35 	    part_type == DOS_PART_TYPE_EXTENDED_LINUX);
36 }
37 
get_bootable(dos_partition_t * p)38 static int get_bootable(dos_partition_t *p)
39 {
40 	int ret = 0;
41 
42 	if (p->sys_ind == 0xef)
43 		ret |= PART_EFI_SYSTEM_PARTITION;
44 	if (p->boot_ind == 0x80)
45 		ret |= PART_BOOTABLE;
46 	return ret;
47 }
48 
print_one_part(dos_partition_t * p,lbaint_t ext_part_sector,int part_num,unsigned int disksig)49 static void print_one_part(dos_partition_t *p, lbaint_t ext_part_sector,
50 			   int part_num, unsigned int disksig)
51 {
52 	lbaint_t lba_start = ext_part_sector + get_unaligned_le32(p->start4);
53 	lbaint_t lba_size  = get_unaligned_le32(p->size4);
54 
55 	printf("%3d\t%-10" LBAFlength "u\t%-10" LBAFlength
56 		"u\t%08x-%02x\t%02x%s%s\n",
57 		part_num, lba_start, lba_size, disksig, part_num, p->sys_ind,
58 		(is_extended(p->sys_ind) ? " Extd" : ""),
59 		(get_bootable(p) ? " Boot" : ""));
60 }
61 
test_block_type(unsigned char * buffer)62 static int test_block_type(unsigned char *buffer)
63 {
64 	int slot;
65 	struct dos_partition *p;
66 	int part_count = 0;
67 
68 	if((buffer[DOS_PART_MAGIC_OFFSET + 0] != 0x55) ||
69 	    (buffer[DOS_PART_MAGIC_OFFSET + 1] != 0xaa) ) {
70 		return (-1);
71 	} /* no DOS Signature at all */
72 	p = (struct dos_partition *)&buffer[DOS_PART_TBL_OFFSET];
73 
74 	/* Check that the boot indicators are valid and count the partitions. */
75 	for (slot = 0; slot < 4; ++slot, ++p) {
76 		if (p->boot_ind != 0 && p->boot_ind != 0x80)
77 			break;
78 		if (p->sys_ind)
79 			++part_count;
80 	}
81 
82 	/*
83 	 * If the partition table is invalid or empty,
84 	 * check if this is a DOS PBR
85 	 */
86 	if (slot != 4 || !part_count) {
87 		if (!strncmp((char *)&buffer[DOS_PBR_FSTYPE_OFFSET],
88 			     "FAT", 3) ||
89 		    !strncmp((char *)&buffer[DOS_PBR32_FSTYPE_OFFSET],
90 			     "FAT32", 5))
91 			return DOS_PBR; /* This is a DOS PBR and not an MBR */
92 	}
93 	if (slot == 4)
94 		return DOS_MBR;	/* This is an DOS MBR */
95 
96 	/* This is neither a DOS MBR nor a DOS PBR */
97 	return -1;
98 }
99 
part_test_dos(struct blk_desc * desc)100 static int part_test_dos(struct blk_desc *desc)
101 {
102 #ifndef CONFIG_XPL_BUILD
103 	ALLOC_CACHE_ALIGN_BUFFER(legacy_mbr, mbr,
104 			DIV_ROUND_UP(desc->blksz, sizeof(legacy_mbr)));
105 
106 	if (blk_dread(desc, 0, 1, (ulong *)mbr) != 1)
107 		return -1;
108 
109 	if (test_block_type((unsigned char *)mbr) != DOS_MBR)
110 		return -1;
111 
112 	if (desc->sig_type == SIG_TYPE_NONE && mbr->unique_mbr_signature) {
113 		desc->sig_type = SIG_TYPE_MBR;
114 		desc->mbr_sig = mbr->unique_mbr_signature;
115 	}
116 #else
117 	ALLOC_CACHE_ALIGN_BUFFER(unsigned char, buffer, desc->blksz);
118 
119 	if (blk_dread(desc, 0, 1, (ulong *)buffer) != 1)
120 		return -1;
121 
122 	if (test_block_type(buffer) != DOS_MBR)
123 		return -1;
124 #endif
125 
126 	return 0;
127 }
128 
129 /*  Print a partition that is relative to its Extended partition table
130  */
print_partition_extended(struct blk_desc * desc,lbaint_t ext_part_sector,lbaint_t relative,int part_num,unsigned int disksig)131 static void print_partition_extended(struct blk_desc *desc,
132 				     lbaint_t ext_part_sector,
133 				     lbaint_t relative,
134 				     int part_num, unsigned int disksig)
135 {
136 	ALLOC_CACHE_ALIGN_BUFFER(unsigned char, buffer, desc->blksz);
137 	dos_partition_t *pt;
138 	int i;
139 
140 	/* set a maximum recursion level */
141 	if (part_num > MAX_EXT_PARTS)
142 	{
143 		printf("** Nested DOS partitions detected, stopping **\n");
144 		return;
145     }
146 
147 	if (blk_dread(desc, ext_part_sector, 1, (ulong *)buffer) != 1) {
148 		printf ("** Can't read partition table on %d:" LBAFU " **\n",
149 			desc->devnum, ext_part_sector);
150 		return;
151 	}
152 	i=test_block_type(buffer);
153 	if (i != DOS_MBR) {
154 		printf ("bad MBR sector signature 0x%02x%02x\n",
155 			buffer[DOS_PART_MAGIC_OFFSET],
156 			buffer[DOS_PART_MAGIC_OFFSET + 1]);
157 		return;
158 	}
159 
160 	if (!ext_part_sector)
161 		disksig = get_unaligned_le32(&buffer[DOS_PART_DISKSIG_OFFSET]);
162 
163 	/* Print all primary/logical partitions */
164 	pt = (dos_partition_t *) (buffer + DOS_PART_TBL_OFFSET);
165 	for (i = 0; i < 4; i++, pt++) {
166 		/*
167 		 * fdisk does not show the extended partitions that
168 		 * are not in the MBR
169 		 */
170 
171 		if ((pt->sys_ind != 0) &&
172 		    (ext_part_sector == 0 || !is_extended (pt->sys_ind)) ) {
173 			print_one_part(pt, ext_part_sector, part_num, disksig);
174 		}
175 
176 		/* Reverse engr the fdisk part# assignment rule! */
177 		if ((ext_part_sector == 0) ||
178 		    (pt->sys_ind != 0 && !is_extended (pt->sys_ind)) ) {
179 			part_num++;
180 		}
181 	}
182 
183 	/* Follows the extended partitions */
184 	pt = (dos_partition_t *) (buffer + DOS_PART_TBL_OFFSET);
185 	for (i = 0; i < 4; i++, pt++) {
186 		if (is_extended (pt->sys_ind)) {
187 			lbaint_t lba_start
188 				= get_unaligned_le32 (pt->start4) + relative;
189 
190 			print_partition_extended(desc, lba_start,
191 						 !ext_part_sector ? lba_start :
192 						 relative, part_num, disksig);
193 		}
194 	}
195 
196 	return;
197 }
198 
199 /*  Print a partition that is relative to its Extended partition table
200  */
part_get_info_extended(struct blk_desc * desc,lbaint_t ext_part_sector,lbaint_t relative,int part_num,int which_part,struct disk_partition * info,uint disksig)201 static int part_get_info_extended(struct blk_desc *desc,
202 				  lbaint_t ext_part_sector, lbaint_t relative,
203 				  int part_num, int which_part,
204 				  struct disk_partition *info, uint disksig)
205 {
206 	ALLOC_CACHE_ALIGN_BUFFER(unsigned char, buffer, desc->blksz);
207 	struct disk_partition wdinfo = { 0 };
208 	dos_partition_t *pt;
209 	int i, ret;
210 	int dos_type;
211 
212 	/* set a maximum recursion level */
213 	if (part_num > MAX_EXT_PARTS)
214 	{
215 		printf("** Nested DOS partitions detected, stopping **\n");
216 		return -1;
217     }
218 
219 	if (blk_dread(desc, ext_part_sector, 1, (ulong *)buffer) != 1) {
220 		printf ("** Can't read partition table on %d:" LBAFU " **\n",
221 			desc->devnum, ext_part_sector);
222 		return -1;
223 	}
224 	if (buffer[DOS_PART_MAGIC_OFFSET] != 0x55 ||
225 		buffer[DOS_PART_MAGIC_OFFSET + 1] != 0xaa) {
226 		printf ("bad MBR sector signature 0x%02x%02x\n",
227 			buffer[DOS_PART_MAGIC_OFFSET],
228 			buffer[DOS_PART_MAGIC_OFFSET + 1]);
229 		return -1;
230 	}
231 
232 	if (CONFIG_IS_ENABLED(PARTITION_UUIDS) && !ext_part_sector)
233 		disksig = get_unaligned_le32(&buffer[DOS_PART_DISKSIG_OFFSET]);
234 
235 	ret = part_get_info_whole_disk(desc, &wdinfo);
236 	if (ret)
237 		return ret;
238 
239 	/* Print all primary/logical partitions */
240 	pt = (dos_partition_t *) (buffer + DOS_PART_TBL_OFFSET);
241 	for (i = 0; i < 4; i++, pt++) {
242 		/*
243 		 * fdisk does not show the extended partitions that
244 		 * are not in the MBR
245 		 */
246 		if (((pt->boot_ind & ~0x80) == 0) &&
247 		    (pt->sys_ind != 0) &&
248 		    (part_num == which_part) &&
249 		    (ext_part_sector == 0 || is_extended(pt->sys_ind) == 0)) {
250 			if (wdinfo.blksz > DOS_PART_DEFAULT_SECTOR)
251 				info->blksz = wdinfo.blksz;
252 			else
253 				info->blksz = DOS_PART_DEFAULT_SECTOR;
254 			info->start = (lbaint_t)(ext_part_sector +
255 					get_unaligned_le32(pt->start4));
256 			info->size  = (lbaint_t)get_unaligned_le32(pt->size4);
257 			part_set_generic_name(desc, part_num,
258 					      (char *)info->name);
259 			/* sprintf(info->type, "%d, pt->sys_ind); */
260 			strcpy((char *)info->type, "U-Boot");
261 			info->bootable = get_bootable(pt);
262 			if (CONFIG_IS_ENABLED(PARTITION_UUIDS)) {
263 				char str[12];
264 
265 				sprintf(str, "%08x-%02x", disksig, part_num);
266 				disk_partition_set_uuid(info, str);
267 			}
268 			info->sys_ind = pt->sys_ind;
269 			return 0;
270 		}
271 
272 		/* Reverse engr the fdisk part# assignment rule! */
273 		if ((ext_part_sector == 0) ||
274 		    (pt->sys_ind != 0 && !is_extended (pt->sys_ind)) ) {
275 			part_num++;
276 		}
277 	}
278 
279 	/* Follows the extended partitions */
280 	pt = (dos_partition_t *) (buffer + DOS_PART_TBL_OFFSET);
281 	for (i = 0; i < 4; i++, pt++) {
282 		if (is_extended (pt->sys_ind)) {
283 			lbaint_t lba_start
284 				= get_unaligned_le32 (pt->start4) + relative;
285 
286 			return part_get_info_extended(desc, lba_start,
287 				 ext_part_sector == 0 ? lba_start : relative,
288 				 part_num, which_part, info, disksig);
289 		}
290 	}
291 
292 	/* Check for DOS PBR if no partition is found */
293 	dos_type = test_block_type(buffer);
294 
295 	if (dos_type == DOS_PBR) {
296 		info->start = 0;
297 		info->size = desc->lba;
298 		if (wdinfo.blksz > DOS_PART_DEFAULT_SECTOR)
299 			info->blksz = wdinfo.blksz;
300 		else
301 			info->blksz = DOS_PART_DEFAULT_SECTOR;
302 		info->bootable = 0;
303 		strcpy((char *)info->type, "U-Boot");
304 		disk_partition_clr_uuid(info);
305 		return 0;
306 	}
307 
308 	return -1;
309 }
310 
part_print_dos(struct blk_desc * desc)311 static void __maybe_unused part_print_dos(struct blk_desc *desc)
312 {
313 	printf("Part\tStart Sector\tNum Sectors\tUUID\t\tType\n");
314 	print_partition_extended(desc, 0, 0, 1, 0);
315 }
316 
part_get_info_dos(struct blk_desc * desc,int part,struct disk_partition * info)317 static int __maybe_unused part_get_info_dos(struct blk_desc *desc, int part,
318 					    struct disk_partition *info)
319 {
320 	return part_get_info_extended(desc, 0, 0, 1, part, info, 0);
321 }
322 
is_valid_dos_buf(void * buf)323 int is_valid_dos_buf(void *buf)
324 {
325 	return test_block_type(buf) == DOS_MBR ? 0 : -1;
326 }
327 
328 #if IS_ENABLED(CONFIG_CMD_MBR)
lba_to_chs(lbaint_t lba,unsigned char * rc,unsigned char * rh,unsigned char * rs)329 static void lba_to_chs(lbaint_t lba, unsigned char *rc, unsigned char *rh,
330 		       unsigned char *rs)
331 {
332 	unsigned int c, h, s;
333 	/* use fixed CHS geometry */
334 	unsigned int sectpertrack = 63;
335 	unsigned int heads = 255;
336 
337 	c = (lba + 1) / sectpertrack / heads;
338 	h = (lba + 1) / sectpertrack - c * heads;
339 	s = (lba + 1) - (c * heads + h) * sectpertrack;
340 
341 	if (c > 1023) {
342 		c = 1023;
343 		h = 254;
344 		s = 63;
345 	}
346 
347 	*rc = c & 0xff;
348 	*rh = h;
349 	*rs = s + ((c & 0x300) >> 2);
350 }
351 
mbr_fill_pt_entry(dos_partition_t * pt,lbaint_t start,lbaint_t relative,lbaint_t size,uchar sys_ind,bool bootable)352 static void mbr_fill_pt_entry(dos_partition_t *pt, lbaint_t start,
353 		lbaint_t relative, lbaint_t size, uchar sys_ind, bool bootable)
354 {
355 	pt->boot_ind = bootable ? 0x80 : 0x00;
356 	pt->sys_ind = sys_ind;
357 	lba_to_chs(start, &pt->cyl, &pt->head, &pt->sector);
358 	lba_to_chs(start + size - 1, &pt->end_cyl, &pt->end_head, &pt->end_sector);
359 	put_unaligned_le32(relative, &pt->start4);
360 	put_unaligned_le32(size, &pt->size4);
361 }
362 
write_mbr_partitions(struct blk_desc * dev,struct disk_partition * p,int count,unsigned int disksig)363 int write_mbr_partitions(struct blk_desc *dev,
364 		struct disk_partition *p, int count, unsigned int disksig)
365 {
366 	ALLOC_CACHE_ALIGN_BUFFER(unsigned char, buffer, dev->blksz);
367 	lbaint_t ext_part_start = 0, ext_part_size = 0, ext_part_sect = 0;
368 	dos_partition_t *pt;
369 	int i;
370 
371 	memset(buffer, 0, dev->blksz);
372 	buffer[DOS_PART_MAGIC_OFFSET] = 0x55;
373 	buffer[DOS_PART_MAGIC_OFFSET + 1] = 0xaa;
374 	put_unaligned_le32(disksig, &buffer[DOS_PART_DISKSIG_OFFSET]);
375 	pt = (dos_partition_t *) (buffer + DOS_PART_TBL_OFFSET);
376 
377 	/* create all primary partitions */
378 	for (i = 0; i < 4 && i < count; i++, pt++) {
379 		mbr_fill_pt_entry(pt, p[i].start, p[i].start, p[i].size,
380 				  p[i].sys_ind, p[i].bootable);
381 		if (is_extended(p[i].sys_ind)) {
382 			ext_part_start = p[i].start;
383 			ext_part_size = p[i].size;
384 			ext_part_sect = p[i].start;
385 		}
386 	}
387 
388 	if (i < count && !ext_part_start) {
389 		printf("%s: extended partition is needed for more than 4 partitions\n",
390 		        __func__);
391 		return -1;
392 	}
393 
394 	/* write MBR */
395 	if (blk_dwrite(dev, 0, 1, buffer) != 1) {
396 		printf("%s: failed writing 'MBR' (1 blks at 0x0)\n",
397 		       __func__);
398 		return -1;
399 	}
400 
401 	/* create extended volumes */
402 	for (; i < count; i++) {
403 		lbaint_t next_ebr = 0;
404 
405 		memset(buffer, 0, dev->blksz);
406 		buffer[DOS_PART_MAGIC_OFFSET] = 0x55;
407 		buffer[DOS_PART_MAGIC_OFFSET + 1] = 0xaa;
408 		pt = (dos_partition_t *) (buffer + DOS_PART_TBL_OFFSET);
409 
410 		mbr_fill_pt_entry(pt, p[i].start, p[i].start - ext_part_sect,
411 				  p[i].size, p[i].sys_ind, p[i].bootable);
412 
413 		if (i + 1 < count) {
414 			pt++;
415 			next_ebr = p[i].start + p[i].size;
416 			mbr_fill_pt_entry(pt, next_ebr,
417 					  next_ebr - ext_part_start,
418 					  p[i+1].start + p[i+1].size - next_ebr,
419 					  DOS_PART_TYPE_EXTENDED, 0);
420 		}
421 
422 		/* write EBR */
423 		if (blk_dwrite(dev, ext_part_sect, 1, buffer) != 1) {
424 			printf("%s: failed writing 'EBR' (1 blks at 0x" LBAF ")\n",
425 			       __func__, ext_part_sect);
426 			return -1;
427 		}
428 		ext_part_sect = next_ebr;
429 	}
430 
431 	/* Update the partition table entries*/
432 	part_init(dev);
433 
434 	return 0;
435 }
436 
layout_mbr_partitions(struct disk_partition * p,int count,lbaint_t total_sectors)437 int layout_mbr_partitions(struct disk_partition *p, int count,
438 			  lbaint_t total_sectors)
439 {
440 	struct disk_partition *ext = NULL;
441 	int i, j;
442 	lbaint_t ext_vol_start;
443 
444 	/* calculate primary partitions start and size if needed */
445 	if (!p[0].start)
446 		p[0].start = DOS_PART_DEFAULT_GAP;
447 	for (i = 0; i < 4 && i < count; i++) {
448 		if (!p[i].start)
449 			p[i].start = p[i - 1].start + p[i - 1].size;
450 		if (!p[i].size) {
451 			lbaint_t end = total_sectors;
452 			lbaint_t allocated = 0;
453 
454 			for (j = i + 1; j < 4 && j < count; j++) {
455 				if (p[j].start) {
456 					end = p[j].start;
457 					break;
458 				}
459 				allocated += p[j].size;
460 			}
461 			p[i].size = end - allocated - p[i].start;
462 		}
463 		if (p[i].sys_ind == 0x05)
464 			ext = &p[i];
465 	}
466 
467 	if (count <= 4)
468 		return 0;
469 
470 	if (!ext) {
471 		log_err("extended partition is needed for more than 4 partitions\n");
472 		return -EINVAL;
473 	}
474 
475 	/* calculate extended volumes start and size if needed */
476 	ext_vol_start = ext->start;
477 	for (i = 4; i < count; i++) {
478 		if (!p[i].start)
479 			p[i].start = ext_vol_start + DOS_PART_DEFAULT_GAP;
480 		if (!p[i].size) {
481 			lbaint_t end = ext->start + ext->size;
482 			lbaint_t allocated = 0;
483 
484 			for (j = i + 1; j < count; j++) {
485 				if (p[j].start) {
486 					end = p[j].start - DOS_PART_DEFAULT_GAP;
487 					break;
488 				}
489 				allocated += p[j].size + DOS_PART_DEFAULT_GAP;
490 			}
491 			p[i].size = end - allocated - p[i].start;
492 		}
493 		ext_vol_start = p[i].start + p[i].size;
494 	}
495 
496 	return 0;
497 }
498 #endif
499 
write_mbr_sector(struct blk_desc * desc,void * buf)500 int write_mbr_sector(struct blk_desc *desc, void *buf)
501 {
502 	if (is_valid_dos_buf(buf))
503 		return -1;
504 
505 	/* write MBR */
506 	if (blk_dwrite(desc, 0, 1, buf) != 1) {
507 		printf("%s: failed writing '%s' (1 blks at 0x0)\n",
508 		       __func__, "MBR");
509 		return 1;
510 	}
511 
512 	/* Update the partition table entries*/
513 	part_init(desc);
514 
515 	return 0;
516 }
517 
518 U_BOOT_PART_TYPE(dos) = {
519 	.name		= "DOS",
520 	.part_type	= PART_TYPE_DOS,
521 	.max_entries	= DOS_ENTRY_NUMBERS,
522 	.get_info	= part_get_info_ptr(part_get_info_dos),
523 	.print		= part_print_ptr(part_print_dos),
524 	.test		= part_test_dos,
525 };
526