2011-11-24 12:43:46 +00:00
|
|
|
/*
|
|
|
|
* 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.
|
|
|
|
*
|
|
|
|
*
|
|
|
|
*/
|
|
|
|
|
|
|
|
/**
|
|
|
|
* @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 <common.h>
|
|
|
|
#include <malloc.h>
|
|
|
|
#include <errno.h>
|
|
|
|
#include <block.h>
|
|
|
|
#include <asm/unaligned.h>
|
|
|
|
#include <disks.h>
|
2012-10-11 20:45:21 +00:00
|
|
|
#include <filetype.h>
|
2013-02-16 13:47:07 +00:00
|
|
|
#include <dma.h>
|
2013-05-24 10:54:54 +00:00
|
|
|
#include <linux/err.h>
|
2011-11-24 12:43:46 +00:00
|
|
|
|
2013-02-16 13:47:07 +00:00
|
|
|
#include "partitions/parser.h"
|
2011-11-24 12:43:46 +00:00
|
|
|
|
2013-02-16 13:47:07 +00:00
|
|
|
static LIST_HEAD(partition_parser_list);
|
2011-11-24 12:43:46 +00:00
|
|
|
|
|
|
|
/**
|
|
|
|
* 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)
|
|
|
|
{
|
2013-02-16 13:47:08 +00:00
|
|
|
char *partition_name;
|
|
|
|
int ret;
|
|
|
|
uint64_t start = part->first_sec * SECTOR_SIZE;
|
|
|
|
uint64_t size = part->size * SECTOR_SIZE;
|
2013-05-24 10:54:54 +00:00
|
|
|
struct cdev *cdev;
|
2013-02-16 13:47:08 +00:00
|
|
|
|
|
|
|
partition_name = asprintf("%s.%d", blk->cdev.name, no);
|
|
|
|
if (!partition_name)
|
|
|
|
return -ENOMEM;
|
|
|
|
dev_dbg(blk->dev, "Registering partition %s on drive %s\n",
|
|
|
|
partition_name, blk->cdev.name);
|
2013-05-24 10:54:54 +00:00
|
|
|
cdev = devfs_add_partition(blk->cdev.name,
|
2013-02-16 13:47:08 +00:00
|
|
|
start, size, 0, partition_name);
|
2013-05-24 10:54:54 +00:00
|
|
|
if (IS_ERR(cdev)) {
|
|
|
|
ret = PTR_ERR(cdev);
|
2013-02-16 13:47:08 +00:00
|
|
|
goto out;
|
2013-05-24 10:54:54 +00:00
|
|
|
}
|
2013-02-16 13:47:08 +00:00
|
|
|
|
2013-09-13 13:08:40 +00:00
|
|
|
cdev->dos_partition_type = part->dos_partition_type;
|
2015-06-17 10:40:04 +00:00
|
|
|
strcpy(cdev->partuuid, part->partuuid);
|
2013-09-13 13:08:40 +00:00
|
|
|
|
2013-02-16 13:47:08 +00:00
|
|
|
free(partition_name);
|
|
|
|
|
|
|
|
if (!part->name[0])
|
|
|
|
return 0;
|
|
|
|
|
2015-12-11 10:39:32 +00:00
|
|
|
partition_name = xasprintf("%s.%s", blk->cdev.name, part->name);
|
|
|
|
ret = devfs_create_link(cdev, partition_name);
|
|
|
|
if (ret)
|
|
|
|
dev_warn(blk->dev, "Failed to create link from %s to %s\n",
|
|
|
|
partition_name, blk->cdev.name);
|
|
|
|
free(partition_name);
|
2013-02-16 13:47:08 +00:00
|
|
|
|
2015-12-11 10:39:32 +00:00
|
|
|
return 0;
|
2013-02-16 13:47:08 +00:00
|
|
|
out:
|
|
|
|
free(partition_name);
|
2013-09-13 13:08:40 +00:00
|
|
|
return ret;
|
2011-11-24 12:43:46 +00:00
|
|
|
}
|
|
|
|
|
2013-02-16 13:47:07 +00:00
|
|
|
static struct partition_parser *partition_parser_get_by_filetype(uint8_t *buf)
|
|
|
|
{
|
|
|
|
enum filetype type;
|
|
|
|
struct partition_parser *parser;
|
|
|
|
|
|
|
|
/* first new partition table as EFI GPT */
|
2013-04-19 08:46:05 +00:00
|
|
|
type = file_detect_partition_table(buf, SECTOR_SIZE * 2);
|
2013-02-16 13:47:07 +00:00
|
|
|
|
|
|
|
list_for_each_entry(parser, &partition_parser_list, list) {
|
|
|
|
if (parser->type == type)
|
|
|
|
return parser;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* if not parser found search for old one
|
|
|
|
* so if EFI GPT not enable take it as MBR
|
|
|
|
* useful for compatibility
|
|
|
|
*/
|
2013-04-19 08:46:05 +00:00
|
|
|
type = file_detect_partition_table(buf, SECTOR_SIZE);
|
2014-11-04 07:54:01 +00:00
|
|
|
if (type == filetype_fat && !is_fat_boot_sector(buf))
|
|
|
|
type = filetype_mbr;
|
2013-02-16 13:47:07 +00:00
|
|
|
|
|
|
|
list_for_each_entry(parser, &partition_parser_list, list) {
|
|
|
|
if (parser->type == type)
|
|
|
|
return parser;
|
|
|
|
}
|
|
|
|
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
2011-11-24 12:43:46 +00:00
|
|
|
/**
|
|
|
|
* 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)
|
|
|
|
{
|
2013-02-16 13:47:08 +00:00
|
|
|
struct partition_desc *pdesc;
|
2011-11-24 12:43:46 +00:00
|
|
|
int i;
|
|
|
|
int rc = 0;
|
2013-02-16 13:47:07 +00:00
|
|
|
struct partition_parser *parser;
|
|
|
|
uint8_t *buf;
|
|
|
|
|
2013-02-16 13:47:08 +00:00
|
|
|
pdesc = xzalloc(sizeof(*pdesc));
|
2013-02-16 13:47:07 +00:00
|
|
|
buf = dma_alloc(SECTOR_SIZE * 2);
|
|
|
|
|
2013-04-04 11:59:21 +00:00
|
|
|
rc = block_read(blk, buf, 0, 2);
|
2013-02-16 13:47:07 +00:00
|
|
|
if (rc != 0) {
|
|
|
|
dev_err(blk->dev, "Cannot read MBR/partition table\n");
|
|
|
|
goto on_error;
|
|
|
|
}
|
|
|
|
|
|
|
|
parser = partition_parser_get_by_filetype(buf);
|
|
|
|
if (!parser)
|
|
|
|
goto on_error;
|
|
|
|
|
2013-02-16 13:47:08 +00:00
|
|
|
parser->parse(buf, blk, pdesc);
|
2011-11-24 12:43:46 +00:00
|
|
|
|
2013-02-16 13:47:08 +00:00
|
|
|
if (!pdesc->used_entries)
|
|
|
|
goto on_error;
|
2011-11-24 12:43:46 +00:00
|
|
|
|
|
|
|
/* at least one partition description found */
|
2013-02-16 13:47:08 +00:00
|
|
|
for (i = 0; i < pdesc->used_entries; i++) {
|
|
|
|
rc = register_one_partition(blk, &pdesc->parts[i], i);
|
2011-11-24 12:43:46 +00:00
|
|
|
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;
|
|
|
|
}
|
|
|
|
|
2013-02-16 13:47:07 +00:00
|
|
|
on_error:
|
|
|
|
dma_free(buf);
|
2013-02-16 13:47:08 +00:00
|
|
|
free(pdesc);
|
2011-11-24 12:43:46 +00:00
|
|
|
return rc;
|
|
|
|
}
|
2013-02-16 13:47:07 +00:00
|
|
|
|
|
|
|
int partition_parser_register(struct partition_parser *p)
|
|
|
|
{
|
|
|
|
list_add_tail(&p->list, &partition_parser_list);
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|