Skip to content
Permalink
Browse files
partitions/efi: Support NVIDIA Tegra devices
NVIDIA Tegra consumer devices have EMMC storage that has GPT entry at a
non-standard location. Support looking up GPT entry at a special sector
to enable such devices.

Signed-off-by: Dmitry Osipenko <digetx@gmail.com>
  • Loading branch information
digetx authored and intel-lab-lkp committed Aug 17, 2021
1 parent 0e707f7 commit 55b4a78926188f60b2dc062524a5b4b5b0657c67
Show file tree
Hide file tree
Showing 6 changed files with 98 additions and 0 deletions.
@@ -267,3 +267,11 @@ 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 EFI_PARTITION && MMC_BLOCK && (ARCH_TEGRA || COMPILE_TEST)
help
Say Y here if you would like to be able to read the hard disk
partition table format used by NVIDIA Tegra machines.
@@ -20,3 +20,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
@@ -22,6 +22,7 @@ struct parsed_partitions {
int limit;
bool access_beyond_eod;
char *pp_buf;
sector_t force_gpt_sector;
};

typedef struct {
@@ -67,4 +68,5 @@ int osf_partition(struct parsed_partitions *state);
int sgi_partition(struct parsed_partitions *state);
int sun_partition(struct parsed_partitions *state);
int sysv68_partition(struct parsed_partitions *state);
int tegra_partition_forced_gpt(struct parsed_partitions *state);
int ultrix_partition(struct parsed_partitions *state);
@@ -82,6 +82,9 @@ static int (*check_part[])(struct parsed_partitions *) = {
#endif
#ifdef CONFIG_SYSV68_PARTITION
sysv68_partition,
#endif
#ifdef CONFIG_TEGRA_PARTITION
tegra_partition_forced_gpt,
#endif
NULL
};
@@ -619,6 +619,15 @@ static int find_valid_gpt(struct parsed_partitions *state, gpt_header **gpt,
if (!good_agpt && force_gpt)
good_agpt = is_gpt_valid(state, lastlba, &agpt, &aptes);

/*
* The force_gpt_sector is used by NVIDIA Tegra partition parser in
* order to convey a non-standard location of the GPT entry for lookup.
* By default force_gpt_sector is set to 0 and has no effect.
*/
if (!good_agpt && force_gpt && state->force_gpt_sector)
good_agpt = is_gpt_valid(state, state->force_gpt_sector,
&agpt, &aptes);

/* The obviously unsuccessful case */
if (!good_pgpt && !good_agpt)
goto fail;
@@ -0,0 +1,75 @@
// SPDX-License-Identifier: GPL-2.0

#define pr_fmt(fmt) "tegra-partition: " fmt

#include <linux/blkdev.h>
#include <linux/kernel.h>
#include <linux/of.h>
#include <linux/sizes.h>

#include <linux/mmc/blkdev.h>
#include <linux/mmc/card.h>
#include <linux/mmc/host.h>

#include <soc/tegra/common.h>

#include "check.h"

static const struct of_device_id tegra_sdhci_match[] = {
{ .compatible = "nvidia,tegra20-sdhci", },
{ .compatible = "nvidia,tegra30-sdhci", },
{ .compatible = "nvidia,tegra114-sdhci", },
{ .compatible = "nvidia,tegra124-sdhci", },
{}
};

int tegra_partition_forced_gpt(struct parsed_partitions *state)
{
struct gendisk *disk = state->disk;
struct block_device *bdev = disk->part0;
struct mmc_card *card = mmc_bdev_to_card(bdev);
int ret, boot_offset;

if (!soc_is_tegra())
return 0;

/* filter out unrelated and untested boot sources */
if (!card || card->ext_csd.rev < 3 ||
!mmc_card_is_blockaddr(card) ||
mmc_card_is_removable(card->host) ||
bdev_logical_block_size(bdev) != SZ_512 ||
!of_match_node(tegra_sdhci_match, card->host->parent->of_node)) {
pr_debug("%s: unexpected boot source\n", disk->disk_name);
return 0;
}

/*
* 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.
*/
boot_offset = card->ext_csd.raw_boot_mult * SZ_128K /
SZ_512 * MMC_NUM_BOOT_PARTITION;

/*
* The fixed GPT entry address is calculated like this:
*
* gpt_sector = ext_csd.sectors_num - ext_csd.boot_sectors_num - 1
*
* This algorithm is defined by NVIDIA and used by Android devices.
*/
state->force_gpt_sector = get_capacity(disk);
state->force_gpt_sector -= boot_offset + 1;

ret = efi_partition(state);
state->force_gpt_sector = 0;

return ret;
}

0 comments on commit 55b4a78

Please sign in to comment.