summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorNicolas Chauvet <kwizart@gmail.com>2013-07-23 11:49:21 +0200
committerNicolas Chauvet <kwizart@gmail.com>2013-10-12 14:20:36 +0200
commit12655ffcfcd37698adc0c63b80ee5502115f0f29 (patch)
treececada8537127ae84fc877c63ef49c317313233c
parent0e9f05d907a2d89f6a2e84c6252b8d08754abe43 (diff)
downloadkernel-12655ffcfcd37698adc0c63b80ee5502115f0f29.tar.gz
kernel-12655ffcfcd37698adc0c63b80ee5502115f0f29.tar.xz
kernel-12655ffcfcd37698adc0c63b80ee5502115f0f29.zip
Add nvtegra partition support
-rw-r--r--0000-block_add_support_for_nvidia_tegra_partitions.patch694
1 files changed, 694 insertions, 0 deletions
diff --git a/0000-block_add_support_for_nvidia_tegra_partitions.patch b/0000-block_add_support_for_nvidia_tegra_partitions.patch
new file mode 100644
index 00000000..7f61929c
--- /dev/null
+++ b/0000-block_add_support_for_nvidia_tegra_partitions.patch
@@ -0,0 +1,694 @@
+commit 602b7704e23c5ba891af9b32209a7e242d378f69
+Author: Marc Dietrich <marvin24@gmx.de>
+Date: Tue Oct 23 18:31:16 2012 +0200
+
+ block: add support for nvidia/tegra partitions
+
+ This adds support for NVIDIA style parition table found on many
+ Tegra android devices. Alternatively, it adds support for adding
+ partitions on the kernel command line.
+
+ Signed-off-by: Marc Dietrich <marvin24@gmx.de>
+
+diff --git a/block/partitions/Kconfig b/block/partitions/Kconfig
+index 75a54e1..f819729 100644
+--- a/block/partitions/Kconfig
++++ b/block/partitions/Kconfig
+@@ -249,3 +249,27 @@ config SYSV68_PARTITION
+ partition table format used by Motorola Delta machines (using
+ sysv68).
+ Otherwise, say N.
++
++config TEGRAPART_PARTITION
++ bool "tegrapart cmdline partition table support" if PARTITION_ADVANCED
++ default y
++ help
++ Add support for extended tegrapart= cmdline
++
++config NVTEGRA_PARTITION
++ bool "Nvidia Tegra partition table support" if PARTITION_ADVANCED
++ default n
++ help
++ Add support for undocummented partition table format used by Tegra2
++ systems.
++
++config NVTEGRA_HIDE_PARTS
++ string "Hidden NVidia Tegra partitions"
++ depends on NVTEGRA_PARTITION
++ default ""
++ help
++ Fill here the list of partitions you want to hide to the kennel.
++ This is likely used to hide boot configuration partition,
++ bootloader, and others which does not contain valid filesystem.
++ list each partition name (the 3,4 letters identifier) separated
++ by commas.
+diff --git a/block/partitions/Makefile b/block/partitions/Makefile
+index 03af8ea..28653bd 100644
+--- a/block/partitions/Makefile
++++ b/block/partitions/Makefile
+@@ -18,3 +18,5 @@ 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_NVTEGRA_PARTITION) += nvtegra.o
++obj-$(CONFIG_TEGRAPART_PARTITION) += tegrapart.o
+diff --git a/block/partitions/check.c b/block/partitions/check.c
+index bc90867..c4e22b1 100644
+--- a/block/partitions/check.c
++++ b/block/partitions/check.c
+@@ -19,6 +19,8 @@
+
+ #include "check.h"
+
++#include "tegrapart.h"
++#include "nvtegra.h"
+ #include "acorn.h"
+ #include "amiga.h"
+ #include "atari.h"
+@@ -41,6 +43,12 @@ static int (*check_part[])(struct parsed_partitions *) = {
+ * Probe partition formats with tables at disk address 0
+ * that also have an ADFS boot block at 0xdc0.
+ */
++#ifdef CONFIG_NVTEGRA_PARTITION
++ nvtegra_partition,
++#endif
++#ifdef CONFIG_TEGRAPART_PARTITION
++ tegrapart_partition,
++#endif
+ #ifdef CONFIG_ACORN_PARTITION_ICS
+ adfspart_check_ICS,
+ #endif
+diff --git a/block/partitions/nvtegra.c b/block/partitions/nvtegra.c
+new file mode 100644
+index 0000000..7c15d58
+--- /dev/null
++++ b/block/partitions/nvtegra.c
+@@ -0,0 +1,226 @@
++/*
++ * fs/partitions/nvtegria.c
++ * Copyright (c) 2010 Gilles Grandou
++ *
++ * Nvidia uses for its Tegra2 SOCs a proprietary partition system which is
++ * unfortunately undocumented.
++ *
++ * Typically a Tegra2 system embedds an internal Flash memory (MTD or MMC)
++ * The bottom of this memory contains the initial bootstrap code which
++ * implements a communication protocol (typically over usb) which allows a
++ * host system (through a tool called nvflash) to access, read, write and
++ * partition the internal flash.
++ *
++ * The partition table format is not publicaly documented, and usually
++ * partition description is passed to kernel through the command line
++ * (with tegrapart= argument whose support is available in nv-tegra tree,
++ * see http://nv-tegra.nvidia.com/ )
++ *
++ * Rewriting partition table or even switching to a standard msdos is
++ * theorically possible, but it would mean loosing support from nvflash
++ * and from bootloader, while no real alternative exists yet.
++ *
++ * Partition table format has been reverse-engineered from analysis of
++ * an existing partition table as found on Toshiba AC100/Dynabook AZ. All
++ * fields have been guessed and there is no guarantee that it will work
++ * in all situation nor in all other Tegra2 based products.
++ *
++ *
++ * The standard partitions which can be found on an AC100 are the next
++ * ones:
++ *
++ * sector size = 2048 bytes
++ *
++ * Id Name Start Size Comment
++ * sector sectors
++ *
++ * 1 0 1024 unreachable (bootstrap ?)
++ * 2 BCT 1024 512 Boot Configuration Table
++ * 3 PT 1536 256 Partition Table
++ * 4 EBT 1792 1024 Boot Loader
++ * 5 SOS 2816 2560 Recovery Kernel
++ * 6 LNX 5376 4096 System Kernel
++ * 7 MBR 9472 512 MBR - msdos partition table
++ * for the rest of the disk
++ * 8 APP 9984 153600 OS root filesystem
++ * ...
++ *
++ * the 1024 first sectors are hidden to the hardware one booted
++ * (so 1024 should be removed from numbers found in the partition
++ * table)
++ *
++ * There is no standard magic value which can be used for sure
++ * to say that there is a nvtegra partition table at sector 512.
++ * Hence, as an heuristic, we check if the value which would be
++ * found for the BCT partition entry are valid.
++ *
++ */
++
++#include "check.h"
++#include "nvtegra.h"
++
++struct nvtegra_partinfo {
++ unsigned id;
++ char name[4];
++ unsigned type;
++ unsigned unk1[2];
++ char name2[4];
++ unsigned unk2[4];
++ unsigned start;
++ unsigned unk3;
++ unsigned size;
++ unsigned unk4[7];
++};
++
++struct nvtegra_ptable {
++ unsigned unknown[18];
++ struct nvtegra_partinfo partinfo_bct;
++ struct nvtegra_partinfo partinfo[23];
++};
++
++struct partinfo {
++ int valid;
++ char name[4];
++ unsigned start;
++ unsigned size;
++};
++
++char *hidden_parts_str = CONFIG_NVTEGRA_HIDE_PARTS;
++
++static size_t
++read_dev_bytes(struct block_device *bdev, unsigned sector, char *buffer,
++ size_t count)
++{
++ size_t totalreadcount = 0;
++
++ if (!bdev || !buffer)
++ return 0;
++
++ while (count) {
++ int copied = 512;
++ Sector sect;
++ unsigned char *data = read_dev_sector(bdev, sector++, &sect);
++
++ if (!data)
++ break;
++ if (copied > count)
++ copied = count;
++ memcpy(buffer, data, copied);
++ put_dev_sector(sect);
++ buffer += copied;
++ totalreadcount += copied;
++ count -= copied;
++ }
++ return totalreadcount;
++}
++
++int nvtegra_partition(struct parsed_partitions *state)
++{
++ struct nvtegra_ptable *pt;
++ struct nvtegra_partinfo *p;
++ struct partinfo *parts;
++ struct partinfo *part;
++ unsigned len;
++ int count;
++ int i;
++ char *s;
++
++ pt = kzalloc(2048, GFP_KERNEL);
++ if (!pt)
++ return -1;
++
++ if (read_dev_bytes(state->bdev, 2048, (char *)pt, 2048) != 2048) {
++ kfree(pt);
++ return 0;
++ }
++
++ /* check if BCT partinfo looks correct */
++ p = &pt->partinfo_bct;
++ if (p->id != 2)
++ return 0;
++ if (memcmp(p->name, "BCT\0", 4))
++ return 0;
++ if (p->type != 18)
++ return 0;
++ if (memcmp(p->name2, "BCT\0", 4))
++ return 0;
++ if (p->start != 0)
++ return 0;
++ if (p->size != 1536)
++ return 0;
++
++ parts = kzalloc(23 * sizeof(struct partinfo), GFP_KERNEL);
++ if (!parts)
++ return -1;
++
++ /* walk the partition table */
++ p = pt->partinfo;
++ part = parts;
++
++ count = 1;
++ for (i = 1; (p->id < 128) && (i <= 23); i++) {
++ memcpy(part->name, p->name, 4);
++ part->valid = 1;
++ part->start = p->start * 4 - 0x1000;
++ part->size = p->size * 4;
++ p++;
++ part++;
++ }
++
++ /* hide partitions */
++ s = hidden_parts_str;
++ while (*s) {
++ len = strcspn(s, ",: ");
++ part = parts;
++
++ for (i = 1; i <= 23; i++) {
++ if (part->valid) {
++ if (!strncmp(part->name, s, len)
++ && ((len >= 4)
++ || (part->name[len] == '\0'))) {
++ part->valid = 0;
++ break;
++ }
++ }
++ part++;
++ }
++ s += len;
++ s += strspn(s, ",: ");
++ }
++
++ if (*hidden_parts_str)
++ pr_info("\n");
++ pr_info("nvtegrapart: hidden_parts = %s\n", hidden_parts_str);
++
++ /* log partitions */
++ part = parts;
++ for (i = 1; i <= 23; i++) {
++ if (part->valid)
++ pr_info("nvtegrapart: #%d [%-4.4s] start=%u size=%u\n",
++ i, part->name, part->start, part->size);
++ part++;
++ }
++
++ /* finally register valid partitions */
++ count = 1;
++ part = parts;
++ for (i = 1; i <= 23; i++) {
++ if (part->valid)
++ put_partition(state, count++, part->start, part->size);
++ part++;
++ }
++ pr_info("\n");
++
++ kfree(parts);
++ kfree(pt);
++ return 1;
++}
++
++static int __init nvtegra_hideparts_setup(char *options)
++{
++ if (options)
++ hidden_parts_str = options;
++ return 0;
++}
++
++__setup("nvtegra_hideparts=", nvtegra_hideparts_setup);
+diff --git a/block/partitions/nvtegra.h b/block/partitions/nvtegra.h
+new file mode 100644
+index 0000000..ece7a35
+--- /dev/null
++++ b/block/partitions/nvtegra.h
+@@ -0,0 +1,6 @@
++/*
++ * fs/partitions/nvtegra.h
++ *
++ */
++
++int nvtegra_partition(struct parsed_partitions *);
+diff --git a/block/partitions/tegrapart.c b/block/partitions/tegrapart.c
+new file mode 100644
+index 0000000..5a23db2
+--- /dev/null
++++ b/block/partitions/tegrapart.c
+@@ -0,0 +1,355 @@
++/*
++ * fs/partitions/tegrapart.c
++ * Copyright (c) 2011 Gilles Grandou
++ *
++ * msdos mbr code taken from msdos.c
++ * Code extracted from drivers/block/genhd.c
++ * Copyright (C) 1991-1998 Linus Torvalds
++ * Re-organised Feb 1998 Russell King
++ *
++ * Build partitions for mmc0 using:fs_partitions_tegrapart
++ *
++ * - tegrapart= arguments from cmdline
++ * This allows to create partitions for boot partitions
++ * Unfortunately tegrapart does not list all available partitions
++ * so we cannot solely rely on it.
++ *
++ * - MBR partition for others
++ * The code used to read MBR is taken from msdos.c, cleaned of
++ * specific useless stuff (EFI, BSD, ...), and modified to take
++ * the initial MBR offset into account.
++ *
++ */
++
++#include <linux/init.h>
++#include <linux/kernel.h>
++#include <linux/msdos_fs.h>
++
++#include "check.h"
++#include "msdos.h"
++
++#include <asm/unaligned.h>
++#include <asm/div64.h>
++
++char *partition_list;
++
++unsigned strtohex(char *ptr)
++{
++ unsigned x = 0;
++ unsigned c;
++
++ for (;;) {
++ c = *ptr++ | 0x20;
++ if ((c >= '0') && (c <= '9'))
++ x = (x<<4) | (c & 0xf);
++ else if ((c >= 'a') && (c <= 'f'))
++ x = (x<<4) | (c - 87);
++ else
++ return x;
++ }
++}
++
++#define SYS_IND(p) (get_unaligned(&p->sys_ind))
++
++#define NR_SECTS(p) ({ __le32 __a = get_unaligned(&p->nr_sects); \
++ le32_to_cpu(__a); \
++ })
++
++#define START_SECT(p) ({ __le32 __a = get_unaligned(&p->start_sect); \
++ le32_to_cpu(__a); \
++ })
++
++static inline int is_extended_partition(struct partition *p)
++{
++ return (SYS_IND(p) == DOS_EXTENDED_PARTITION ||
++ SYS_IND(p) == WIN98_EXTENDED_PARTITION ||
++ SYS_IND(p) == LINUX_EXTENDED_PARTITION);
++}
++
++#define MSDOS_LABEL_MAGIC1 0x55
++#define MSDOS_LABEL_MAGIC2 0xAA
++
++static inline int msdos_magic_present(unsigned char *p)
++{
++ return (p[0] == MSDOS_LABEL_MAGIC1 && p[1] == MSDOS_LABEL_MAGIC2);
++}
++
++
++static void tegra_msdos_parse_extended(struct parsed_partitions *state,
++ struct block_device *bdev,
++ u64 mbr_offset, u64 first_sector,
++ u64 first_size)
++{
++ struct partition *p;
++ Sector sect;
++ unsigned char *data;
++ u64 this_sector, this_size;
++ int sector_size = bdev_logical_block_size(bdev) / 512;
++ int loopct = 0; /* number of links followed
++ without finding a data partition */
++ int i;
++
++ this_sector = first_sector;
++ this_size = first_size;
++
++ while (1) {
++ if (++loopct > 2) {
++ pr_info("tegra_msdos_parse_extended: loopcnt>2. exit\n");
++ return;
++ }
++
++ pr_info("tegra_msdos_parse_extended: read part sector, start=%llu+%llu size=%llu\n",
++ mbr_offset, this_sector, this_size);
++
++ data = read_dev_sector(bdev, mbr_offset+this_sector, &sect);
++ if (!data) {
++ pr_info("tegra_msdos_parse_extended: read error. exit\n");
++ return;
++ }
++
++ if (!msdos_magic_present(data + 510)) {
++ pr_info("tegra_msdos_parse_extended: no msdos magic. exit\n");
++ goto done;
++ }
++
++ p = (struct partition *) (data + 0x1be);
++
++ /*
++ * First process the data partition(s)
++ */
++ for (i = 0; i < 4; i++, p++) {
++ u64 offs, size, next;
++
++ if (!NR_SECTS(p) || is_extended_partition(p))
++ continue;
++
++ offs = START_SECT(p)*sector_size;
++ size = NR_SECTS(p)*sector_size;
++ next = this_sector + offs;
++ if (i >= 2) {
++ if (offs + size > this_size)
++ continue;
++ if (next < first_sector)
++ continue;
++ if (next + size > first_sector + first_size)
++ continue;
++ }
++
++ pr_info("tegra_msdos_parse_extended: put_partition %d start=%llu+%llu size=%llu\n",
++ state->next, mbr_offset, next, size);
++ put_partition(state, state->next++, mbr_offset+next,
++ size);
++ loopct = 0;
++ }
++
++ pr_info("tegra_msdos_parse_extended: done with this sector\n");
++
++ p -= 4;
++ for (i = 0; i < 4; i++, p++)
++ if (NR_SECTS(p) && is_extended_partition(p)) {
++ pr_info("tegra_msdos_parse_extended: extended part slot %d\n",
++ i + 1);
++ break;
++ }
++
++ if (i == 4)
++ goto done; /* nothing left to do */
++
++ this_sector = first_sector + START_SECT(p) * sector_size;
++ this_size = NR_SECTS(p) * sector_size;
++ put_dev_sector(sect);
++ }
++done:
++ pr_info("tegra_msdos_parse_extended: done\n");
++ put_dev_sector(sect);
++}
++
++int tegra_msdos_parse(struct parsed_partitions *state,
++ struct block_device *bdev, u64 mbr_offset)
++{
++ int sector_size = bdev_logical_block_size(bdev) / 512;
++ Sector sect;
++ unsigned char *data;
++ struct partition *p;
++ int slot;
++
++ pr_info("tegra_msdos_parse: mbr_offset=%llu\n", mbr_offset);
++
++ data = read_dev_sector(bdev, mbr_offset, &sect);
++ if (!data) {
++ pr_info("tegra_msdos_parse: read error. exit\n");
++ return -1;
++ }
++ if (!msdos_magic_present(data + 510)) {
++ pr_info("tegra_msdos_parse: no msdos magic\n");
++ put_dev_sector(sect);
++ return 0;
++ }
++
++ p = (struct partition *) (data + 0x1be);
++ for (slot = 1; slot <= 4; slot++, p++) {
++ if (p->boot_ind != 0 && p->boot_ind != 0x80) {
++ /* 0x1be,0x1ce,0x1de,0x1fe */
++ pr_info("tegra_msdos_parse: slot %d, boot_ind=0x%x. exit\n",
++ slot, p->boot_ind);
++ put_dev_sector(sect);
++ return 0;
++ }
++ }
++
++ p = (struct partition *) (data + 0x1be);
++
++ for (slot = 1; slot <= 4; slot++, p++) {
++ /* 0015f800 1439744 */
++ u64 start = START_SECT(p)*sector_size;
++ /* 01c41400 29627392 */
++ u64 size = NR_SECTS(p)*sector_size;
++ pr_info("tegra_msdos_parse: slot %d, start=%llu size=%llu\n",
++ slot, start, size);
++ if (!size)
++ continue;
++ if (is_extended_partition(p)) {
++ pr_info("tegra_msdos_parse: slot %d extended partition\n",
++ slot);
++ /* put_partition(state, state->next++, start+mbr_offset,
++ size == 1 ? 1 : 2); */
++ pr_info(" <");
++ tegra_msdos_parse_extended(state, state->bdev,
++ mbr_offset, start, size);
++ pr_info(" >");
++ continue;
++ }
++ pr_info("tegra_msdos_parse: put_partition\n");
++ put_partition(state, state->next++, start+mbr_offset, size);
++ }
++
++ pr_info("\n");
++ pr_info("tegra_msdos_parse: done\n");
++
++ put_dev_sector(sect);
++ return 1;
++}
++
++#define STATE_NAME 0
++#define STATE_OFFSET 1
++#define STATE_SIZE 2
++#define STATE_BLOCKSIZE 3
++
++int parse_tegrapart(struct parsed_partitions *state)
++{
++ char *ptr;
++ char *pstart;
++ int pstate;
++ char name[8];
++ u64 offset, size, blocksize, kblocksize;
++ int done;
++ int ret = 0;
++
++ pr_info("parse_tegrapart: tegrapart=%s\n", partition_list);
++
++ kblocksize = bdev_logical_block_size(state->bdev);
++
++ ptr = partition_list;
++ pstart = ptr;
++ pstate = STATE_NAME;
++ name[0] = '\0';
++ offset = 0;
++ size = 0;
++ blocksize = 0;
++ done = 0;
++ do {
++ switch (pstate) {
++ case STATE_NAME:
++ if (*ptr == ':') {
++ int len = ptr-pstart;
++ if (len > 7)
++ len = 7;
++ memcpy(name, pstart, len);
++ name[len] = '\0';
++ pstate++;
++ pstart = ptr + 1;
++ }
++ break;
++ case STATE_OFFSET:
++ if (*ptr == ':') {
++ offset = strtohex(pstart);
++ pstate++;
++ pstart = ptr + 1;
++ }
++ break;
++ case STATE_SIZE:
++ if (*ptr == ':') {
++ size = strtohex(pstart);
++ pstate++;
++ pstart = ptr + 1;
++ }
++ break;
++ case STATE_BLOCKSIZE:
++ if (*ptr == '\0')
++ done = 1;
++ if ((*ptr == ',') || (*ptr == '\0')) {
++ blocksize = strtohex(pstart);
++ pstate = STATE_NAME;
++ pstart = ptr + 1;
++
++ offset = offset*blocksize;
++ size = size*blocksize;
++ do_div(offset, kblocksize);
++ do_div(size, kblocksize);
++
++ if (!strcasecmp(name, "mbr")) {
++ pr_info("parse_tegrapart: mbr start=%llu\n",
++ offset);
++ return tegra_msdos_parse(state,
++ state->bdev, offset);
++ }
++
++ pr_info("parse_tegrapart: part #%d [%s] start=%llu size=%llu\n",
++ state->next, name, offset, size);
++
++ put_partition(state, state->next++, offset,
++ size);
++ ret = 1;
++
++ }
++ break;
++ }
++ ptr++;
++ } while (!done);
++
++ pr_info("parse_tegrapart: done without mbr\n");
++ return ret;
++}
++
++int tegrapart_partition(struct parsed_partitions *state)
++{
++ pr_info("tegrapart_partition: state->bdev->bd_disk->disk_name = %s\n",
++ state->bdev->bd_disk->disk_name);
++
++/* if (strcmp(state->bdev->bd_disk->disk_name, "mmcblk1")) {
++ printk(KERN_INFO "tegrapart_partition: exit\n");
++ return 0;
++ }
++*/
++ if (state->parts[0].size) {
++ pr_info("tegrapart_partition: part[0].size = 0. exit\n");
++ return 0;
++ }
++
++ state->next = 1;
++
++ if (partition_list)
++ return parse_tegrapart(state);
++ else
++ return 0;
++}
++
++static int __init partition_setup(char *options)
++{
++ if (options && *options && !partition_list)
++ partition_list = options;
++
++ return 0;
++}
++
++__setup("tegrapart=", partition_setup);
+diff --git a/block/partitions/tegrapart.h b/block/partitions/tegrapart.h
+new file mode 100644
+index 0000000..96f1c4c
+--- /dev/null
++++ b/block/partitions/tegrapart.h
+@@ -0,0 +1,3 @@
++
++int tegrapart_partition(struct parsed_partitions *);
++