From patchwork Mon Mar 23 16:34:22 2020 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Dmitry Osipenko X-Patchwork-Id: 211073 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org X-Spam-Level: X-Spam-Status: No, score=-9.8 required=3.0 tests=DKIM_SIGNED,DKIM_VALID, DKIM_VALID_AU,FREEMAIL_FORGED_FROMDOMAIN,FREEMAIL_FROM, HEADER_FROM_DIFFERENT_DOMAINS, INCLUDES_PATCH, MAILING_LIST_MULTI, SIGNED_OFF_BY, SPF_HELO_NONE,SPF_PASS,USER_AGENT_GIT autolearn=unavailable autolearn_force=no version=3.4.0 Received: from mail.kernel.org (mail.kernel.org [198.145.29.99]) by smtp.lore.kernel.org (Postfix) with ESMTP id E45C6C54FD0 for ; Mon, 23 Mar 2020 16:36:14 +0000 (UTC) Received: from vger.kernel.org (vger.kernel.org [209.132.180.67]) by mail.kernel.org (Postfix) with ESMTP id BB93020658 for ; Mon, 23 Mar 2020 16:36:14 +0000 (UTC) Authentication-Results: mail.kernel.org; dkim=pass (2048-bit key) header.d=gmail.com header.i=@gmail.com header.b="Ef2iJrYZ" Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1727277AbgCWQfh (ORCPT ); Mon, 23 Mar 2020 12:35:37 -0400 Received: from mail-lj1-f194.google.com ([209.85.208.194]:43043 "EHLO mail-lj1-f194.google.com" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1727501AbgCWQfh (ORCPT ); Mon, 23 Mar 2020 12:35:37 -0400 Received: by mail-lj1-f194.google.com with SMTP id g27so6507091ljn.10; Mon, 23 Mar 2020 09:35:35 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20161025; h=from:to:cc:subject:date:message-id:in-reply-to:references :mime-version:content-transfer-encoding; bh=/RLSJi41laq14R6Fyebh+5anpy5ZQa7HSMlFcMs1a5Q=; b=Ef2iJrYZ+VqQJlD1zunGiT/ix1DJWcis4iUic1fpS0zjbCF80XLwo0IiyF1Z2/Ntda 78hsveibseERJXNdYfFw9SriyYlwhJ9hB6MGCcV9DTpLVPMUoq9nkgDu084n0yKLbSbn 6K1B2Yd0aS6y7oZasQYkMF9iCfk8vXvkSqfcKbOb+qCabAWwdLn6zm6Im4BI8ZfsyHBV Jh1tx4l7LF0PaaR1JnWkefwDgfl2rGNwF/udhV//n4gWC3qj8YyQ9ANZwCquKSn7HSi/ a0okrLpe11tagutRMVuV5magv8FsvxyFEILlHAYLt/ddsgSyjzaga85S58bWGCk0BZLE RcFQ== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20161025; h=x-gm-message-state:from:to:cc:subject:date:message-id:in-reply-to :references:mime-version:content-transfer-encoding; bh=/RLSJi41laq14R6Fyebh+5anpy5ZQa7HSMlFcMs1a5Q=; b=ecgBaFZQ8H4NidVfYYSpQt0WNk3knJ/+a0c0pvqg4FIB5c4qRYMqTjK0fXHnioAFWs 26StF5CPANCYiu2LSBgvbtfw651XpPwh9GUIgg2/rKZlNINHm4K9cek9p7Aaqww12Khb wnKOXKodH41BdmGvTWIz+1H0oS8zrKqQFRIZovo78sVEFGvZGpPTM+prH8Bj/6p1eJxu 7PfYMj7hgHpSSGS6aaiFisVMZN4JNtnOuQJHAgRTeL6OIm3mm47H5X+5HPt2zMxnOl2k wtsnoyq24vHtGYl9PnUsQ1MIXgkVSjo/injF6Gw0KlKbDCSr+SHVh8Koz2CjODK+oiKz RtEg== X-Gm-Message-State: ANhLgQ1ucqbzlaP0/GTofKnGUDrHr1C/58TvXhrvWO8eOC44TbxrBraN 2t3rqv53Lht8nkjlH0gJIJI= X-Google-Smtp-Source: ADFU+vu8aPwBt+XKez2hsewcNmJHhffh+1MzKUMzZ6fo5sv1njcxQEgPBpIYBy3JVQIe8XKAU8uhiA== X-Received: by 2002:a2e:7a0f:: with SMTP id v15mr14770161ljc.156.1584981334413; Mon, 23 Mar 2020 09:35:34 -0700 (PDT) Received: from localhost.localdomain (94-29-39-224.dynamic.spd-mgts.ru. [94.29.39.224]) by smtp.gmail.com with ESMTPSA id m14sm4820017lfo.25.2020.03.23.09.35.33 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Mon, 23 Mar 2020 09:35:33 -0700 (PDT) From: Dmitry Osipenko To: Jens Axboe , Thierry Reding , Jonathan Hunter , =?utf-8?b?TWljaGHFgiBNaXJvc8WCYXc=?= , David Heidelberg , Peter Geis , Stephen Warren , Nicolas Chauvet , Ulf Hansson , Adrian Hunter , Billy Laws Cc: linux-tegra@vger.kernel.org, linux-block@vger.kernel.org, Andrey Danin , Gilles Grandou , Ryan Grachek , linux-mmc@vger.kernel.org, linux-kernel@vger.kernel.org Subject: [PATCH v3 01/10] mmc: core: Add raw_boot_mult field to mmc_ext_csd Date: Mon, 23 Mar 2020 19:34:22 +0300 Message-Id: <20200323163431.7678-2-digetx@gmail.com> X-Mailer: git-send-email 2.25.1 In-Reply-To: <20200323163431.7678-1-digetx@gmail.com> References: <20200323163431.7678-1-digetx@gmail.com> MIME-Version: 1.0 Sender: linux-mmc-owner@vger.kernel.org Precedence: bulk List-ID: X-Mailing-List: linux-mmc@vger.kernel.org In order to support parsing of NVIDIA Tegra Partition Table format, we need to know the BOOT_SIZE_MULT value of the Extended CSD register because NVIDIA's bootloader linearizes the boot0/boot1/main partitions into a single virtual space, and thus, all partition addresses are shifted by the size of boot0 + boot1 partitions. Signed-off-by: Dmitry Osipenko --- drivers/mmc/core/mmc.c | 2 ++ include/linux/mmc/card.h | 1 + 2 files changed, 3 insertions(+) diff --git a/drivers/mmc/core/mmc.c b/drivers/mmc/core/mmc.c index de94fbe629bd..4b7261fbc332 100644 --- a/drivers/mmc/core/mmc.c +++ b/drivers/mmc/core/mmc.c @@ -417,6 +417,8 @@ static int mmc_decode_ext_csd(struct mmc_card *card, u8 *ext_csd) ext_csd[EXT_CSD_ERASE_TIMEOUT_MULT]; card->ext_csd.raw_hc_erase_grp_size = ext_csd[EXT_CSD_HC_ERASE_GRP_SIZE]; + card->ext_csd.raw_boot_mult = + ext_csd[EXT_CSD_BOOT_MULT]; if (card->ext_csd.rev >= 3) { u8 sa_shift = ext_csd[EXT_CSD_S_A_TIMEOUT]; card->ext_csd.part_config = ext_csd[EXT_CSD_PART_CONFIG]; diff --git a/include/linux/mmc/card.h b/include/linux/mmc/card.h index cf3780a6ccc4..90b1d83ce675 100644 --- a/include/linux/mmc/card.h +++ b/include/linux/mmc/card.h @@ -108,6 +108,7 @@ struct mmc_ext_csd { u8 raw_hc_erase_gap_size; /* 221 */ u8 raw_erase_timeout_mult; /* 223 */ u8 raw_hc_erase_grp_size; /* 224 */ + u8 raw_boot_mult; /* 226 */ u8 raw_sec_trim_mult; /* 229 */ u8 raw_sec_erase_mult; /* 230 */ u8 raw_sec_feature_support;/* 231 */ From patchwork Mon Mar 23 16:34:24 2020 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Dmitry Osipenko X-Patchwork-Id: 211074 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org X-Spam-Level: X-Spam-Status: No, score=-9.8 required=3.0 tests=DKIM_SIGNED,DKIM_VALID, DKIM_VALID_AU,FREEMAIL_FORGED_FROMDOMAIN,FREEMAIL_FROM, HEADER_FROM_DIFFERENT_DOMAINS, INCLUDES_PATCH, MAILING_LIST_MULTI, SIGNED_OFF_BY, SPF_HELO_NONE, SPF_PASS, URIBL_BLOCKED, USER_AGENT_GIT autolearn=unavailable autolearn_force=no version=3.4.0 Received: from mail.kernel.org (mail.kernel.org [198.145.29.99]) by smtp.lore.kernel.org (Postfix) with ESMTP id 2696BC54FCF for ; Mon, 23 Mar 2020 16:36:06 +0000 (UTC) Received: from vger.kernel.org (vger.kernel.org [209.132.180.67]) by mail.kernel.org (Postfix) with ESMTP id DFA4120658 for ; Mon, 23 Mar 2020 16:36:05 +0000 (UTC) Authentication-Results: mail.kernel.org; dkim=pass (2048-bit key) header.d=gmail.com header.i=@gmail.com header.b="gk+Vyj1R" Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1727664AbgCWQfo (ORCPT ); Mon, 23 Mar 2020 12:35:44 -0400 Received: from mail-lf1-f67.google.com ([209.85.167.67]:33674 "EHLO mail-lf1-f67.google.com" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1727627AbgCWQfn (ORCPT ); Mon, 23 Mar 2020 12:35:43 -0400 Received: by mail-lf1-f67.google.com with SMTP id c20so10785356lfb.0; Mon, 23 Mar 2020 09:35:38 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20161025; h=from:to:cc:subject:date:message-id:in-reply-to:references :mime-version:content-transfer-encoding; bh=RILIObRT9JS8XulPHlWPQ9vH/RJe3I5zeLXUMy0kxJ0=; b=gk+Vyj1R1kxyD1ehldnOTxnUwn+nfjwX8TCNqq2SOhwe1lJxBWDi5DMYIXC/WnCQuJ exFhGyZUEMvgx5s7hFOiA8mUx3Rv2ndbdgp1cKEpmEJyVwYg3S2oPNp9nF0i14jH89Ik dPmeHleYDYBgm0D3mLbCDwgAmh+pclqSEuBfPyQh8LBsueq6LKQsgF06CatciEjKX1WR s6Morkix+lY4BHl+rJ+J64uylKZmwGsHu02MhW4XA3S19GhlUBag/zrNVvGcSg5H3tsi 5WxPAWQjeF9Z5BQbu2ztVEzYh5iU52mICiDi52Ztg1blULPoIP7UNrGJ5jkBOGF1p9LB FMYA== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20161025; h=x-gm-message-state:from:to:cc:subject:date:message-id:in-reply-to :references:mime-version:content-transfer-encoding; bh=RILIObRT9JS8XulPHlWPQ9vH/RJe3I5zeLXUMy0kxJ0=; b=C8B3Wbr3hqoIW9zJC+J2BPKXNf4RkByS5fXuHSHYpiVi7ck4UIgSlJao5A0SIxIkrC AhCh3chCWHxrH+/7Ml9tLYv1NCFzK7rwMZgvsvH+WxK/vxTQ/odVMerj6/mSCVbv/D+7 lyIrIZ7yS28DgvaZk+hOTZYwFeXAXz2RLThIM3Q3xkPSX03RtxVyatlXzElF99mZuiBC Y5c3Ob/D0pRTXGxwz5xo2oJnfWQDqR2x4oOMwj7ZFo7D4fGaf+liyZZDEPbp+c25JBTC BgA+eFCj9GSiZ0Kl+GX0RLTdX/xLCBDARzIT3hIq+9+PEOESX693MeySW8kQySAZc6NL ymGQ== X-Gm-Message-State: ANhLgQ2D+sCRNmsKqremRWe7+Ku39ln2IKIJr29f10lhaeSN4aYEVqZ0 lJpbw0v19rLcsE07CydqbiM= X-Google-Smtp-Source: ADFU+vv8Jkwt6S4BvRYnbtskRc4pkAFOtNKOvIA51gHCMmcjbaJVPAStXaS0SIj8dYEHhBdOTh0tJg== X-Received: by 2002:a19:660a:: with SMTP id a10mr13723651lfc.9.1584981336899; Mon, 23 Mar 2020 09:35:36 -0700 (PDT) Received: from localhost.localdomain (94-29-39-224.dynamic.spd-mgts.ru. [94.29.39.224]) by smtp.gmail.com with ESMTPSA id m14sm4820017lfo.25.2020.03.23.09.35.35 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Mon, 23 Mar 2020 09:35:36 -0700 (PDT) From: Dmitry Osipenko To: Jens Axboe , Thierry Reding , Jonathan Hunter , =?utf-8?b?TWljaGHFgiBNaXJvc8WCYXc=?= , David Heidelberg , Peter Geis , Stephen Warren , Nicolas Chauvet , Ulf Hansson , Adrian Hunter , Billy Laws Cc: linux-tegra@vger.kernel.org, linux-block@vger.kernel.org, Andrey Danin , Gilles Grandou , Ryan Grachek , linux-mmc@vger.kernel.org, linux-kernel@vger.kernel.org Subject: [PATCH v3 03/10] partitions: Introduce NVIDIA Tegra Partition Table Date: Mon, 23 Mar 2020 19:34:24 +0300 Message-Id: <20200323163431.7678-4-digetx@gmail.com> X-Mailer: git-send-email 2.25.1 In-Reply-To: <20200323163431.7678-1-digetx@gmail.com> References: <20200323163431.7678-1-digetx@gmail.com> MIME-Version: 1.0 Sender: linux-mmc-owner@vger.kernel.org Precedence: bulk List-ID: X-Mailing-List: linux-mmc@vger.kernel.org All NVIDIA Tegra devices use a special partition table format for the internal storage partitioning. Most of Tegra devices have GPT partition in addition to TegraPT, but some older Android consumer-grade devices do not or GPT is placed in a wrong sector, and thus, the TegraPT is needed in order to support these devices properly in the upstream kernel. This patch adds support for NVIDIA Tegra Partition Table format that is used at least by all NVIDIA Tegra20 and Tegra30 devices. Signed-off-by: Dmitry Osipenko --- arch/arm/mach-tegra/tegra.c | 54 ++++ block/partitions/Kconfig | 10 + block/partitions/Makefile | 1 + block/partitions/check.c | 4 + block/partitions/tegra.c | 512 ++++++++++++++++++++++++++++++++++ block/partitions/tegra.h | 8 + include/soc/tegra/bootdata.h | 46 +++ include/soc/tegra/common.h | 9 + include/soc/tegra/partition.h | 91 ++++++ 9 files changed, 735 insertions(+) create mode 100644 block/partitions/tegra.c create mode 100644 block/partitions/tegra.h create mode 100644 include/soc/tegra/bootdata.h create mode 100644 include/soc/tegra/partition.h diff --git a/arch/arm/mach-tegra/tegra.c b/arch/arm/mach-tegra/tegra.c index 3882a6c66969..9236819c5a2f 100644 --- a/arch/arm/mach-tegra/tegra.c +++ b/arch/arm/mach-tegra/tegra.c @@ -28,7 +28,9 @@ #include +#include #include +#include #include #include @@ -62,9 +64,61 @@ u32 tegra_uart_config[3] = { 0, }; +static void __init tegra_boot_config_table_init(void) +{ + struct tegra30_boot_config_table __iomem *t30_bct; + struct tegra20_boot_config_table __iomem *t20_bct; + struct tegra20_boot_info_table __iomem *t20_bit; + u32 iram_end = TEGRA_IRAM_BASE + TEGRA_IRAM_SIZE; + u32 iram_start = TEGRA_IRAM_BASE; + u32 pt_addr, pt_size, bct_size; + + t20_bit = IO_ADDRESS(TEGRA_IRAM_BASE); + + if (of_machine_is_compatible("nvidia,tegra20")) { + bct_size = sizeof(*t20_bct); + + if (t20_bit->bct_size != bct_size || + t20_bit->bct_ptr < iram_start || + t20_bit->bct_ptr > iram_end - bct_size) + return; + + t20_bct = IO_ADDRESS(t20_bit->bct_ptr); + + if (t20_bct->boot_data_version != TEGRA_BOOTDATA_VERSION_T20) + return; + + pt_addr = t20_bct->partition_table_logical_sector_address; + pt_size = t20_bct->partition_table_num_logical_sectors; + + } else if (of_machine_is_compatible("nvidia,tegra30")) { + bct_size = sizeof(*t30_bct); + + if (t20_bit->bct_size != bct_size || + t20_bit->bct_ptr < iram_start || + t20_bit->bct_ptr > iram_end - bct_size) + return; + + t30_bct = IO_ADDRESS(t20_bit->bct_ptr); + + if (t30_bct->boot_data_version != TEGRA_BOOTDATA_VERSION_T30) + return; + + pt_addr = t30_bct->partition_table_logical_sector_address; + pt_size = t30_bct->partition_table_num_logical_sectors; + } else { + return; + } + + pr_info("%s: BCT found in IRAM\n", __func__); + + tegra_partition_table_setup(pt_addr, pt_size); +} + static void __init tegra_init_early(void) { of_register_trusted_foundations(); + tegra_boot_config_table_init(); tegra_cpu_reset_handler_init(); call_firmware_op(l2x0_init); } diff --git a/block/partitions/Kconfig b/block/partitions/Kconfig index 702689a628f0..2e38fde39b9c 100644 --- a/block/partitions/Kconfig +++ b/block/partitions/Kconfig @@ -268,3 +268,13 @@ config CMDLINE_PARTITION help Say Y here if you want to read the partition table from bootargs. The format for the command line is just like mtdparts. + +config TEGRA_PARTITION + bool "NVIDIA Tegra Partition support" if PARTITION_ADVANCED + default y if ARCH_TEGRA + depends on ARCH_TEGRA || COMPILE_TEST + depends on MMC + select MMC_BLOCK + help + Say Y here if you would like to be able to read the hard disk + partition table format used by NVIDIA Tegra machines. diff --git a/block/partitions/Makefile b/block/partitions/Makefile index 2f276b677c81..807319883a18 100644 --- a/block/partitions/Makefile +++ b/block/partitions/Makefile @@ -21,3 +21,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_TEGRA_PARTITION) += tegra.o diff --git a/block/partitions/check.c b/block/partitions/check.c index ffe408fead0c..91268773b6ce 100644 --- a/block/partitions/check.c +++ b/block/partitions/check.c @@ -36,6 +36,7 @@ #include "karma.h" #include "sysv68.h" #include "cmdline.h" +#include "tegra.h" int warn_no_part = 1; /*This is ugly: should make genhd removable media aware*/ @@ -108,6 +109,9 @@ static int (*check_part[])(struct parsed_partitions *) = { #endif #ifdef CONFIG_SYSV68_PARTITION sysv68_partition, +#endif +#ifdef CONFIG_TEGRA_PARTITION + tegra_partition, #endif NULL }; diff --git a/block/partitions/tegra.c b/block/partitions/tegra.c new file mode 100644 index 000000000000..4cb8064bf458 --- /dev/null +++ b/block/partitions/tegra.c @@ -0,0 +1,512 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * NVIDIA Tegra Partition Table + * + * Copyright (C) 2020 GRATE-DRIVER project + * Copyright (C) 2020 Dmitry Osipenko + * + * Credits for the partition table format: + * + * Andrey Danin (Toshiba AC100 TegraPT format) + * Gilles Grandou (Toshiba AC100 TegraPT format) + * Ryan Grachek (Google TV "Molly" TegraPT format) + * Stephen Warren (Useful suggestions about eMMC/etc) + */ + +#define pr_fmt(fmt) "tegra-partition: " fmt + +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +#include +#include + +#include "check.h" +#include "tegra.h" + +#define TEGRA_PT_SECTOR_SZ (TEGRA_PT_LOGICAL_SECTOR_SIZE / SECTOR_SIZE) +#define TEGRA_PT_SECTOR(s) (TEGRA_PT_SECTOR_SZ * (s)) + +#define TEGRA_PT_INFO(ptp, fmt, ...) \ + pr_info("%s: " fmt, \ + (ptp)->state->bdev->bd_disk->disk_name, ##__VA_ARGS__) + +#define TEGRA_PT_ERR(ptp, fmt, ...) \ + pr_err("%s: " fmt, \ + (ptp)->state->bdev->bd_disk->disk_name, ##__VA_ARGS__) + +#define TEGRA_PT_PARSE_ERR(ptp, fmt, ...) \ + TEGRA_PT_ERR(ptp, "sector %llu: invalid " fmt, \ + (ptp)->sector, ##__VA_ARGS__) + +struct tegra_partition_table_parser { + struct tegra_partition_table *pt; + struct parsed_partitions *state; + bool pt_entry_checked; + sector_t sector; + int boot_offset; + u32 dev_instance; + u32 dev_id; +}; + +union tegra_partition_table_u { + struct tegra_partition_table pt; + u8 pt_parts[TEGRA_PT_SECTOR_SZ][SECTOR_SIZE]; +}; + +struct tegra_partition_type { + unsigned int type; + char *name; +}; + +static sector_t tegra_pt_sector_address; +static sector_t tegra_pt_sectors_num; + +void tegra_partition_table_setup(sector_t logical_sector_address, + sector_t logical_sectors_num) +{ + tegra_pt_sector_address = TEGRA_PT_SECTOR(logical_sector_address); + tegra_pt_sectors_num = TEGRA_PT_SECTOR(logical_sectors_num); + + pr_info("initialized to sector = %llu sectors_num = %llu\n", + tegra_pt_sector_address, tegra_pt_sectors_num); +} + +/* + * Some partitions are very sensitive, changing data on them may brick device. + * + * For more details about partitions see: + * + * "https://docs.nvidia.com/jetson/l4t/Tegra Linux Driver Package Development Guide/part_config.html" + */ +static const char * const partitions_blacklist[] = { + "BCT", "EBT", "EKS", "GP1", "GPT", "MBR", "PT", +}; + +static bool tegra_partition_name_match(struct tegra_partition *p, + const char *name) +{ + return !strncmp(p->partition_name, name, TEGRA_PT_NAME_SIZE); +} + +static bool tegra_partition_skip(struct tegra_partition *p, + struct tegra_partition_table_parser *ptp, + sector_t sector) +{ + unsigned int i; + + /* skip eMMC boot partitions */ + if (sector < ptp->boot_offset) + return true; + + for (i = 0; i < ARRAY_SIZE(partitions_blacklist); i++) { + if (tegra_partition_name_match(p, partitions_blacklist[i])) + return true; + } + + return false; +} + +static const struct tegra_partition_type tegra_partition_expected_types[] = { + { .type = TEGRA_PT_PART_TYPE_BCT, .name = "BCT", }, + { .type = TEGRA_PT_PART_TYPE_EBT, .name = "EBT", }, + { .type = TEGRA_PT_PART_TYPE_PT, .name = "PT", }, + { .type = TEGRA_PT_PART_TYPE_GP1, .name = "GP1", }, + { .type = TEGRA_PT_PART_TYPE_GPT, .name = "GPT", }, + { .type = TEGRA_PT_PART_TYPE_GENERIC, .name = NULL, }, +}; + +static int tegra_partition_type_valid(struct tegra_partition_table_parser *ptp, + struct tegra_partition *p) +{ + const struct tegra_partition_type *ptype; + unsigned int i; + + for (i = 0; i < ARRAY_SIZE(tegra_partition_expected_types); i++) { + ptype = &tegra_partition_expected_types[i]; + + if (ptype->name && !tegra_partition_name_match(p, ptype->name)) + continue; + + if (p->part_info.partition_type == ptype->type) + return 0; + + /* + * Unsure about all possible types, let's emit error and + * allow to continue for now. + */ + if (!ptype->name) + return 1; + } + + return -1; +} + +static bool tegra_partition_valid(struct tegra_partition_table_parser *ptp, + struct tegra_partition *p, + struct tegra_partition *prev, + sector_t sector, + sector_t size) +{ + struct tegra_partition_info *prev_pi = &prev->part_info; + sector_t sect_end = TEGRA_PT_SECTOR(prev_pi->logical_sector_address + + prev_pi->logical_sectors_num); + char *type, name[2][TEGRA_PT_NAME_SIZE + 1]; + int err; + + strscpy(name[0], p->partition_name, sizeof(name[0])); + strscpy(name[1], prev->partition_name, sizeof(name[1])); + + /* validate expected partition name/type */ + err = tegra_partition_type_valid(ptp, p); + if (err) { + TEGRA_PT_PARSE_ERR(ptp, "partition_type: [%s] partition_type=%u\n", + name[0], p->part_info.partition_type); + if (err < 0) + return false; + } + + /* validate partition table BCT addresses */ + if (tegra_partition_name_match(p, "PT")) { + if (sector != tegra_pt_sector_address && + size != tegra_pt_sectors_num) { + TEGRA_PT_PARSE_ERR(ptp, "PT location: sector=%llu size=%llu\n", + sector, size); + return false; + } + + if (ptp->pt_entry_checked) { + TEGRA_PT_PARSE_ERR(ptp, "(duplicated) PT\n"); + return false; + } + + ptp->pt_entry_checked = true; + } + + if (sector + size < sector) { + TEGRA_PT_PARSE_ERR(ptp, "size: [%s] integer overflow sector=%llu size=%llu\n", + name[0], sector, size); + return false; + } + + /* validate allocation_policy=sequential (absolute unsupported) */ + if (p != prev && sect_end > sector) { + TEGRA_PT_PARSE_ERR(ptp, "allocation_policy: [%s] end=%llu [%s] sector=%llu size=%llu\n", + name[1], sect_end, name[0], sector, size); + return false; + } + + if (ptp->dev_instance != p->mount_info.device_instance) { + TEGRA_PT_PARSE_ERR(ptp, "device_instance: [%s] device_instance=%u|%u\n", + name[0], ptp->dev_instance, + p->mount_info.device_instance); + return false; + } + + if (ptp->dev_id != p->mount_info.device_id) { + TEGRA_PT_PARSE_ERR(ptp, "device_id: [%s] device_id=%u|%u\n", + name[0], ptp->dev_id, + p->mount_info.device_id); + return false; + } + + if (p->partition_id > 127) { + TEGRA_PT_PARSE_ERR(ptp, "partition_id: [%s] partition_id=%u\n", + name[0], p->partition_id); + return false; + } + + sect_end = get_capacity(ptp->state->bdev->bd_disk); + + /* eMMC boot partitions are below ptp->boot_offset */ + if (sector < ptp->boot_offset) { + sect_end += ptp->boot_offset; + type = "boot"; + } else { + sector -= ptp->boot_offset; + type = "main"; + } + + /* validate size */ + if (!size || sector + size > sect_end) { + TEGRA_PT_PARSE_ERR(ptp, "size: [%s] %s partition boot_offt=%d end=%llu sector=%llu size=%llu\n", + name[0], type, ptp->boot_offset, sect_end, + sector, size); + return false; + } + + return true; +} + +static bool tegra_partitions_parse(struct tegra_partition_table_parser *ptp, + bool check_only) +{ + struct parsed_partitions *state = ptp->state; + struct tegra_partition_table *pt = ptp->pt; + sector_t sector, size; + int i, slot = 1; + + ptp->pt_entry_checked = false; + + for (i = 0; i < pt->secure.num_partitions; i++) { + struct tegra_partition *p = &pt->partitions[i]; + struct tegra_partition *prev = &pt->partitions[max(i - 1, 0)]; + struct tegra_partition_info *pi = &p->part_info; + + if (slot == state->limit && !check_only) + break; + + sector = TEGRA_PT_SECTOR(pi->logical_sector_address); + size = TEGRA_PT_SECTOR(pi->logical_sectors_num); + + if (!tegra_partition_valid(ptp, p, prev, sector, size)) + return false; + + if (check_only || + tegra_partition_skip(p, ptp, sector)) + continue; + + put_partition(state, slot++, sector - ptp->boot_offset, size); + } + + if (!ptp->pt_entry_checked) { + TEGRA_PT_PARSE_ERR(ptp, "PT: table entry not found\n"); + return false; + } + + return true; +} + +static bool +tegra_partition_table_parsed(struct tegra_partition_table_parser *ptp) +{ + if (ptp->pt->secure.num_partitions == 0 || + ptp->pt->secure.num_partitions > TEGRA_PT_MAX_PARTITIONS) { + TEGRA_PT_PARSE_ERR(ptp, "num_partitions=%u\n", + ptp->pt->secure.num_partitions); + return false; + } + + return tegra_partitions_parse(ptp, true) && + tegra_partitions_parse(ptp, false); +} + +static int +tegra_partition_table_insec_hdr_valid(struct tegra_partition_table_parser *ptp) +{ + if (ptp->pt->insecure.magic != TEGRA_PT_MAGIC || + ptp->pt->insecure.version != TEGRA_PT_VERSION) { + TEGRA_PT_PARSE_ERR(ptp, "insecure header: magic=0x%llx ver=0x%x\n", + ptp->pt->insecure.magic, + ptp->pt->insecure.version); + return 0; + } + + return 1; +} + +static int +tegra_partition_table_sec_hdr_valid(struct tegra_partition_table_parser *ptp) +{ + size_t pt_size = ptp->pt->secure.num_partitions; + + pt_size *= sizeof(ptp->pt->partitions[0]); + pt_size += TEGRA_PT_HEADER_SZ; + + if (ptp->pt->secure.magic != TEGRA_PT_MAGIC || + ptp->pt->secure.version != TEGRA_PT_VERSION || + ptp->pt->secure.length != ptp->pt->insecure.length || + ptp->pt->secure.length < pt_size) { + TEGRA_PT_PARSE_ERR(ptp, "secure header: magic=0x%llx ver=0x%x length=%u|%u|%zu\n", + ptp->pt->secure.magic, + ptp->pt->secure.version, + ptp->pt->secure.length, + ptp->pt->insecure.length, + pt_size); + return 0; + } + + return 1; +} + +static int +tegra_partition_table_unencrypted(struct tegra_partition_table_parser *ptp) +{ + /* AES IV, all zeros if unencrypted */ + if (ptp->pt->secure.random_data[0] || ptp->pt->secure.random_data[1] || + ptp->pt->secure.random_data[2] || ptp->pt->secure.random_data[3]) { + pr_err_once("encrypted partition table unsupported\n"); + return 0; + } + + return 1; +} + +static const u32 tegra20_sdhci_bases[TEGRA_PT_SDHCI_DEVICE_INSTANCES] = { + 0xc8000000, 0xc8000200, 0xc8000400, 0xc8000600, +}; + +static const u32 tegra30_sdhci_bases[TEGRA_PT_SDHCI_DEVICE_INSTANCES] = { + 0x78000000, 0x78000200, 0x78000400, 0x78000600, +}; + +static const struct of_device_id tegra_sdhci_match[] = { + { .compatible = "nvidia,tegra20-sdhci", .data = tegra20_sdhci_bases, }, + { .compatible = "nvidia,tegra30-sdhci", .data = tegra30_sdhci_bases, }, + {} +}; + +static int +tegra_partition_table_emmc_boot_offset(struct tegra_partition_table_parser *ptp) +{ + struct mmc_card *card = mmc_bdev_to_card(ptp->state->bdev); + const struct of_device_id *matched; + const u32 *sdhci_bases; + u32 sdhci_base; + unsigned int i; + int err; + + /* filter out unexpected/untested boot sources */ + if (!card || card->ext_csd.rev < 3 || + !mmc_card_is_blockaddr(card) || + mmc_card_is_removable(card->host)) + return -1; + + /* skip everything unrelated to Tegra eMMC */ + matched = of_match_node(tegra_sdhci_match, card->host->parent->of_node); + if (!matched) + return -1; + + sdhci_bases = matched->data; + + /* figure out SDHCI instance ID by the base address */ + err = of_property_read_u32_index(card->host->parent->of_node, + "reg", 0, &sdhci_base); + if (err) + return -1; + + for (i = 0; i < TEGRA_PT_SDHCI_DEVICE_INSTANCES; i++) { + if (sdhci_base == sdhci_bases[i]) + break; + } + + if (i == TEGRA_PT_SDHCI_DEVICE_INSTANCES) + return -1; + + ptp->dev_id = TEGRA_PT_SDHCI_DEVICE_ID; + ptp->dev_instance = i; + + /* + * eMMC storage has two special boot partitions in addition to the + * main one. NVIDIA's bootloader linearizes eMMC boot0->boot1->main + * accesses, this means that the partition table addresses are shifted + * by the size of boot partitions. In accordance with the eMMC + * specification, the boot partition size is calculated as follows: + * + * boot partition size = 128K byte x BOOT_SIZE_MULT + * + * This function returns number of sectors occupied by the both boot + * partitions. + */ + return card->ext_csd.raw_boot_mult * SZ_128K / + SECTOR_SIZE * MMC_NUM_BOOT_PARTITION; +} + +static int tegra_read_partition_table(struct tegra_partition_table_parser *ptp) +{ + union tegra_partition_table_u *ptu = (typeof(ptu))ptp->pt; + unsigned int i; + Sector sect; + void *part; + + for (i = 0; i < ARRAY_SIZE(ptu->pt_parts); i++) { + /* + * Partition table takes at maximum 4096 bytes, but + * read_part_sector() guarantees only that SECTOR_SIZE will + * be read at minimum. + */ + part = read_part_sector(ptp->state, ptp->sector + i, §); + if (!part) { + TEGRA_PT_ERR(ptp, "failed to read sector %llu\n", + ptp->sector + i); + return 0; + } + + memcpy(ptu->pt_parts[i], part, SECTOR_SIZE); + put_dev_sector(sect); + } + + return 1; +} + +int tegra_partition(struct parsed_partitions *state) +{ + struct tegra_partition_table_parser ptp = {}; + sector_t end_sector; + int ret = 0; + + if (!soc_is_tegra()) + return 0; + + ptp.state = state; + + ptp.boot_offset = tegra_partition_table_emmc_boot_offset(&ptp); + if (ptp.boot_offset < 0) + return 0; + + if (tegra_pt_sector_address < ptp.boot_offset) { + TEGRA_PT_INFO(&ptp, + "scanning eMMC boot partitions unimplemented\n"); + return 0; + } + + ptp.pt = kmalloc(TEGRA_PT_LOGICAL_SECTOR_SIZE, GFP_KERNEL); + if (!ptp.pt) + return 0; + + ptp.sector = tegra_pt_sector_address - ptp.boot_offset; + end_sector = ptp.sector + tegra_pt_sectors_num; + + /* + * Partition table is duplicated till the end_sector. + * If first table is corrupted, we will try next. + */ + while (ptp.sector < end_sector) { + ret = tegra_read_partition_table(&ptp); + if (!ret) + goto next_sector; + + ret = tegra_partition_table_insec_hdr_valid(&ptp); + if (!ret) + goto next_sector; + + ret = tegra_partition_table_unencrypted(&ptp); + if (!ret) + goto next_sector; + + ret = tegra_partition_table_sec_hdr_valid(&ptp); + if (!ret) + goto next_sector; + + ret = tegra_partition_table_parsed(&ptp); + if (ret) + break; +next_sector: + ptp.sector += TEGRA_PT_SECTOR_SZ; + } + + if (ret == 1) + strlcat(state->pp_buf, "\n", PAGE_SIZE); + + kfree(ptp.pt); + + return ret; +} diff --git a/block/partitions/tegra.h b/block/partitions/tegra.h new file mode 100644 index 000000000000..cd1fe0b3a4a1 --- /dev/null +++ b/block/partitions/tegra.h @@ -0,0 +1,8 @@ +/* SPDX-License-Identifier: GPL-2.0 */ + +#ifndef __FS_PART_TEGRA_H__ +#define __FS_PART_TEGRA_H__ + +int tegra_partition(struct parsed_partitions *state); + +#endif /* __FS_PART_TEGRA_H__ */ diff --git a/include/soc/tegra/bootdata.h b/include/soc/tegra/bootdata.h new file mode 100644 index 000000000000..7be207cb2519 --- /dev/null +++ b/include/soc/tegra/bootdata.h @@ -0,0 +1,46 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ + +#ifndef __SOC_TEGRA_BOOTDATA_H__ +#define __SOC_TEGRA_BOOTDATA_H__ + +#include +#include + +#define TEGRA_BOOTDATA_VERSION_T20 NVBOOT_BOOTDATA_VERSION(0x2, 0x1) +#define TEGRA_BOOTDATA_VERSION_T30 NVBOOT_BOOTDATA_VERSION(0x3, 0x1) + +#define NVBOOT_BOOTDATA_VERSION(a, b) ((((a) & 0xffff) << 16) | \ + ((b) & 0xffff)) +#define NVBOOT_CMAC_AES_HASH_LENGTH 4 + +struct tegra20_boot_info_table { + u32 unused_data1[14]; + u32 bct_size; + u32 bct_ptr; +} __packed; + +struct tegra20_boot_config_table { + u32 crypto_hash[NVBOOT_CMAC_AES_HASH_LENGTH]; + u32 random_aes_blk[NVBOOT_CMAC_AES_HASH_LENGTH]; + u32 boot_data_version; + u32 unused_data1[712]; + u32 unused_consumer_data1; + u16 partition_table_logical_sector_address; + u16 partition_table_num_logical_sectors; + u32 unused_consumer_data[294]; + u32 unused_data[3]; +} __packed; + +struct tegra30_boot_config_table { + u32 crypto_hash[NVBOOT_CMAC_AES_HASH_LENGTH]; + u32 random_aes_blk[NVBOOT_CMAC_AES_HASH_LENGTH]; + u32 boot_data_version; + u32 unused_data1[1016]; + u32 unused_consumer_data1; + u16 partition_table_logical_sector_address; + u16 partition_table_num_logical_sectors; + u32 unused_consumer_data[502]; + u32 unused_data[3]; +} __packed; + +#endif /* __SOC_TEGRA_BOOTDATA_H__ */ diff --git a/include/soc/tegra/common.h b/include/soc/tegra/common.h index 98027a76ce3d..744280ecab5f 100644 --- a/include/soc/tegra/common.h +++ b/include/soc/tegra/common.h @@ -6,6 +6,15 @@ #ifndef __SOC_TEGRA_COMMON_H__ #define __SOC_TEGRA_COMMON_H__ +#include + +#ifdef CONFIG_ARCH_TEGRA bool soc_is_tegra(void); +#else +static inline bool soc_is_tegra(void) +{ + return false; +} +#endif #endif /* __SOC_TEGRA_COMMON_H__ */ diff --git a/include/soc/tegra/partition.h b/include/soc/tegra/partition.h new file mode 100644 index 000000000000..17f5fbdaf49e --- /dev/null +++ b/include/soc/tegra/partition.h @@ -0,0 +1,91 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ + +#ifndef __SOC_TEGRA_PARTITION_H__ +#define __SOC_TEGRA_PARTITION_H__ + +#include +#include + +#define TEGRA_PT_MAGIC 0xffffffff8f9e8d8bULL +#define TEGRA_PT_VERSION 0x100 +#define TEGRA_PT_LOGICAL_SECTOR_SIZE 4096 +#define TEGRA_PT_AES_HASH_SIZE 4 +#define TEGRA_PT_NAME_SIZE 4 + +#define TEGRA_PT_SDHCI_DEVICE_ID 18 +#define TEGRA_PT_SDHCI_DEVICE_INSTANCES 4 + +#define TEGRA_PT_PART_TYPE_BCT 1 +#define TEGRA_PT_PART_TYPE_EBT 2 +#define TEGRA_PT_PART_TYPE_PT 3 +#define TEGRA_PT_PART_TYPE_GENERIC 6 +#define TEGRA_PT_PART_TYPE_GP1 9 +#define TEGRA_PT_PART_TYPE_GPT 10 + +#define TEGRA_PT_HEADER_SZ \ + (sizeof(struct tegra_partition_header_insecure) + \ + sizeof(struct tegra_partition_header_secure)) \ + +#define TEGRA_PT_MAX_PARTITIONS \ + ((TEGRA_PT_LOGICAL_SECTOR_SIZE - TEGRA_PT_HEADER_SZ) / \ + sizeof(struct tegra_partition)) + +struct tegra_partition_mount_info { + u32 device_id; + u32 device_instance; + u32 device_attr; + u8 mount_path[TEGRA_PT_NAME_SIZE]; + u32 file_system_type; + u32 file_system_attr; +} __packed; + +struct tegra_partition_info { + u32 partition_attr; + u32 __pad1; + u64 logical_sector_address; + u64 logical_sectors_num; + u64 __pad2[2]; + u32 partition_type; + u32 __pad3; +} __packed; + +struct tegra_partition { + u32 partition_id; + u8 partition_name[TEGRA_PT_NAME_SIZE]; + struct tegra_partition_mount_info mount_info; + struct tegra_partition_info part_info; +} __packed; + +struct tegra_partition_header_insecure { + u64 magic; + u32 version; + u32 length; + u32 signature[TEGRA_PT_AES_HASH_SIZE]; +} __packed; + +struct tegra_partition_header_secure { + u32 random_data[TEGRA_PT_AES_HASH_SIZE]; + u64 magic; + u32 version; + u32 length; + u32 num_partitions; + u32 __pad; +} __packed; + +struct tegra_partition_table { + struct tegra_partition_header_insecure insecure; + struct tegra_partition_header_secure secure; + struct tegra_partition partitions[TEGRA_PT_MAX_PARTITIONS]; +} __packed; + +#ifdef CONFIG_TEGRA_PARTITION +void tegra_partition_table_setup(sector_t logical_sector_address, + sector_t logical_sectors_num); +#else +static inline void tegra_partition_table_setup(sector_t logical_sector_address, + sector_t logical_sectors_num) +{ +} +#endif + +#endif /* __SOC_TEGRA_PARTITION_H__ */ From patchwork Mon Mar 23 16:34:26 2020 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Dmitry Osipenko X-Patchwork-Id: 211075 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org X-Spam-Level: X-Spam-Status: No, score=-9.8 required=3.0 tests=DKIM_SIGNED,DKIM_VALID, DKIM_VALID_AU,FREEMAIL_FORGED_FROMDOMAIN,FREEMAIL_FROM, HEADER_FROM_DIFFERENT_DOMAINS, INCLUDES_PATCH, MAILING_LIST_MULTI, SIGNED_OFF_BY, SPF_HELO_NONE,SPF_PASS,USER_AGENT_GIT autolearn=unavailable autolearn_force=no version=3.4.0 Received: from mail.kernel.org (mail.kernel.org [198.145.29.99]) by smtp.lore.kernel.org (Postfix) with ESMTP id 58A36C3F2CD for ; Mon, 23 Mar 2020 16:36:06 +0000 (UTC) Received: from vger.kernel.org (vger.kernel.org [209.132.180.67]) by mail.kernel.org (Postfix) with ESMTP id 26EF920836 for ; Mon, 23 Mar 2020 16:36:06 +0000 (UTC) Authentication-Results: mail.kernel.org; dkim=pass (2048-bit key) header.d=gmail.com header.i=@gmail.com header.b="CyNyeMKW" Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1727658AbgCWQfo (ORCPT ); Mon, 23 Mar 2020 12:35:44 -0400 Received: from mail-lj1-f194.google.com ([209.85.208.194]:43052 "EHLO mail-lj1-f194.google.com" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1727507AbgCWQfm (ORCPT ); Mon, 23 Mar 2020 12:35:42 -0400 Received: by mail-lj1-f194.google.com with SMTP id g27so6507421ljn.10; Mon, 23 Mar 2020 09:35:40 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20161025; h=from:to:cc:subject:date:message-id:in-reply-to:references :mime-version:content-transfer-encoding; bh=w0YlAmpluZ5D3NO8kHqv749YNcVtvXYMv+8BYDY6gX8=; b=CyNyeMKW8PFLFOs7Bf7yjiDE4vIvnA+m7J9nEYRxdr+Gx1hmxopSIb5ynvJR2d9yYu FYzHnpeLwnV7RWIcWnXZnyTP+PsSoYJ5rbQjBQ5kWKyzAeJgpJ8yjaureGlZGmefXBvh EY+bpzSo2CeGLmzEfaVHgMJ9F/SalVmxfsm+vZouSgpfMu8I0HWYpK9c4dNMlk60iH2S p/AnZlnl3PKdLdg1R6EfJiBS3XoSqkrtGMqe7AqPuh+JxJRNEkEe7UvpTE3HrtLrgJZI sSWP/L9WQstfoR8glEAbL3+37AIMouj+vfXg5vc0e56Ltz+1BPzomgmMYe1cEv57J2Ap 4frA== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20161025; h=x-gm-message-state:from:to:cc:subject:date:message-id:in-reply-to :references:mime-version:content-transfer-encoding; bh=w0YlAmpluZ5D3NO8kHqv749YNcVtvXYMv+8BYDY6gX8=; b=Hb51vSoE5wnThK8bahflY4zf+i67/F8c9U88AQsl4zr50tR7sfBB63LfXkDkKtKJ21 1CY8fyH56PEKd7EeAgWmOzS3HfYLv/dG2c4qU/l+Yiu/Je3GkQYbOA8e/Mnb9Nyhq/LZ cRaiSf3edZWLpWmSnqmcgePLdie5BJecOHjPptIV/IuhWba5jecIbSsw7L7Ir80Undo+ /QwazAYRfN5dMAhD/6Swvu8S1B2LUR+CZHo1hUQo+6HK/CbBbcVxMcnOXALoldxnPvHN BR5fmR+MIY/+OCDbYKkOYhNDcfQ/4x6dRyOsVBIxUYdy6QQqSvZHyxXY1jjeHWxjD6TS EKuw== X-Gm-Message-State: ANhLgQ2V88b/V4OCIlEVOYRxMZZ5NJCE+38nuEjy0PMn56sz5ULkius+ m1W5yxI04BIMSRjqtoRk6ik= X-Google-Smtp-Source: ADFU+vuaNbeMeWKlKJCFpCe6dFW/Y2Ycr+EGB6ZeJQNghi8JRS+awpjDqrGdi02SL38G0Vf4wvZl5w== X-Received: by 2002:a2e:984d:: with SMTP id e13mr13623932ljj.275.1584981339297; Mon, 23 Mar 2020 09:35:39 -0700 (PDT) Received: from localhost.localdomain (94-29-39-224.dynamic.spd-mgts.ru. [94.29.39.224]) by smtp.gmail.com with ESMTPSA id m14sm4820017lfo.25.2020.03.23.09.35.38 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Mon, 23 Mar 2020 09:35:38 -0700 (PDT) From: Dmitry Osipenko To: Jens Axboe , Thierry Reding , Jonathan Hunter , =?utf-8?b?TWljaGHFgiBNaXJvc8WCYXc=?= , David Heidelberg , Peter Geis , Stephen Warren , Nicolas Chauvet , Ulf Hansson , Adrian Hunter , Billy Laws Cc: linux-tegra@vger.kernel.org, linux-block@vger.kernel.org, Andrey Danin , Gilles Grandou , Ryan Grachek , linux-mmc@vger.kernel.org, linux-kernel@vger.kernel.org Subject: [PATCH v3 05/10] mmc: block: Add mmc_bdev_to_part_type() helper Date: Mon, 23 Mar 2020 19:34:26 +0300 Message-Id: <20200323163431.7678-6-digetx@gmail.com> X-Mailer: git-send-email 2.25.1 In-Reply-To: <20200323163431.7678-1-digetx@gmail.com> References: <20200323163431.7678-1-digetx@gmail.com> MIME-Version: 1.0 Sender: linux-mmc-owner@vger.kernel.org Precedence: bulk List-ID: X-Mailing-List: linux-mmc@vger.kernel.org NVIDIA Tegra Partition Table parser needs to know eMMC boot partition ID that is associated with the block device in order to validate and parse partition table properly. This patch adds new mmc_bdev_to_part_type() helper which takes block device for the input and returns a corresponding MMC card partition ID (part_type). This is needed by tegra-partition parser in order to distinguish boot0 eMMC partition from boot1. Signed-off-by: Dmitry Osipenko --- drivers/mmc/core/block.c | 16 ++++++++++++++++ include/linux/mmc/blkdev.h | 1 + 2 files changed, 17 insertions(+) diff --git a/drivers/mmc/core/block.c b/drivers/mmc/core/block.c index 36d84a8e182c..2cee57c7388d 100644 --- a/drivers/mmc/core/block.c +++ b/drivers/mmc/core/block.c @@ -321,6 +321,22 @@ struct mmc_card *mmc_bdev_to_card(struct block_device *bdev) return md->queue.card; } +int mmc_bdev_to_part_type(struct block_device *bdev) +{ + struct mmc_blk_data *md; + struct mmc_card *card; + + card = mmc_bdev_to_card(bdev); + if (!card) + return -EINVAL; + + md = mmc_blk_get(bdev->bd_disk); + if (!md) + return -EINVAL; + + return md->part_type; +} + static int mmc_blk_open(struct block_device *bdev, fmode_t mode) { struct mmc_blk_data *md = mmc_blk_get(bdev->bd_disk); diff --git a/include/linux/mmc/blkdev.h b/include/linux/mmc/blkdev.h index 67608c58de70..24e73ac02b4b 100644 --- a/include/linux/mmc/blkdev.h +++ b/include/linux/mmc/blkdev.h @@ -9,5 +9,6 @@ struct block_device; struct mmc_card; struct mmc_card *mmc_bdev_to_card(struct block_device *bdev); +int mmc_bdev_to_part_type(struct block_device *bdev); #endif /* LINUX_MMC_BLOCK_DEVICE_H */ From patchwork Mon Mar 23 16:34:30 2020 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Dmitry Osipenko X-Patchwork-Id: 211076 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org X-Spam-Level: X-Spam-Status: No, score=-9.8 required=3.0 tests=DKIM_SIGNED,DKIM_VALID, DKIM_VALID_AU,FREEMAIL_FORGED_FROMDOMAIN,FREEMAIL_FROM, HEADER_FROM_DIFFERENT_DOMAINS, INCLUDES_PATCH, MAILING_LIST_MULTI, SIGNED_OFF_BY, SPF_HELO_NONE,SPF_PASS,USER_AGENT_GIT autolearn=unavailable autolearn_force=no version=3.4.0 Received: from mail.kernel.org (mail.kernel.org [198.145.29.99]) by smtp.lore.kernel.org (Postfix) with ESMTP id 4B835C54FCF for ; Mon, 23 Mar 2020 16:36:02 +0000 (UTC) Received: from vger.kernel.org (vger.kernel.org [209.132.180.67]) by mail.kernel.org (Postfix) with ESMTP id 21AAC20658 for ; Mon, 23 Mar 2020 16:36:02 +0000 (UTC) Authentication-Results: mail.kernel.org; dkim=pass (2048-bit key) header.d=gmail.com header.i=@gmail.com header.b="rG5Hq3wW" Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1727697AbgCWQfq (ORCPT ); Mon, 23 Mar 2020 12:35:46 -0400 Received: from mail-lf1-f67.google.com ([209.85.167.67]:40008 "EHLO mail-lf1-f67.google.com" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1727670AbgCWQfq (ORCPT ); Mon, 23 Mar 2020 12:35:46 -0400 Received: by mail-lf1-f67.google.com with SMTP id j17so10756612lfe.7; Mon, 23 Mar 2020 09:35:44 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20161025; h=from:to:cc:subject:date:message-id:in-reply-to:references :mime-version:content-transfer-encoding; bh=hkYPIPdOfV7br4C4RHUcphVGjfFEoNa4bM1BVezoPBk=; b=rG5Hq3wWvpFqgAS9rq9wpDrYRFkZtPlx87cRxnzG6q8IHm5vYnFrMkt+57dJND+mNc WSoNJf2y6rVAuBSeRYTAposgo3Rjhu1myiVTZedwXbRZdHZ20Ps7+LyESzNkAbHqw40s oedHQ378rv2wH2q2B2zPzvMTfTWPuk5qWn6Is5gE5zo9oyeXVSk6+Ndi4R2yL7qy8U8O jJd4bpGGWwDp6DnvaepPrwqFeQ9nd9pXSDL/+QFjz6IKIxOmvXSOfAxv5cPTr1yBWTSO eit1KUMzdGp3X0/MajYzM+xc0mXDb/9PH9wkKcxktz6K24RyobU4ZR8yqNhZVlm8QYGd xp/Q== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20161025; h=x-gm-message-state:from:to:cc:subject:date:message-id:in-reply-to :references:mime-version:content-transfer-encoding; bh=hkYPIPdOfV7br4C4RHUcphVGjfFEoNa4bM1BVezoPBk=; b=BEZO3gGvQ55pT97vS214sQWPyDolmQCtGWbYb3peO/N8t5bkujI6OiX9YIAN6qBuFd elA/2c+5qfk5vpIJR5sX4s9Ba1dtiYLoDcGLGrpxad5LAeOPazzR5AKvrkucoxqGq63E B0ZOgkRxZlaN2udgPoKruF4Ib/tPUjMfuNBFHmnp3GDZKv1fDvHJHYtn9CRZdblaJg1V LhzhclkvVoMofS0YaTWtHXkPOfEoi2KPqLPzUjUAI9VBm3Go2PEWRj7w8ulUeAvn23hf HLvhCToPTg1d+GJNPgUyqKFz+HOL5rY2xMdnmTJVUd4VyWiNUn3rg6InIiaUq9iR6SWX f4mg== X-Gm-Message-State: ANhLgQ1+ijkA2nO1lrgnClx3eCzTLbWd3XrMDui15Nr38jfOdb6ZYVkm gk4Y5jTpL+Ope4owxoVB5Ro= X-Google-Smtp-Source: ADFU+vtCz1E3FwWgwg/vbftXiI9QdeSSSVnbOJ9AhJmWkWw2WyRvdU/qRlRfzyhbeUClGTO342m4PA== X-Received: by 2002:a19:c005:: with SMTP id q5mr4712508lff.216.1584981344049; Mon, 23 Mar 2020 09:35:44 -0700 (PDT) Received: from localhost.localdomain (94-29-39-224.dynamic.spd-mgts.ru. [94.29.39.224]) by smtp.gmail.com with ESMTPSA id m14sm4820017lfo.25.2020.03.23.09.35.42 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Mon, 23 Mar 2020 09:35:43 -0700 (PDT) From: Dmitry Osipenko To: Jens Axboe , Thierry Reding , Jonathan Hunter , =?utf-8?b?TWljaGHFgiBNaXJvc8WCYXc=?= , David Heidelberg , Peter Geis , Stephen Warren , Nicolas Chauvet , Ulf Hansson , Adrian Hunter , Billy Laws Cc: linux-tegra@vger.kernel.org, linux-block@vger.kernel.org, Andrey Danin , Gilles Grandou , Ryan Grachek , linux-mmc@vger.kernel.org, linux-kernel@vger.kernel.org Subject: [PATCH v3 09/10] mmc: sdhci-tegra: Enable boot partitions scanning on Tegra20 and Tegra30 Date: Mon, 23 Mar 2020 19:34:30 +0300 Message-Id: <20200323163431.7678-10-digetx@gmail.com> X-Mailer: git-send-email 2.25.1 In-Reply-To: <20200323163431.7678-1-digetx@gmail.com> References: <20200323163431.7678-1-digetx@gmail.com> MIME-Version: 1.0 Sender: linux-mmc-owner@vger.kernel.org Precedence: bulk List-ID: X-Mailing-List: linux-mmc@vger.kernel.org Consumer-grade devices usually have a custom NVIDIA Tegra Partition table used by built-in eMMC storage. On some devices partition table resides on a boot eMMC partition, and thus, the boot partitions need to be scanned. Signed-off-by: Dmitry Osipenko --- drivers/mmc/host/sdhci-tegra.c | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/drivers/mmc/host/sdhci-tegra.c b/drivers/mmc/host/sdhci-tegra.c index 3e2c5101291d..364572eaed8a 100644 --- a/drivers/mmc/host/sdhci-tegra.c +++ b/drivers/mmc/host/sdhci-tegra.c @@ -100,6 +100,7 @@ #define NVQUIRK_NEEDS_PAD_CONTROL BIT(7) #define NVQUIRK_DIS_CARD_CLK_CONFIG_TAP BIT(8) #define NVQUIRK_CQHCI_DCMD_R1B_CMD_TIMING BIT(9) +#define NVQUIRK_SCAN_BOOT_PARTITIONS BIT(10) /* SDMMC CQE Base Address for Tegra Host Ver 4.1 and Higher */ #define SDHCI_TEGRA_CQE_BASE_ADDR 0xF000 @@ -1304,7 +1305,8 @@ static const struct sdhci_tegra_soc_data soc_data_tegra20 = { .pdata = &sdhci_tegra20_pdata, .dma_mask = DMA_BIT_MASK(32), .nvquirks = NVQUIRK_FORCE_SDHCI_SPEC_200 | - NVQUIRK_ENABLE_BLOCK_GAP_DET, + NVQUIRK_ENABLE_BLOCK_GAP_DET | + NVQUIRK_SCAN_BOOT_PARTITIONS, }; static const struct sdhci_pltfm_data sdhci_tegra30_pdata = { @@ -1333,7 +1335,8 @@ static const struct sdhci_tegra_soc_data soc_data_tegra30 = { .nvquirks = NVQUIRK_ENABLE_SDHCI_SPEC_300 | NVQUIRK_ENABLE_SDR50 | NVQUIRK_ENABLE_SDR104 | - NVQUIRK_HAS_PADCALIB, + NVQUIRK_HAS_PADCALIB | + NVQUIRK_SCAN_BOOT_PARTITIONS, }; static const struct sdhci_ops tegra114_sdhci_ops = { @@ -1586,6 +1589,9 @@ static int sdhci_tegra_probe(struct platform_device *pdev) /* HW busy detection is supported, but R1B responses are required. */ host->mmc->caps |= MMC_CAP_WAIT_WHILE_BUSY | MMC_CAP_NEED_RSP_BUSY; + if (tegra_host->soc_data->nvquirks & NVQUIRK_SCAN_BOOT_PARTITIONS) + host->mmc->scan_mmc_boot_partitions = true; + tegra_sdhci_parse_dt(host); tegra_host->power_gpio = devm_gpiod_get_optional(&pdev->dev, "power", From patchwork Mon Mar 23 16:34:31 2020 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Dmitry Osipenko X-Patchwork-Id: 211077 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org X-Spam-Level: X-Spam-Status: No, score=-9.8 required=3.0 tests=DKIM_SIGNED,DKIM_VALID, DKIM_VALID_AU,FREEMAIL_FORGED_FROMDOMAIN,FREEMAIL_FROM, HEADER_FROM_DIFFERENT_DOMAINS, INCLUDES_PATCH, MAILING_LIST_MULTI, SIGNED_OFF_BY, SPF_HELO_NONE,SPF_PASS,USER_AGENT_GIT autolearn=unavailable autolearn_force=no version=3.4.0 Received: from mail.kernel.org (mail.kernel.org [198.145.29.99]) by smtp.lore.kernel.org (Postfix) with ESMTP id 632DAC54FCE for ; Mon, 23 Mar 2020 16:35:59 +0000 (UTC) Received: from vger.kernel.org (vger.kernel.org [209.132.180.67]) by mail.kernel.org (Postfix) with ESMTP id 30BCC20753 for ; Mon, 23 Mar 2020 16:35:59 +0000 (UTC) Authentication-Results: mail.kernel.org; dkim=pass (2048-bit key) header.d=gmail.com header.i=@gmail.com header.b="HmajspLK" Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1727773AbgCWQfz (ORCPT ); Mon, 23 Mar 2020 12:35:55 -0400 Received: from mail-lj1-f193.google.com ([209.85.208.193]:44775 "EHLO mail-lj1-f193.google.com" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1727689AbgCWQfs (ORCPT ); Mon, 23 Mar 2020 12:35:48 -0400 Received: by mail-lj1-f193.google.com with SMTP id w4so15289678lji.11; Mon, 23 Mar 2020 09:35:46 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20161025; h=from:to:cc:subject:date:message-id:in-reply-to:references :mime-version:content-transfer-encoding; bh=QniCpQjPJXTAwB46jjMW57nPV4Kx9N7fme8aB82yFro=; b=HmajspLKp94NMbwJgNkK9F+3DreNcShuPTxmQie5bnGBkmLFBLrZ1hdjQgG36En68G RIKiSvCWyf/i9H0sVfcccBxhhCujmIFtVk5uqwOt5NyMDsXSfqVLEZb7DrGuwrlvHpie RZh7HUEufQo8Cgb7Y/kx+cMg2SYz3Rc6PRKqVEPmF0n4aI90H9eWEgs0LI1eOptut6rL JrhOJVQPEA1vZwDwLDqlVQ07gYPWC/hHh9wKABaAJoS7UkFzeefnauZUXEaEx1OHF9ol ObYrl65IwlTFNeb3MKgeyVTDUHQqj7m+GLuuiQebM5/8fP8nVYpSX5P3GUVtB36pYGvA Wj3Q== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20161025; h=x-gm-message-state:from:to:cc:subject:date:message-id:in-reply-to :references:mime-version:content-transfer-encoding; bh=QniCpQjPJXTAwB46jjMW57nPV4Kx9N7fme8aB82yFro=; b=Lx/2ogsCNtkaFawTZKPgWUbO5/khdKYT4rmYpkwaaTbZ0L7EF0o/L12Wib37hhYDv8 91A8MEFoPuXlKv7/7NGjYTM77APwOoBMtlZ6C2kAroYYkPov/BpaLG1aC4PxuPGmeMsN 5F28D9xs/hvAKidFpANzFvdt64a4d3QaetSaJGUhLuRb0AYbuHAF5dzVtlp6MvEPz1A2 sbWylfCqua6VrY3BLUlfFQG1m0hCnNmJ9N4JonLzEDi4oFIv8Q1spC1eAC46fc11KQou vHSZKnPqbl69azdMzqkpKx8d0z+sUA4A+lEB4E0+7Jp7MBNJktl1u5K7yUyzKYZCpKVw N4WA== X-Gm-Message-State: ANhLgQ17ttWrLUnmXRhXrg6htB2zix0bfA0vdFOvljnPUcZcm4xFZiZ5 RmKMsbJpaXPXNTWjIEGWa4Q= X-Google-Smtp-Source: ADFU+vv7x9KQTUFEfdMp/RNH6gi82I774PfSv5dAfz6Culd+uLgWQ7cCiPrxd20mWcn/9n44DuCr5w== X-Received: by 2002:a2e:9a54:: with SMTP id k20mr7111758ljj.272.1584981345380; Mon, 23 Mar 2020 09:35:45 -0700 (PDT) Received: from localhost.localdomain (94-29-39-224.dynamic.spd-mgts.ru. [94.29.39.224]) by smtp.gmail.com with ESMTPSA id m14sm4820017lfo.25.2020.03.23.09.35.44 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Mon, 23 Mar 2020 09:35:44 -0700 (PDT) From: Dmitry Osipenko To: Jens Axboe , Thierry Reding , Jonathan Hunter , =?utf-8?b?TWljaGHFgiBNaXJvc8WCYXc=?= , David Heidelberg , Peter Geis , Stephen Warren , Nicolas Chauvet , Ulf Hansson , Adrian Hunter , Billy Laws Cc: linux-tegra@vger.kernel.org, linux-block@vger.kernel.org, Andrey Danin , Gilles Grandou , Ryan Grachek , linux-mmc@vger.kernel.org, linux-kernel@vger.kernel.org Subject: [PATCH v3 10/10] partitions/tegra: Implement eMMC boot partitions scanning Date: Mon, 23 Mar 2020 19:34:31 +0300 Message-Id: <20200323163431.7678-11-digetx@gmail.com> X-Mailer: git-send-email 2.25.1 In-Reply-To: <20200323163431.7678-1-digetx@gmail.com> References: <20200323163431.7678-1-digetx@gmail.com> MIME-Version: 1.0 Sender: linux-mmc-owner@vger.kernel.org Precedence: bulk List-ID: X-Mailing-List: linux-mmc@vger.kernel.org Some NVIDIA Tegra devices store partition table on eMMC boot partition. In order to support this case, the tegra-partition parser will read out partition table from a boot partition and stash it for the main eMMC partition. Signed-off-by: Dmitry Osipenko --- block/partitions/tegra.c | 122 +++++++++++++++++++++++++++++++++++---- 1 file changed, 111 insertions(+), 11 deletions(-) diff --git a/block/partitions/tegra.c b/block/partitions/tegra.c index 4cb8064bf458..09c300330f81 100644 --- a/block/partitions/tegra.c +++ b/block/partitions/tegra.c @@ -25,6 +25,7 @@ #include #include #include +#include #include #include @@ -50,7 +51,10 @@ struct tegra_partition_table_parser { struct tegra_partition_table *pt; struct parsed_partitions *state; + struct mmc_card *card; bool pt_entry_checked; + unsigned int boot_id; + bool snapshot_mode; sector_t sector; int boot_offset; u32 dev_instance; @@ -67,6 +71,7 @@ struct tegra_partition_type { char *name; }; +static struct tegra_partition_table *scratch_pt; static sector_t tegra_pt_sector_address; static sector_t tegra_pt_sectors_num; @@ -224,6 +229,10 @@ static bool tegra_partition_valid(struct tegra_partition_table_parser *ptp, return false; } + /* size will be validated when ptp->snapshot_mode=false */ + if (ptp->snapshot_mode && size) + return true; + sect_end = get_capacity(ptp->state->bdev->bd_disk); /* eMMC boot partitions are below ptp->boot_offset */ @@ -254,6 +263,9 @@ static bool tegra_partitions_parse(struct tegra_partition_table_parser *ptp, sector_t sector, size; int i, slot = 1; + if (ptp->snapshot_mode && !check_only) + return true; + ptp->pt_entry_checked = false; for (i = 0; i < pt->secure.num_partitions; i++) { @@ -369,6 +381,7 @@ tegra_partition_table_emmc_boot_offset(struct tegra_partition_table_parser *ptp) { struct mmc_card *card = mmc_bdev_to_card(ptp->state->bdev); const struct of_device_id *matched; + int part_type, area_type; const u32 *sdhci_bases; u32 sdhci_base; unsigned int i; @@ -404,6 +417,32 @@ tegra_partition_table_emmc_boot_offset(struct tegra_partition_table_parser *ptp) ptp->dev_id = TEGRA_PT_SDHCI_DEVICE_ID; ptp->dev_instance = i; + area_type = mmc_bdev_to_area_type(ptp->state->bdev); + + if (WARN_ON(area_type < 0)) + return -1; + + switch (area_type) { + case MMC_BLK_DATA_AREA_BOOT: + part_type = mmc_bdev_to_part_type(ptp->state->bdev); + + if (WARN_ON(part_type < 0)) + return -1; + + ptp->boot_id = part_type - EXT_CSD_PART_CONFIG_ACC_BOOT0; + ptp->snapshot_mode = true; + break; + + case MMC_BLK_DATA_AREA_MAIN: + break; + + default: + TEGRA_PT_ERR(ptp, "unexpected area_type: %u\n", area_type); + return -1; + } + + ptp->card = card; + /* * eMMC storage has two special boot partitions in addition to the * main one. NVIDIA's bootloader linearizes eMMC boot0->boot1->main @@ -427,6 +466,9 @@ static int tegra_read_partition_table(struct tegra_partition_table_parser *ptp) Sector sect; void *part; + if (scratch_pt) + return 1; + for (i = 0; i < ARRAY_SIZE(ptu->pt_parts); i++) { /* * Partition table takes at maximum 4096 bytes, but @@ -462,18 +504,64 @@ int tegra_partition(struct parsed_partitions *state) if (ptp.boot_offset < 0) return 0; - if (tegra_pt_sector_address < ptp.boot_offset) { - TEGRA_PT_INFO(&ptp, - "scanning eMMC boot partitions unimplemented\n"); - return 0; - } + /* + * Some devices store partition table on boot MMC partition. + * In this case a "snapshot mode" will be used, which will + * only read->check->store partition table, the stored table + * will be used for the main MMC partition later on. + */ + if (ptp.snapshot_mode) { + sector_t boot_start, boot_end, boot_size; - ptp.pt = kmalloc(TEGRA_PT_LOGICAL_SECTOR_SIZE, GFP_KERNEL); - if (!ptp.pt) - return 0; + /* partition is already snapshoted, no need to proceed */ + if (scratch_pt) + return 0; + + boot_size = ptp.boot_offset / MMC_NUM_BOOT_PARTITION; + boot_start = ptp.boot_id * boot_size; + boot_end = boot_start + boot_size; - ptp.sector = tegra_pt_sector_address - ptp.boot_offset; - end_sector = ptp.sector + tegra_pt_sectors_num; + /* + * Bail out if partition table isn't located here, at this MMC + * partition. + */ + if (tegra_pt_sector_address < boot_start || + tegra_pt_sector_address >= boot_end) + return 0; + + ptp.boot_offset = boot_start; + + /* + * Note that mmc_blk_probe() always registers boot partitions + * after the main and we rely on this feature, otherwise + * scratch_pt won't be released (although this is not a big + * deal). + */ + ptp.pt = kmalloc(TEGRA_PT_LOGICAL_SECTOR_SIZE, GFP_KERNEL); + if (!ptp.pt) + return 0; + + ptp.sector = tegra_pt_sector_address - ptp.boot_offset; + end_sector = ptp.sector + tegra_pt_sectors_num; + + } else if (scratch_pt) { + TEGRA_PT_INFO(&ptp, "using stashed partition table\n"); + + ptp.pt = scratch_pt; + ptp.sector = 0; + end_sector = 1; + + } else { + if (tegra_pt_sector_address < ptp.boot_offset) + return 0; + + ptp.pt = kmalloc(TEGRA_PT_LOGICAL_SECTOR_SIZE, GFP_KERNEL); + if (!ptp.pt) + return 0; + + ptp.sector = tegra_pt_sector_address - ptp.boot_offset; + end_sector = ptp.sector + tegra_pt_sectors_num; + } /* * Partition table is duplicated till the end_sector. @@ -503,9 +591,21 @@ int tegra_partition(struct parsed_partitions *state) ptp.sector += TEGRA_PT_SECTOR_SZ; } - if (ret == 1) + if (ret == 1) { + if (ptp.snapshot_mode) { + ptp.card->quirks |= MMC_QUIRK_RESCAN_MAIN_BLKDEV; + scratch_pt = ptp.pt; + + strlcat(state->pp_buf, + " stashed tegra-partition table\n", PAGE_SIZE); + + return ret; + } + strlcat(state->pp_buf, "\n", PAGE_SIZE); + } + scratch_pt = NULL; kfree(ptp.pt); return ret;