diff options
Diffstat (limited to 'fs/partitions')
| -rw-r--r-- | fs/partitions/Kconfig | 6 | ||||
| -rw-r--r-- | fs/partitions/Makefile | 1 | ||||
| -rw-r--r-- | fs/partitions/check.c | 31 | ||||
| -rw-r--r-- | fs/partitions/check.h | 20 | ||||
| -rw-r--r-- | fs/partitions/cmdline.c | 194 | ||||
| -rw-r--r-- | fs/partitions/cmdline.h | 23 | ||||
| -rw-r--r-- | fs/partitions/efi.c | 12 |
7 files changed, 285 insertions, 2 deletions
diff --git a/fs/partitions/Kconfig b/fs/partitions/Kconfig index cb5f0a3f1b03..6115cde646fa 100644 --- a/fs/partitions/Kconfig +++ b/fs/partitions/Kconfig @@ -249,3 +249,9 @@ config SYSV68_PARTITION partition table format used by Motorola Delta machines (using sysv68). Otherwise, say N. + +config CMDLINE_PARTITION + bool "Kernel command line partition table support" if PARTITION_ADVANCED + help + Say Y here if you would like to pass the partition table for a device + on the kernel command line. diff --git a/fs/partitions/Makefile b/fs/partitions/Makefile index 03af8eac51da..11e3a899559b 100644 --- a/fs/partitions/Makefile +++ b/fs/partitions/Makefile @@ -18,3 +18,4 @@ obj-$(CONFIG_IBM_PARTITION) += ibm.o obj-$(CONFIG_EFI_PARTITION) += efi.o obj-$(CONFIG_KARMA_PARTITION) += karma.o obj-$(CONFIG_SYSV68_PARTITION) += sysv68.o +obj-$(CONFIG_CMDLINE_PARTITION) += cmdline.o diff --git a/fs/partitions/check.c b/fs/partitions/check.c index 79fbf3f390f0..6ffb1ac7de0c 100644 --- a/fs/partitions/check.c +++ b/fs/partitions/check.c @@ -38,6 +38,7 @@ #include "efi.h" #include "karma.h" #include "sysv68.h" +#include "cmdline.h" #ifdef CONFIG_BLK_DEV_MD extern void md_autodetect_dev(dev_t dev); @@ -46,6 +47,9 @@ extern void md_autodetect_dev(dev_t dev); int warn_no_part = 1; /*This is ugly: should make genhd removable media aware*/ static int (*check_part[])(struct parsed_partitions *) = { +#ifdef CONFIG_CMDLINE_PARTITION + cmdline_partition, +#endif /* * Probe partition formats with tables at disk address 0 * that also have an ADFS boot block at 0xdc0. @@ -286,6 +290,13 @@ ssize_t part_inflight_show(struct device *dev, return sprintf(buf, "%8u %8u\n", p->in_flight[0], p->in_flight[1]); } +ssize_t part_partition_name_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct hd_struct *p = dev_to_part(dev); + return sprintf(buf, "%s\n", p->partition_name); +} + #ifdef CONFIG_FAIL_MAKE_REQUEST ssize_t part_fail_show(struct device *dev, struct device_attribute *attr, char *buf) @@ -317,6 +328,8 @@ static DEVICE_ATTR(discard_alignment, S_IRUGO, part_discard_alignment_show, NULL); static DEVICE_ATTR(stat, S_IRUGO, part_stat_show, NULL); static DEVICE_ATTR(inflight, S_IRUGO, part_inflight_show, NULL); +static DEVICE_ATTR(partition_name, S_IRUGO, part_partition_name_show, NULL); + #ifdef CONFIG_FAIL_MAKE_REQUEST static struct device_attribute dev_attr_fail = __ATTR(make-it-fail, S_IRUGO|S_IWUSR, part_fail_show, part_fail_store); @@ -330,6 +343,7 @@ static struct attribute *part_attrs[] = { &dev_attr_discard_alignment.attr, &dev_attr_stat.attr, &dev_attr_inflight.attr, + &dev_attr_partition_name.attr, #ifdef CONFIG_FAIL_MAKE_REQUEST &dev_attr_fail.attr, #endif @@ -355,10 +369,21 @@ static void part_release(struct device *dev) kfree(p); } +static int part_uevent(struct device *dev, struct kobj_uevent_env *env) +{ + struct hd_struct *part = dev_to_part(dev); + + add_uevent_var(env, "PARTN=%u", part->partno); + if (part->partition_name) + add_uevent_var(env, "PARTNAME=%s", part->partition_name); + return 0; +} + struct device_type part_type = { .name = "partition", .groups = part_attr_groups, .release = part_release, + .uevent = part_uevent, }; static void delete_partition_rcu_cb(struct rcu_head *head) @@ -400,6 +425,11 @@ static ssize_t whole_disk_show(struct device *dev, static DEVICE_ATTR(whole_disk, S_IRUSR | S_IRGRP | S_IROTH, whole_disk_show, NULL); +static void name_partition(struct hd_struct *p, const char *name) +{ + strlcpy(p->partition_name, name, GENHD_PART_NAME_SIZE); +} + struct hd_struct *add_partition(struct gendisk *disk, int partno, sector_t start, sector_t len, int flags) { @@ -682,6 +712,7 @@ rescan: disk->disk_name, p, -PTR_ERR(part)); continue; } + name_partition(part, state->parts[p].name); #ifdef CONFIG_BLK_DEV_MD if (state->parts[p].flags & ADDPART_FLAG_RAID) md_autodetect_dev(part_to_dev(part)->devt); diff --git a/fs/partitions/check.h b/fs/partitions/check.h index 8e4e103ba216..5ef9da15197b 100644 --- a/fs/partitions/check.h +++ b/fs/partitions/check.h @@ -1,6 +1,8 @@ #include <linux/pagemap.h> #include <linux/blkdev.h> +#define PART_NAME_SIZE 128 + /* * add_gd_partition adds a partitions details to the devices partition * description. @@ -12,6 +14,7 @@ struct parsed_partitions { sector_t from; sector_t size; int flags; + char name[PART_NAME_SIZE]; } parts[DISK_MAX_PARTS]; int next; int limit; @@ -30,7 +33,8 @@ static inline void *read_part_sector(struct parsed_partitions *state, } static inline void -put_partition(struct parsed_partitions *p, int n, sector_t from, sector_t size) +put_named_partition(struct parsed_partitions *p, int n, sector_t from, + sector_t size, const char *name, size_t name_size) { if (n < p->limit) { char tmp[1 + BDEVNAME_SIZE + 10 + 1]; @@ -39,8 +43,22 @@ put_partition(struct parsed_partitions *p, int n, sector_t from, sector_t size) p->parts[n].size = size; snprintf(tmp, sizeof(tmp), " %s%d", p->name, n); strlcat(p->pp_buf, tmp, PAGE_SIZE); + if (name) { + if (name_size > PART_NAME_SIZE - 1) + name_size = PART_NAME_SIZE - 1; + memcpy(p->parts[n].name, name, name_size); + p->parts[n].name[name_size] = 0; + snprintf(tmp, sizeof(tmp), " (%s)", p->parts[n].name); + strlcat(p->pp_buf, tmp, PAGE_SIZE); + } } } +static inline void +put_partition(struct parsed_partitions *p, int n, sector_t from, sector_t size) +{ + put_named_partition(p, n, from, size, NULL, 0); +} + extern int warn_no_part; diff --git a/fs/partitions/cmdline.c b/fs/partitions/cmdline.c new file mode 100644 index 000000000000..7b3a8407e385 --- /dev/null +++ b/fs/partitions/cmdline.c @@ -0,0 +1,194 @@ +/* + * fs/partitions/cmdline.c + * + * Copyright (C) 2010 Google, Inc. + * Author: Colin Cross <ccross@android.com> + * + * This software is licensed under the terms of the GNU General Public + * License version 2, as published by the Free Software Foundation, and + * may be copied, distributed, and modified under those terms. + * + * 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. + * + */ + +/*#define DEBUG 1*/ + +#include <linux/init.h> +#include <linux/fs.h> +#include <linux/blkdev.h> +#include <linux/slab.h> + +#include "check.h" +#include "cmdline.h" + +static char *cmdline; +static int cmdline_parsed; +static struct part_device *cmdline_device; + +struct part { + char *name; + unsigned long from; + unsigned long size; + unsigned long sector_size; + struct part *next_part; +}; + +struct part_device { + char *name; + struct part *first_part; + struct part_device *next_device; +}; + + +/* Passed a string like: + * system:3600:10000:800 + */ +static struct part *parse_partition(char *s, int alloc_size, void **alloc) +{ + char *p; + struct part *this_part; + pr_debug("%s: '%s', %d, %p\n", __func__, s, alloc_size, alloc); + + if (*alloc == NULL) + *alloc = kzalloc(alloc_size, GFP_KERNEL); + + this_part = *alloc; + *alloc += sizeof(*this_part); + + /* Name */ + p = strchr(s, ':'); + if (!p) + return this_part; + *p = 0; + this_part->name = s; + + /* From */ + s = p+1; + p = strchr(s, ':'); + if (!p) + return this_part; + *p = 0; + this_part->from = simple_strtoul(s, NULL, 16); + + /* Size */ + s = p+1; + p = strchr(s, ':'); + if (!p) + return this_part; + *p = 0; + this_part->size = simple_strtoul(s, NULL, 16); + + /* Sector size */ + s = p+1; + this_part->sector_size = simple_strtoul(s, NULL, 16); + pr_debug("%s: Found %s %lu %lu %lu\n", __func__, this_part->name, + this_part->from, this_part->size, this_part->sector_size); + return this_part; +} + +/* Passed a string like: + * system:3600:10000:800,cache:13600:4000:800,userdata:17600:80000:800 + * Could be an empty string + */ +static struct part *parse_partition_list(char *s, int alloc_size, void **alloc) +{ + char *p; + struct part *this_part; + struct part *next_part = NULL; + pr_debug("%s: '%s', %d, %p\n", __func__, s, alloc_size, alloc); + + alloc_size += sizeof(struct part); + p = strchr(s, ','); + if (p) { + *p = 0; + next_part = parse_partition_list(p+1, alloc_size, alloc); + if (!next_part) + BUG(); + } + this_part = parse_partition(s, alloc_size, alloc); + this_part->next_part = next_part; + return this_part; +} +/* Passed a string like: + * sdhci.0=system:3600:10000:800,cache:13600:4000:800,userdata:17600:80000:800 + */ +static struct part_device *parse_device(char *s, int alloc_size, void **alloc) { + char *p; + struct part *part; + struct part_device *device; + + pr_debug("%s: '%s', %d, %p\n", __func__, s, alloc_size, alloc); + p = strchr(s, '='); + if (!p) + return NULL; + *p = 0; + alloc_size += sizeof(struct part_device); + part = parse_partition_list(p+1, alloc_size, alloc); + if (part) { + device = *alloc; + *alloc += sizeof(struct part_device); + device->name = s; + device->first_part = part; + } + return device; +} + + +static void parse_cmdline(void) { + char *s = cmdline; + void *alloc = 0; + if (cmdline_parsed) + return; + cmdline_device = parse_device(s, 0, &alloc); + cmdline_parsed = 1; +} + + +int copy_partitions_to_state(struct part_device *device, + struct parsed_partitions *state, unsigned int ssz) +{ + int i = 0; + struct part *part = device->first_part; + while (part) { + sector_t from = part->from * (part->sector_size / ssz); + sector_t size = part->size * (part->sector_size / ssz); + put_named_partition(state, i+1, from, size, part->name, + strlen(part->name)); + i++; + part = part->next_part; + } + return i; +} + +int cmdline_partition(struct parsed_partitions *state) +{ + struct block_device *bdev = state->bdev; + unsigned int ssz = bdev_logical_block_size(bdev); + parse_cmdline(); + pr_debug("%s: %s\n", __func__, dev_name(disk_to_dev(bdev->bd_disk))); + + if (!cmdline_device) + return 0; + + if (strcmp(cmdline_device->name, dev_name(disk_to_dev(bdev->bd_disk)))) + return 0; + + /* We have a command line partition that matches this device */ + copy_partitions_to_state(cmdline_device, state, ssz); + return 1; +} + + +__init static int cmdline_partition_setup(char *s) +{ + cmdline = s; + return 1; +} + +__setup("tegrapart=", cmdline_partition_setup); + + diff --git a/fs/partitions/cmdline.h b/fs/partitions/cmdline.h new file mode 100644 index 000000000000..68dfd2c51b28 --- /dev/null +++ b/fs/partitions/cmdline.h @@ -0,0 +1,23 @@ +/* + * fs/partitions/cmdline.h + * + * Copyright (C) 2010 Google, Inc. + * Author: Colin Cross <ccross@android.com> + * + * This software is licensed under the terms of the GNU General Public + * License version 2, as published by the Free Software Foundation, and + * may be copied, distributed, and modified under those terms. + * + * 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. + * + */ + +#ifndef FS_PART_CMDLINE_H +#define FS_PART_CMDLINE_H + +extern int cmdline_partition(struct parsed_partitions *state); + +#endif diff --git a/fs/partitions/efi.c b/fs/partitions/efi.c index dbb44d4bb8a7..35bbbd279d73 100644 --- a/fs/partitions/efi.c +++ b/fs/partitions/efi.c @@ -96,6 +96,7 @@ #include <linux/crc32.h> #include <linux/math64.h> #include <linux/slab.h> +#include <linux/nls.h> #include "check.h" #include "efi.h" @@ -617,11 +618,20 @@ int efi_partition(struct parsed_partitions *state) u64 start = le64_to_cpu(ptes[i].starting_lba); u64 size = le64_to_cpu(ptes[i].ending_lba) - le64_to_cpu(ptes[i].starting_lba) + 1ULL; + u8 name[sizeof(ptes->partition_name) / sizeof(efi_char16_t)]; + int len; if (!is_pte_valid(&ptes[i], last_lba(state->bdev))) continue; - put_partition(state, i+1, start * ssz, size * ssz); + len = utf16s_to_utf8s(ptes[i].partition_name, + sizeof(ptes[i].partition_name) / + sizeof(efi_char16_t), + UTF16_LITTLE_ENDIAN, name, + sizeof(name)); + + put_named_partition(state, i+1, start * ssz, size * ssz, + name, len); /* If this is a RAID volume, tell md */ if (!efi_guidcmp(ptes[i].partition_type_guid, |
