===================================================================
@@ -249,3 +249,28 @@ 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
+ depends on MACH_PAZ00 && !EMBEDDED_MMC_START_OFFSET
+ 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.
===================================================================
@@ -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
===================================================================
@@ -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,13 @@ static int (*check_part[])(struct parsed
* 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
===================================================================
@@ -0,0 +1,227 @@
+/*
+ * 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++, §);
+
+ 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)
+ printk(KERN_INFO "\n");
+ printk(KERN_INFO "nvtegrapart: hidden_parts = %s\n", hidden_parts_str);
+
+ /* log partitions */
+ part = parts;
+ for (i = 1; i <= 23; i++) {
+ if (part->valid)
+ printk(KERN_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++;
+ }
+ printk("\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);
===================================================================
@@ -0,0 +1,6 @@
+/*
+ * fs/partitions/nvtegra.h
+ *
+ */
+
+int nvtegra_partition(struct parsed_partitions *);
===================================================================
@@ -0,0 +1,357 @@
+/*
+ * 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 = NULL;
+
+
+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) {
+ printk(KERN_INFO "tegra_msdos_parse_extended: loopcnt>2. exit\n");
+ return;
+ }
+
+ printk(KERN_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, §);
+ if (!data) {
+ printk(KERN_INFO "tegra_msdos_parse_extended: read error. exit\n");
+ return;
+ }
+
+ if (!msdos_magic_present(data + 510)) {
+ printk(KERN_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;
+ }
+
+ printk(KERN_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;
+ }
+
+ printk(KERN_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)) {
+ printk(KERN_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:
+ printk(KERN_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;
+
+ printk(KERN_INFO "tegra_msdos_parse: mbr_offset=%llu\n", mbr_offset);
+
+ data = read_dev_sector(bdev, mbr_offset, §);
+ if (!data) {
+ printk(KERN_INFO "tegra_msdos_parse: read error. exit\n");
+ return -1;
+ }
+ if (!msdos_magic_present(data + 510)) {
+ printk(KERN_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
+ printk("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++) {
+ u64 start = START_SECT(p)*sector_size; // 0015f800 1439744
+ u64 size = NR_SECTS(p)*sector_size; // 01c41400 29627392
+ printk(KERN_INFO "tegra_msdos_parse: slot %d, start=%llu size=%llu\n", slot, start, size);
+ if (!size)
+ continue;
+ if (is_extended_partition(p)) {
+ printk(KERN_INFO "tegra_msdos_parse: slot %d extended partition\n", slot);
+ //put_partition(state, state->next++, start+mbr_offset, size == 1 ? 1 : 2);
+ printk(" <");
+ tegra_msdos_parse_extended(state, state->bdev, mbr_offset, start, size);
+ printk(" >");
+ continue;
+ }
+ printk(KERN_INFO "tegra_msdos_parse: put_partition\n");
+ put_partition(state, state->next++, start+mbr_offset, size);
+ }
+
+ printk("\n");
+ printk(KERN_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;
+
+ printk(KERN_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")) {
+ printk(KERN_INFO "parse_tegrapart: mbr start=%llu\n", offset);
+ return tegra_msdos_parse(state, state->bdev, offset);
+ }
+
+ printk(KERN_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);
+
+ printk(KERN_INFO "parse_tegrapart: done without mbr\n");
+ return ret;
+}
+
+
+
+int
+tegrapart_partition(struct parsed_partitions *state)
+{
+ printk(KERN_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) {
+ printk(KERN_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);
+
===================================================================
@@ -0,0 +1,3 @@
+
+int tegrapart_partition(struct parsed_partitions *);
+