u-boot-brain/disk/part_dos.c
Egbert Eich 9d956e0fef disk/part_dos: check harder for partition table
Devices that used to have a whole disk FAT filesystem but got then
partitioned will most likely still have a FAT or FAT32 signature
in the first sector as this sector does not get overwritten by
a partitioning tool (otherwise the tool would risk to kill the mbr).

The current partition search algorithm will erronously detects such
a device as a raw FAT device.

Instead of looking for the FAT or FAT32 signatures immediately we
use the same algorithm as used by the Linux kernel and first check
for a valid boot indicator flag on each of the 4 partitions.
If the value of this flag is invalid for the first entry we then
do the raw partition check.
If the flag for any higher partition is wrong we assume the device
is neiter a MBR nor PBR device.

Signed-off-by: Egbert Eich <eich@suse.com>
2013-05-01 16:24:01 -04:00

287 lines
7.8 KiB
C

/*
* (C) Copyright 2001
* Raymond Lo, lo@routefree.com
* Wolfgang Denk, DENX Software Engineering, wd@denx.de.
*
* See file CREDITS for list of people who contributed to this
* project.
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License as
* published by the Free Software Foundation; either version 2 of
* the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston,
* MA 02111-1307 USA
*/
/*
* Support for harddisk partitions.
*
* To be compatible with LinuxPPC and Apple we use the standard Apple
* SCSI disk partitioning scheme. For more information see:
* http://developer.apple.com/techpubs/mac/Devices/Devices-126.html#MARKER-14-92
*/
#include <common.h>
#include <command.h>
#include <ide.h>
#include "part_dos.h"
#ifdef HAVE_BLOCK_DEVICE
/* Convert char[4] in little endian format to the host format integer
*/
static inline int le32_to_int(unsigned char *le32)
{
return ((le32[3] << 24) +
(le32[2] << 16) +
(le32[1] << 8) +
le32[0]
);
}
static inline int is_extended(int part_type)
{
return (part_type == 0x5 ||
part_type == 0xf ||
part_type == 0x85);
}
static inline int is_bootable(dos_partition_t *p)
{
return p->boot_ind == 0x80;
}
static void print_one_part(dos_partition_t *p, int ext_part_sector,
int part_num, unsigned int disksig)
{
int lba_start = ext_part_sector + le32_to_int (p->start4);
int lba_size = le32_to_int (p->size4);
printf("%3d\t%-10d\t%-10d\t%08x-%02x\t%02x%s%s\n",
part_num, lba_start, lba_size, disksig, part_num, p->sys_ind,
(is_extended(p->sys_ind) ? " Extd" : ""),
(is_bootable(p) ? " Boot" : ""));
}
static int test_block_type(unsigned char *buffer)
{
int slot;
struct dos_partition *p;
if((buffer[DOS_PART_MAGIC_OFFSET + 0] != 0x55) ||
(buffer[DOS_PART_MAGIC_OFFSET + 1] != 0xaa) ) {
return (-1);
} /* no DOS Signature at all */
p = (struct dos_partition *)&buffer[DOS_PART_TBL_OFFSET];
for (slot = 0; slot < 3; slot++) {
if (p->boot_ind != 0 && p->boot_ind != 0x80) {
if (!slot &&
(strncmp((char *)&buffer[DOS_PBR_FSTYPE_OFFSET],
"FAT", 3) == 0 ||
strncmp((char *)&buffer[DOS_PBR32_FSTYPE_OFFSET],
"FAT32", 5) == 0)) {
return DOS_PBR; /* is PBR */
} else {
return -1;
}
}
}
return DOS_MBR; /* Is MBR */
}
int test_part_dos (block_dev_desc_t *dev_desc)
{
ALLOC_CACHE_ALIGN_BUFFER(unsigned char, buffer, dev_desc->blksz);
if (dev_desc->block_read(dev_desc->dev, 0, 1, (ulong *) buffer) != 1)
return -1;
if (test_block_type(buffer) != DOS_MBR)
return -1;
return 0;
}
/* Print a partition that is relative to its Extended partition table
*/
static void print_partition_extended(block_dev_desc_t *dev_desc,
int ext_part_sector, int relative,
int part_num, unsigned int disksig)
{
ALLOC_CACHE_ALIGN_BUFFER(unsigned char, buffer, dev_desc->blksz);
dos_partition_t *pt;
int i;
if (dev_desc->block_read(dev_desc->dev, ext_part_sector, 1, (ulong *) buffer) != 1) {
printf ("** Can't read partition table on %d:%d **\n",
dev_desc->dev, ext_part_sector);
return;
}
i=test_block_type(buffer);
if (i != DOS_MBR) {
printf ("bad MBR sector signature 0x%02x%02x\n",
buffer[DOS_PART_MAGIC_OFFSET],
buffer[DOS_PART_MAGIC_OFFSET + 1]);
return;
}
if (!ext_part_sector)
disksig = le32_to_int(&buffer[DOS_PART_DISKSIG_OFFSET]);
/* Print all primary/logical partitions */
pt = (dos_partition_t *) (buffer + DOS_PART_TBL_OFFSET);
for (i = 0; i < 4; i++, pt++) {
/*
* fdisk does not show the extended partitions that
* are not in the MBR
*/
if ((pt->sys_ind != 0) &&
(ext_part_sector == 0 || !is_extended (pt->sys_ind)) ) {
print_one_part(pt, ext_part_sector, part_num, disksig);
}
/* Reverse engr the fdisk part# assignment rule! */
if ((ext_part_sector == 0) ||
(pt->sys_ind != 0 && !is_extended (pt->sys_ind)) ) {
part_num++;
}
}
/* Follows the extended partitions */
pt = (dos_partition_t *) (buffer + DOS_PART_TBL_OFFSET);
for (i = 0; i < 4; i++, pt++) {
if (is_extended (pt->sys_ind)) {
int lba_start = le32_to_int (pt->start4) + relative;
print_partition_extended(dev_desc, lba_start,
ext_part_sector == 0 ? lba_start : relative,
part_num, disksig);
}
}
return;
}
/* Print a partition that is relative to its Extended partition table
*/
static int get_partition_info_extended (block_dev_desc_t *dev_desc, int ext_part_sector,
int relative, int part_num,
int which_part, disk_partition_t *info,
unsigned int disksig)
{
ALLOC_CACHE_ALIGN_BUFFER(unsigned char, buffer, dev_desc->blksz);
dos_partition_t *pt;
int i;
if (dev_desc->block_read (dev_desc->dev, ext_part_sector, 1, (ulong *) buffer) != 1) {
printf ("** Can't read partition table on %d:%d **\n",
dev_desc->dev, ext_part_sector);
return -1;
}
if (buffer[DOS_PART_MAGIC_OFFSET] != 0x55 ||
buffer[DOS_PART_MAGIC_OFFSET + 1] != 0xaa) {
printf ("bad MBR sector signature 0x%02x%02x\n",
buffer[DOS_PART_MAGIC_OFFSET],
buffer[DOS_PART_MAGIC_OFFSET + 1]);
return -1;
}
#ifdef CONFIG_PARTITION_UUIDS
if (!ext_part_sector)
disksig = le32_to_int(&buffer[DOS_PART_DISKSIG_OFFSET]);
#endif
/* Print all primary/logical partitions */
pt = (dos_partition_t *) (buffer + DOS_PART_TBL_OFFSET);
for (i = 0; i < 4; i++, pt++) {
/*
* fdisk does not show the extended partitions that
* are not in the MBR
*/
if (((pt->boot_ind & ~0x80) == 0) &&
(pt->sys_ind != 0) &&
(part_num == which_part) &&
(is_extended(pt->sys_ind) == 0)) {
info->blksz = 512;
info->start = ext_part_sector + le32_to_int (pt->start4);
info->size = le32_to_int (pt->size4);
switch(dev_desc->if_type) {
case IF_TYPE_IDE:
case IF_TYPE_SATA:
case IF_TYPE_ATAPI:
sprintf ((char *)info->name, "hd%c%d",
'a' + dev_desc->dev, part_num);
break;
case IF_TYPE_SCSI:
sprintf ((char *)info->name, "sd%c%d",
'a' + dev_desc->dev, part_num);
break;
case IF_TYPE_USB:
sprintf ((char *)info->name, "usbd%c%d",
'a' + dev_desc->dev, part_num);
break;
case IF_TYPE_DOC:
sprintf ((char *)info->name, "docd%c%d",
'a' + dev_desc->dev, part_num);
break;
default:
sprintf ((char *)info->name, "xx%c%d",
'a' + dev_desc->dev, part_num);
break;
}
/* sprintf(info->type, "%d, pt->sys_ind); */
sprintf ((char *)info->type, "U-Boot");
info->bootable = is_bootable(pt);
#ifdef CONFIG_PARTITION_UUIDS
sprintf(info->uuid, "%08x-%02x", disksig, part_num);
#endif
return 0;
}
/* Reverse engr the fdisk part# assignment rule! */
if ((ext_part_sector == 0) ||
(pt->sys_ind != 0 && !is_extended (pt->sys_ind)) ) {
part_num++;
}
}
/* Follows the extended partitions */
pt = (dos_partition_t *) (buffer + DOS_PART_TBL_OFFSET);
for (i = 0; i < 4; i++, pt++) {
if (is_extended (pt->sys_ind)) {
int lba_start = le32_to_int (pt->start4) + relative;
return get_partition_info_extended (dev_desc, lba_start,
ext_part_sector == 0 ? lba_start : relative,
part_num, which_part, info, disksig);
}
}
return -1;
}
void print_part_dos (block_dev_desc_t *dev_desc)
{
printf("Part\tStart Sector\tNum Sectors\tUUID\t\tType\n");
print_partition_extended(dev_desc, 0, 0, 1, 0);
}
int get_partition_info_dos (block_dev_desc_t *dev_desc, int part, disk_partition_t * info)
{
return get_partition_info_extended(dev_desc, 0, 0, 1, part, info, 0);
}
#endif