diff options
author | Nicolas Chauvet <kwizart@gmail.com> | 2013-07-23 11:49:21 +0200 |
---|---|---|
committer | Nicolas Chauvet <kwizart@gmail.com> | 2013-10-12 14:20:36 +0200 |
commit | 12655ffcfcd37698adc0c63b80ee5502115f0f29 (patch) | |
tree | cecada8537127ae84fc877c63ef49c317313233c | |
parent | 0e9f05d907a2d89f6a2e84c6252b8d08754abe43 (diff) | |
download | kernel-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.patch | 694 |
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++, §); ++ ++ 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, §); ++ 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, §); ++ 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 *); ++ |