/* * Copyright (C) 2009...2011 Juergen Beisert, Pengutronix * * 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 * */ /** * @file * @brief Generic support for partition tables on disk like media * * @todo Support for disks larger than 4 GiB * @todo Reliable size detection for BIOS based disks (on x86 only) */ #include #include #include #include #include #include struct partition { uint64_t first_sec; uint64_t size; }; struct partition_desc { int used_entries; struct partition parts[8]; }; /** * Reject values which cannot be used in Barebox * @param val Value to be check * @return 0 if value can be used in Barebox, -EINVAL if not * * @note this routine can be removed when Barebox uses file offsets larger * than 32 bit */ static int check_offset_value(uint64_t val) { #if 1 /* until Barebox can handle 64 bit offsets */ if (val > (__INT_MAX__ / SECTOR_SIZE)) return -EINVAL; #endif return 0; } /** * Guess the size of the disk, based on the partition table entries * @param dev device to create partitions for * @param table partition table * @return sector count */ static int disk_guess_size(struct device_d *dev, struct partition_entry *table) { uint64_t size = 0; int i; for (i = 0; i < 4; i++) { if (table[i].partition_start != 0) { size += get_unaligned(&table[i].partition_start) - size; size += get_unaligned(&table[i].partition_size); } } /* limit disk sector counts we can't handle due to 32 bit limits */ if (check_offset_value(size) != 0) { dev_warn(dev, "Warning: Sector count limited due to 31 bit" "contraints\n"); size = __INT_MAX__ / SECTOR_SIZE; } return (int)size; } /** * Check if a DOS like partition describes this block device * @param blk Block device to register to * @param pd Where to store the partition information * * It seems at least on ARM this routine canot use temp. stack space for the * sector. So, keep the malloc/free. */ static void __maybe_unused try_dos_partition(struct block_device *blk, struct partition_desc *pd) { uint8_t *buffer; struct partition_entry *table; struct partition pentry; int i, rc; buffer = xmalloc(SECTOR_SIZE); /* read in the MBR to get the partition table */ rc = blk->ops->read(blk, buffer, 0, 1); if (rc != 0) { dev_err(blk->dev, "Cannot read MBR/partition table\n"); goto on_error; } if ((buffer[510] != 0x55) || (buffer[511] != 0xAA)) { dev_info(blk->dev, "No partition table found\n"); goto on_error; } table = (struct partition_entry *)&buffer[446]; /* valid for x86 BIOS based disks only */ if (blk->num_blocks == 0) blk->num_blocks = disk_guess_size(blk->dev, table); for (i = 0; i < 4; i++) { pentry.first_sec = get_unaligned(&table[i].partition_start); pentry.size = get_unaligned(&table[i].partition_size); /* do we have to ignore this partition due to limitations? */ if (check_offset_value(pentry.first_sec) != 0) continue; if (check_offset_value(pentry.size) != 0) continue; if (pentry.first_sec != 0) { pd->parts[pd->used_entries].first_sec = pentry.first_sec; pd->parts[pd->used_entries].size = pentry.size; pd->used_entries++; } else { dev_dbg(blk->dev, "Skipping empty partition %d\n", i); } } on_error: free(buffer); } /** * Register one partition on the given block device * @param blk Block device to register to * @param part Partition description * @param no Partition number * @return 0 on success */ static int register_one_partition(struct block_device *blk, struct partition *part, int no) { char partition_name[19]; sprintf(partition_name, "%s.%d", blk->cdev.name, no); dev_dbg(blk->dev, "Registering partition %s on drive %s\n", partition_name, blk->cdev.name); return devfs_add_partition(blk->cdev.name, part->first_sec * SECTOR_SIZE, part->size * SECTOR_SIZE, 0, partition_name); } /** * Try to collect partition information on the given block device * @param blk Block device to examine * @return 0 most of the time, negative value else * * It is not a failure if no partition information is found */ int parse_partition_table(struct block_device *blk) { struct partition_desc pdesc = { .used_entries = 0, }; int i; int rc = 0; #ifdef CONFIG_PARTITION_DISK_DOS try_dos_partition(blk, &pdesc); #endif if (!pdesc.used_entries) return 0; /* at least one partition description found */ for (i = 0; i < pdesc.used_entries; i++) { rc = register_one_partition(blk, &pdesc.parts[i], i); if (rc != 0) dev_err(blk->dev, "Failed to register partition %d on %s (%d)\n", i, blk->cdev.name, rc); if (rc != -ENODEV) rc = 0; } return rc; }