2020-05-16 15:40:39

by Dmitry Osipenko

[permalink] [raw]
Subject: [PATCH v5 0/6] Introduce NVIDIA Tegra Partition Table

Hello,

This series adds support for the NVIDIA Tegra Partition Table format,
which is needed in order to support consumer-grade Tegra-based devices
by the upstream kernel. These devices have Secure Boot enabled and it
can't be disabled, and thus, it's not possible to easily modify bootloader
and eMMC storage partitioning.

Big thanks to everyone who helped with figuring out the TegraPT format
and with testing!

Changelog:

v5: - Corrected Kconfig dependency, like it was suggested by Randy Dunlap
in the review comment to v4.

- Corrected a typo in the commit message, which was spotted by
Steve McIntyre in v4.

- The new force_gpt_sector variable has been moved out to the common
parser state because I realized that multiple drives could be scanned
simultaneously, and thus, a global shared variable isn't a suitable
variant.

- The Tegra's enforced GPT-scan now supports older bootloader versions,
which do not provide the gpt_sector= command line option. The gpt_sector
should be calculated using NVIDIA's algorithm in this case.

v4: - Scanning of the eMMC boot partitions has been dropped because it
requires a bit too messy hacks in the kernel. We can live without
this feature, at least for now it's not really needed.

- Instead of the dropped boot partitions scanning, the "gpt_sector="
command line option has been brought back [1]. But now it's done
in a different way, not bothering platforms other than Tegra and
not touching block devices other than eMMC, which was requested during
of the [1] review. The "gpt_sector=" usage is needed when partition
table is inaccessible by kernel, which is the case for the Ouya game
console device for example. Please note that "gpt_sector=" is not
available on all devices, such devices will fall back to the TegraPT,
Samsung Galaxy Tab 10.1 is an example of a such device.

[1] https://patchwork.ozlabs.org/project/linux-tegra/patch/[email protected]/

- We got a bit more broad testing and discovered that Samsung Galaxy
Tab 10.1 uses 2K logical sectors instead of 4K. So the 2K sectors
are supported now by the TegraPT parser. Thanks to Nils Östlund for
the testing!

- TegraPT parser now utilizes "tegraboot=" cmdline option which is
passed to kernel by the NVIDIA's bootloader. It's used for verifying
that SDMMC device is *the* boot source.

- Added patch that exposes Boot Configuration Table to userspace via
sysfs. Thanks to Michał Mirosław for the suggestion!

- Misc changes:
- The EB2 (second bootloader) partition is added to the list of
known partitions. Used by the Galaxy Tab 10.

- The blkdev logical sector size is checked now, for consistency.
It always should be 512 bytes on the supported/tested devices.

- Verbose error messages are replaced with pr_debug().

v3: - Fixed "BUG: KASAN: slab-out-of-bounds in tegra_partition". Thanks to
Peter Geis for noticing the problem.

- The MMC boot partitions scanning is now opt-in. See this patch:

mmc: block: Support partition-table scanning on boot partitions

- The found MMC boot partitions won't be assigned to the MMC boot
block device ever due to the new GENHD_FL_PART_SCAN_ONCE flag.

This makes us to ensure that the old behavior of the MMC core is
preserved for a non-Tegra MMC-block users.

New patches in v3:

block: Introduce GENHD_FL_PART_SCAN_ONCE
mmc: sdhci-tegra: Enable boot partitions scanning on Tegra20 and Tegra30

v2: - Addressed v1 review comments from Stephen Warren by using BIT for
locating BCT position in IRAM.

- Added more validations to the TegraPT parser: partition type is
verified, eMMC instance ID is verified.

- TegraPT parser now doesn't touch any devices other than eMMC.

- EKS (encrypted keys) partition is blacklisted now.

- Implemented eMMC boot partitions scanning. These new patches are
added in a result:

mmc: block: Add mmc_bdev_to_part_type() helper
mmc: block: Add mmc_bdev_to_area_type() helper
mmc: block: Add MMC_QUIRK_RESCAN_MAIN_BLKDEV
mmc: block: Enable partition-table scanning for boot partitions
partitions/tegra: Implement eMMC boot partitions scanning

Dmitry Osipenko (6):
mmc: core: Add raw_boot_mult field to mmc_ext_csd
mmc: block: Add mmc_bdev_to_card() helper
partitions: Introduce NVIDIA Tegra Partition Table
partitions/efi: Support GPT entry lookup at a non-standard location
partitions/tegra: Support enforced GPT scanning
soc/tegra: Expose Boot Configuration Table via sysfs

arch/arm/mach-tegra/tegra.c | 58 ++++
block/partitions/Kconfig | 8 +
block/partitions/Makefile | 1 +
block/partitions/check.h | 3 +
block/partitions/core.c | 4 +
block/partitions/efi.c | 14 +
block/partitions/tegra.c | 624 ++++++++++++++++++++++++++++++++++
drivers/mmc/core/block.c | 15 +
drivers/mmc/core/mmc.c | 2 +
drivers/soc/tegra/Makefile | 1 +
drivers/soc/tegra/bootdata.c | 51 +++
drivers/soc/tegra/common.c | 17 +
include/linux/mmc/blkdev.h | 13 +
include/linux/mmc/card.h | 1 +
include/soc/tegra/bootdata.h | 48 +++
include/soc/tegra/common.h | 12 +
include/soc/tegra/partition.h | 84 +++++
17 files changed, 956 insertions(+)
create mode 100644 block/partitions/tegra.c
create mode 100644 drivers/soc/tegra/bootdata.c
create mode 100644 include/linux/mmc/blkdev.h
create mode 100644 include/soc/tegra/bootdata.h
create mode 100644 include/soc/tegra/partition.h

--
2.26.0


2020-05-16 15:42:15

by Dmitry Osipenko

[permalink] [raw]
Subject: [PATCH v5 6/6] soc/tegra: Expose Boot Configuration Table via sysfs

It's quite useful to have unencrypted BCT exposed to userspace for
debugging purposes, so let's expose it via sysfs. The BCT data will be
present in '/sys/tegra/boot_config_table' binary file if BCT is available.

Suggested-by: Michał Mirosław <[email protected]>
Signed-off-by: Dmitry Osipenko <[email protected]>
---
arch/arm/mach-tegra/tegra.c | 4 +++
drivers/soc/tegra/Makefile | 1 +
drivers/soc/tegra/bootdata.c | 51 ++++++++++++++++++++++++++++++++++++
drivers/soc/tegra/common.c | 17 ++++++++++++
include/soc/tegra/bootdata.h | 2 ++
include/soc/tegra/common.h | 3 +++
6 files changed, 78 insertions(+)
create mode 100644 drivers/soc/tegra/bootdata.c

diff --git a/arch/arm/mach-tegra/tegra.c b/arch/arm/mach-tegra/tegra.c
index da6bcd85398b..5f40463f1b97 100644
--- a/arch/arm/mach-tegra/tegra.c
+++ b/arch/arm/mach-tegra/tegra.c
@@ -72,6 +72,7 @@ static void __init tegra_boot_config_table_init(void)
u32 iram_end = TEGRA_IRAM_BASE + TEGRA_IRAM_SIZE;
u32 iram_start = TEGRA_IRAM_BASE;
u32 pt_addr, pt_size, bct_size;
+ void __iomem *bct_ptr;

t20_bit = IO_ADDRESS(TEGRA_IRAM_BASE);

@@ -90,6 +91,7 @@ static void __init tegra_boot_config_table_init(void)

pt_addr = t20_bct->partition_table_logical_sector_address;
pt_size = t20_bct->partition_table_num_logical_sectors;
+ bct_ptr = t20_bct;

} else if (of_machine_is_compatible("nvidia,tegra30")) {
bct_size = sizeof(*t30_bct);
@@ -106,12 +108,14 @@ static void __init tegra_boot_config_table_init(void)

pt_addr = t30_bct->partition_table_logical_sector_address;
pt_size = t30_bct->partition_table_num_logical_sectors;
+ bct_ptr = t30_bct;
} else {
return;
}

pr_info("%s: BCT found in IRAM\n", __func__);

+ tegra_bootdata_bct_setup(bct_ptr, bct_size);
tegra_partition_table_setup(pt_addr, pt_size);
}

diff --git a/drivers/soc/tegra/Makefile b/drivers/soc/tegra/Makefile
index 9c809c1814bd..8be2bfb4d95d 100644
--- a/drivers/soc/tegra/Makefile
+++ b/drivers/soc/tegra/Makefile
@@ -1,6 +1,7 @@
# SPDX-License-Identifier: GPL-2.0
obj-y += fuse/

+obj-y += bootdata.o
obj-y += common.o
obj-$(CONFIG_SOC_TEGRA_FLOWCTRL) += flowctrl.o
obj-$(CONFIG_SOC_TEGRA_PMC) += pmc.o
diff --git a/drivers/soc/tegra/bootdata.c b/drivers/soc/tegra/bootdata.c
new file mode 100644
index 000000000000..e18a27b74023
--- /dev/null
+++ b/drivers/soc/tegra/bootdata.c
@@ -0,0 +1,51 @@
+// SPDX-License-Identifier: GPL-2.0
+
+#include <linux/init.h>
+#include <linux/io.h>
+#include <linux/sizes.h>
+#include <linux/slab.h>
+#include <linux/sysfs.h>
+#include <linux/types.h>
+
+#include <soc/tegra/bootdata.h>
+#include <soc/tegra/common.h>
+
+/*
+ * spare_bct[] will be released once kernel is booted, hence not wasting
+ * kernel space if BCT is missing. The tegra_bct can't be allocated during
+ * of BCT setting up because it's too early for the slab allocator.
+ */
+static u8 spare_bct[SZ_8K] __initdata;
+static u8 *tegra_bct;
+
+static ssize_t boot_config_table_read(struct file *filp,
+ struct kobject *kobj,
+ struct bin_attribute *bin_attr,
+ char *buf, loff_t off, size_t count)
+{
+ memcpy(buf, tegra_bct + off, count);
+ return count;
+}
+static BIN_ATTR_RO(boot_config_table, 0);
+
+static int __init tegra_bootdata_bct_sysfs_init(void)
+{
+ if (!bin_attr_boot_config_table.size)
+ return 0;
+
+ tegra_bct = kmalloc(GFP_KERNEL, bin_attr_boot_config_table.size);
+ if (!tegra_bct)
+ return -ENOMEM;
+
+ memcpy(tegra_bct, spare_bct, bin_attr_boot_config_table.size);
+
+ return sysfs_create_bin_file(tegra_soc_kobj,
+ &bin_attr_boot_config_table);
+}
+late_initcall(tegra_bootdata_bct_sysfs_init)
+
+void __init tegra_bootdata_bct_setup(void __iomem *bct_ptr, size_t bct_size)
+{
+ memcpy_fromio(spare_bct, bct_ptr, bct_size);
+ bin_attr_boot_config_table.size = bct_size;
+}
diff --git a/drivers/soc/tegra/common.c b/drivers/soc/tegra/common.c
index 3dc54f59cafe..2b4b49eacb2e 100644
--- a/drivers/soc/tegra/common.c
+++ b/drivers/soc/tegra/common.c
@@ -3,10 +3,15 @@
* Copyright (C) 2014 NVIDIA CORPORATION. All rights reserved.
*/

+#include <linux/init.h>
+#include <linux/kernel.h>
#include <linux/of.h>
+#include <linux/sysfs.h>

#include <soc/tegra/common.h>

+struct kobject *tegra_soc_kobj;
+
static const struct of_device_id tegra_machine_match[] = {
{ .compatible = "nvidia,tegra20", },
{ .compatible = "nvidia,tegra30", },
@@ -31,3 +36,15 @@ bool soc_is_tegra(void)

return match != NULL;
}
+
+static int __init tegra_soc_sysfs_init(void)
+{
+ if (!soc_is_tegra())
+ return 0;
+
+ tegra_soc_kobj = kobject_create_and_add("tegra", NULL);
+ WARN_ON(!tegra_soc_kobj);
+
+ return 0;
+}
+arch_initcall(tegra_soc_sysfs_init)
diff --git a/include/soc/tegra/bootdata.h b/include/soc/tegra/bootdata.h
index 7be207cb2519..d5c7a251517d 100644
--- a/include/soc/tegra/bootdata.h
+++ b/include/soc/tegra/bootdata.h
@@ -43,4 +43,6 @@ struct tegra30_boot_config_table {
u32 unused_data[3];
} __packed;

+void tegra_bootdata_bct_setup(void __iomem *bct_ptr, size_t bct_size);
+
#endif /* __SOC_TEGRA_BOOTDATA_H__ */
diff --git a/include/soc/tegra/common.h b/include/soc/tegra/common.h
index 744280ecab5f..0bc11b45c98e 100644
--- a/include/soc/tegra/common.h
+++ b/include/soc/tegra/common.h
@@ -7,8 +7,11 @@
#define __SOC_TEGRA_COMMON_H__

#include <linux/types.h>
+#include <linux/sysfs.h>

#ifdef CONFIG_ARCH_TEGRA
+extern struct kobject *tegra_soc_kobj;
+
bool soc_is_tegra(void);
#else
static inline bool soc_is_tegra(void)
--
2.26.0

2020-05-16 15:42:49

by Dmitry Osipenko

[permalink] [raw]
Subject: [PATCH v5 2/6] mmc: block: Add mmc_bdev_to_card() helper

NVIDIA Tegra Partition Table takes into account MMC card's BOOT_SIZE_MULT
parameter, and thus, the partition parser needs to retrieve that EXT_CSD
value from the block device. There are also some other parts of struct
mmc_card that are needed for the partition parser in order to calculate
the eMMC offset and verify different things. This patch introduces new
helper which takes block device for the input argument and returns the
corresponding MMC card.

Signed-off-by: Dmitry Osipenko <[email protected]>
---
drivers/mmc/core/block.c | 15 +++++++++++++++
include/linux/mmc/blkdev.h | 13 +++++++++++++
2 files changed, 28 insertions(+)
create mode 100644 include/linux/mmc/blkdev.h

diff --git a/drivers/mmc/core/block.c b/drivers/mmc/core/block.c
index c5367e2c8487..99298e888381 100644
--- a/drivers/mmc/core/block.c
+++ b/drivers/mmc/core/block.c
@@ -40,6 +40,7 @@
#include <linux/debugfs.h>

#include <linux/mmc/ioctl.h>
+#include <linux/mmc/blkdev.h>
#include <linux/mmc/card.h>
#include <linux/mmc/host.h>
#include <linux/mmc/mmc.h>
@@ -305,6 +306,20 @@ static ssize_t force_ro_store(struct device *dev, struct device_attribute *attr,
return ret;
}

+struct mmc_card *mmc_bdev_to_card(struct block_device *bdev)
+{
+ struct mmc_blk_data *md;
+
+ if (bdev->bd_disk->major != MMC_BLOCK_MAJOR)
+ return NULL;
+
+ md = mmc_blk_get(bdev->bd_disk);
+ if (!md)
+ return NULL;
+
+ return md->queue.card;
+}
+
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
new file mode 100644
index 000000000000..67608c58de70
--- /dev/null
+++ b/include/linux/mmc/blkdev.h
@@ -0,0 +1,13 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+/*
+ * linux/include/linux/mmc/blkdev.h
+ */
+#ifndef LINUX_MMC_BLOCK_DEVICE_H
+#define LINUX_MMC_BLOCK_DEVICE_H
+
+struct block_device;
+struct mmc_card;
+
+struct mmc_card *mmc_bdev_to_card(struct block_device *bdev);
+
+#endif /* LINUX_MMC_BLOCK_DEVICE_H */
--
2.26.0

2020-05-18 07:48:09

by Michał Mirosław

[permalink] [raw]
Subject: Re: [PATCH v5 6/6] soc/tegra: Expose Boot Configuration Table via sysfs

On Sat, May 16, 2020 at 06:36:44PM +0300, Dmitry Osipenko wrote:
> It's quite useful to have unencrypted BCT exposed to userspace for
> debugging purposes, so let's expose it via sysfs. The BCT data will be
> present in '/sys/tegra/boot_config_table' binary file if BCT is available.
[...]
> +/*
> + * spare_bct[] will be released once kernel is booted, hence not wasting
> + * kernel space if BCT is missing. The tegra_bct can't be allocated during
> + * of BCT setting up because it's too early for the slab allocator.
> + */
> +static u8 spare_bct[SZ_8K] __initdata;
> +static u8 *tegra_bct;
> +
> +static ssize_t boot_config_table_read(struct file *filp,
> + struct kobject *kobj,
> + struct bin_attribute *bin_attr,
> + char *buf, loff_t off, size_t count)
> +{
> + memcpy(buf, tegra_bct + off, count);
> + return count;
> +}
> +static BIN_ATTR_RO(boot_config_table, 0);
> +
> +static int __init tegra_bootdata_bct_sysfs_init(void)
> +{
> + if (!bin_attr_boot_config_table.size)
> + return 0;
> +
> + tegra_bct = kmalloc(GFP_KERNEL, bin_attr_boot_config_table.size);
> + if (!tegra_bct)
> + return -ENOMEM;
> +
> + memcpy(tegra_bct, spare_bct, bin_attr_boot_config_table.size);
> +
> + return sysfs_create_bin_file(tegra_soc_kobj,
> + &bin_attr_boot_config_table);

Should we consider freeing the table if sysfs registration fails?

> +}
> +late_initcall(tegra_bootdata_bct_sysfs_init)
> +
> +void __init tegra_bootdata_bct_setup(void __iomem *bct_ptr, size_t bct_size)
> +{
> + memcpy_fromio(spare_bct, bct_ptr, bct_size);
> + bin_attr_boot_config_table.size = bct_size;
[...]

The size isn't checked anywhere. How the maximum is obtained? At least
a comment would be good if the 8k limit is guaranteed by other means.

Best Regards,
Micha? Miros?aw

2020-05-18 08:02:34

by Dmitry Osipenko

[permalink] [raw]
Subject: Re: [PATCH v5 6/6] soc/tegra: Expose Boot Configuration Table via sysfs

18.05.2020 10:45, Michał Mirosław пишет:
> On Sat, May 16, 2020 at 06:36:44PM +0300, Dmitry Osipenko wrote:
>> It's quite useful to have unencrypted BCT exposed to userspace for
>> debugging purposes, so let's expose it via sysfs. The BCT data will be
>> present in '/sys/tegra/boot_config_table' binary file if BCT is available.
> [...]
>> +/*
>> + * spare_bct[] will be released once kernel is booted, hence not wasting
>> + * kernel space if BCT is missing. The tegra_bct can't be allocated during
>> + * of BCT setting up because it's too early for the slab allocator.
>> + */
>> +static u8 spare_bct[SZ_8K] __initdata;
>> +static u8 *tegra_bct;
>> +
>> +static ssize_t boot_config_table_read(struct file *filp,
>> + struct kobject *kobj,
>> + struct bin_attribute *bin_attr,
>> + char *buf, loff_t off, size_t count)
>> +{
>> + memcpy(buf, tegra_bct + off, count);
>> + return count;
>> +}
>> +static BIN_ATTR_RO(boot_config_table, 0);
>> +
>> +static int __init tegra_bootdata_bct_sysfs_init(void)
>> +{
>> + if (!bin_attr_boot_config_table.size)
>> + return 0;
>> +
>> + tegra_bct = kmalloc(GFP_KERNEL, bin_attr_boot_config_table.size);
>> + if (!tegra_bct)
>> + return -ENOMEM;
>> +
>> + memcpy(tegra_bct, spare_bct, bin_attr_boot_config_table.size);
>> +
>> + return sysfs_create_bin_file(tegra_soc_kobj,
>> + &bin_attr_boot_config_table);
>
> Should we consider freeing the table if sysfs registration fails?

This is a good suggestion, thank you :)

>> +}
>> +late_initcall(tegra_bootdata_bct_sysfs_init)
>> +
>> +void __init tegra_bootdata_bct_setup(void __iomem *bct_ptr, size_t bct_size)
>> +{
>> + memcpy_fromio(spare_bct, bct_ptr, bct_size);
>> + bin_attr_boot_config_table.size = bct_size;
> [...]
>
> The size isn't checked anywhere. How the maximum is obtained? At least
> a comment would be good if the 8k limit is guaranteed by other means.

Yes, I can add a clarifying comment about the 8K. It also should be
possible to create a union of T20/T30 BCT structs and then use use
sizeof(bct_union). I'll consider this change for the next version, thank
you for the suggestion.