summaryrefslogtreecommitdiff
path: root/fs/partitions
diff options
context:
space:
mode:
Diffstat (limited to 'fs/partitions')
-rw-r--r--fs/partitions/Kconfig6
-rw-r--r--fs/partitions/Makefile1
-rw-r--r--fs/partitions/check.c31
-rw-r--r--fs/partitions/check.h20
-rw-r--r--fs/partitions/cmdline.c194
-rw-r--r--fs/partitions/cmdline.h23
-rw-r--r--fs/partitions/efi.c12
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,