diff --git a/Documentation/admin-guide/discoverable-root.rst b/Documentation/admin-guide/discoverable-root.rst new file mode 100644 index 000000000000..9645bf39e405 --- /dev/null +++ b/Documentation/admin-guide/discoverable-root.rst @@ -0,0 +1,33 @@ +.. SPDX-License-Identifier: GPL-2.0 + +.. _discoverable_root: + +Discoverable root partitions +============================ + +On EFI systems using a supported architecture, the kernel can discover the root +block device from GPT partition type UUID metadata on the disk containing the +active EFI System Partition. + +This follows the `Discoverable Partitions Specification`_ which defines a list +of architecture-specific root partition type UUIDs. + +Specifying ``root=`` on the kernel command line takes precedence and entirely +disables this automatic root partition discovery. + +The disk to search is identified by the Boot Loader Interface +``LoaderDevicePartUUID`` EFI variable. If multiple partitions on that disk match +the architecture root partition type UUID, the kernel selects the first match in +block device enumeration order. Systems should not expose multiple eligible root +partitions unless that ordering is intended. + +Partitions marked with the DPS ``no-auto`` GPT attribute are skipped. This +allows a partition with an otherwise discoverable type UUID to opt out from +automatic discovery. + +The DPS read-only attribute is not enforced by kernel root discovery. The +root filesystem is mounted read-only by default unless ``rw`` is specified, +and user space remains responsible for later remount policy. + +.. _Discoverable Partitions Specification: + https://uapi-group.org/specifications/specs/discoverable_partitions_specification/ diff --git a/Documentation/admin-guide/index.rst b/Documentation/admin-guide/index.rst index cd28dfe91b06..0d9c2796ae09 100644 --- a/Documentation/admin-guide/index.rst +++ b/Documentation/admin-guide/index.rst @@ -50,6 +50,7 @@ Booting the kernel bootconfig kernel-parameters + discoverable-root efi-stub initrd diff --git a/Documentation/admin-guide/kernel-parameters.txt b/Documentation/admin-guide/kernel-parameters.txt index a68003c3599c..19d1f559d053 100644 --- a/Documentation/admin-guide/kernel-parameters.txt +++ b/Documentation/admin-guide/kernel-parameters.txt @@ -6691,6 +6691,11 @@ Kernel parameters ramdisk, "nfs" and "cifs" for root on a network file system, or "mtd" and "ubi" for mounting from raw flash. + If this option is omitted, the kernel may try to + discover the root block device from the GPT partition + type UUID metadata when additional requirements are met. + See Documentation/admin-guide/discoverable-root.rst. + rootdelay= [KNL] Delay (in seconds) to pause before attempting to mount the root filesystem diff --git a/arch/alpha/Kconfig b/arch/alpha/Kconfig index 7b7dafe7d9df..400cbb7525c8 100644 --- a/arch/alpha/Kconfig +++ b/arch/alpha/Kconfig @@ -5,6 +5,7 @@ config ALPHA select ARCH_32BIT_USTAT_F_TINODE select ARCH_HAS_CURRENT_STACK_POINTER select ARCH_HAS_DMA_OPS if PCI + select ARCH_HAS_DPS_ROOT_PARTITION_TYPE_UUID select ARCH_MIGHT_HAVE_PC_PARPORT select ARCH_MIGHT_HAVE_PC_SERIO select ARCH_MODULE_NEEDS_WEAK_PER_CPU if SMP diff --git a/arch/alpha/include/asm/dps_root.h b/arch/alpha/include/asm/dps_root.h new file mode 100644 index 000000000000..7f70a83f72de --- /dev/null +++ b/arch/alpha/include/asm/dps_root.h @@ -0,0 +1,8 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ + +#ifndef _ASM_ALPHA_DPS_ROOT_H +#define _ASM_ALPHA_DPS_ROOT_H + +#define DPS_ROOT_PARTITION_TYPE_UUID "6523f8ae-3eb1-4e2a-a05a-18b695ae656f" + +#endif /* _ASM_ALPHA_DPS_ROOT_H */ diff --git a/arch/arc/Kconfig b/arch/arc/Kconfig index 2ed7186c81c5..cc3a57a0111f 100644 --- a/arch/arc/Kconfig +++ b/arch/arc/Kconfig @@ -10,6 +10,7 @@ config ARC select ARCH_HAS_CACHE_LINE_SIZE select ARCH_HAS_DEBUG_VM_PGTABLE select ARCH_HAS_DMA_PREP_COHERENT + select ARCH_HAS_DPS_ROOT_PARTITION_TYPE_UUID select ARCH_HAS_PTE_SPECIAL select ARCH_HAS_SETUP_DMA_OPS select ARCH_HAS_SYNC_DMA_FOR_CPU diff --git a/arch/arc/include/asm/dps_root.h b/arch/arc/include/asm/dps_root.h new file mode 100644 index 000000000000..c9db3ddf1a53 --- /dev/null +++ b/arch/arc/include/asm/dps_root.h @@ -0,0 +1,8 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ + +#ifndef _ASM_ARC_DPS_ROOT_H +#define _ASM_ARC_DPS_ROOT_H + +#define DPS_ROOT_PARTITION_TYPE_UUID "d27f46ed-2919-4cb8-bd25-9531f3c16534" + +#endif /* _ASM_ARC_DPS_ROOT_H */ diff --git a/arch/arm/Kconfig b/arch/arm/Kconfig index 9187240a02db..8004b680cd99 100644 --- a/arch/arm/Kconfig +++ b/arch/arm/Kconfig @@ -14,6 +14,7 @@ config ARM select ARCH_HAS_DMA_ALLOC if MMU select ARCH_HAS_DMA_OPS select ARCH_HAS_DMA_WRITE_COMBINE if !ARM_DMA_MEM_BUFFERABLE + select ARCH_HAS_DPS_ROOT_PARTITION_TYPE_UUID select ARCH_HAS_ELF_RANDOMIZE select ARCH_HAS_FORTIFY_SOURCE select ARCH_HAS_KEEPINITRD diff --git a/arch/arm/include/asm/dps_root.h b/arch/arm/include/asm/dps_root.h new file mode 100644 index 000000000000..e9f0f24bcac2 --- /dev/null +++ b/arch/arm/include/asm/dps_root.h @@ -0,0 +1,8 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ + +#ifndef _ASM_ARM_DPS_ROOT_H +#define _ASM_ARM_DPS_ROOT_H + +#define DPS_ROOT_PARTITION_TYPE_UUID "69dad710-2ce4-4e3c-b16c-21a1d49abed3" + +#endif /* _ASM_ARM_DPS_ROOT_H */ diff --git a/arch/arm64/Kconfig b/arch/arm64/Kconfig index b3afe0688919..f8e956af7146 100644 --- a/arch/arm64/Kconfig +++ b/arch/arm64/Kconfig @@ -26,6 +26,7 @@ config ARM64 select ARCH_HAS_DEBUG_VM_PGTABLE select ARCH_HAS_DMA_OPS if XEN select ARCH_HAS_DMA_PREP_COHERENT + select ARCH_HAS_DPS_ROOT_PARTITION_TYPE_UUID select ARCH_HAS_ACPI_TABLE_UPGRADE if ACPI select ARCH_HAS_FAST_MULTIPLIER select ARCH_HAS_FORTIFY_SOURCE diff --git a/arch/arm64/include/asm/dps_root.h b/arch/arm64/include/asm/dps_root.h new file mode 100644 index 000000000000..7344f9a52343 --- /dev/null +++ b/arch/arm64/include/asm/dps_root.h @@ -0,0 +1,8 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ + +#ifndef _ASM_ARM64_DPS_ROOT_H +#define _ASM_ARM64_DPS_ROOT_H + +#define DPS_ROOT_PARTITION_TYPE_UUID "b921b045-1df0-41c3-af44-4c6f280d3fae" + +#endif /* _ASM_ARM64_DPS_ROOT_H */ diff --git a/arch/loongarch/Kconfig b/arch/loongarch/Kconfig index bec7cc1d72ee..fb6df0b4ae82 100644 --- a/arch/loongarch/Kconfig +++ b/arch/loongarch/Kconfig @@ -17,6 +17,7 @@ config LOONGARCH select ARCH_HAS_CPU_FINALIZE_INIT select ARCH_HAS_CURRENT_STACK_POINTER select ARCH_HAS_DEBUG_VM_PGTABLE + select ARCH_HAS_DPS_ROOT_PARTITION_TYPE_UUID select ARCH_HAS_FAST_MULTIPLIER select ARCH_HAS_FORTIFY_SOURCE select ARCH_HAS_KCOV diff --git a/arch/loongarch/include/asm/dps_root.h b/arch/loongarch/include/asm/dps_root.h new file mode 100644 index 000000000000..6022b60428ad --- /dev/null +++ b/arch/loongarch/include/asm/dps_root.h @@ -0,0 +1,8 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ + +#ifndef _ASM_LOONGARCH_DPS_ROOT_H +#define _ASM_LOONGARCH_DPS_ROOT_H + +#define DPS_ROOT_PARTITION_TYPE_UUID "77055800-792c-4f94-b39a-98c91b762bb6" + +#endif /* _ASM_LOONGARCH_DPS_ROOT_H */ diff --git a/arch/mips/Kconfig b/arch/mips/Kconfig index 8555bbf47c63..c5fab534b705 100644 --- a/arch/mips/Kconfig +++ b/arch/mips/Kconfig @@ -10,6 +10,7 @@ config MIPS select ARCH_HAS_CURRENT_STACK_POINTER select ARCH_HAS_DEBUG_VIRTUAL if !64BIT select ARCH_HAS_DMA_OPS if MACH_JAZZ + select ARCH_HAS_DPS_ROOT_PARTITION_TYPE_UUID select ARCH_HAS_FORTIFY_SOURCE select ARCH_HAS_KCOV select ARCH_HAS_NON_OVERLAPPING_ADDRESS_SPACE if !EVA diff --git a/arch/mips/include/asm/dps_root.h b/arch/mips/include/asm/dps_root.h new file mode 100644 index 000000000000..b07cab7399ad --- /dev/null +++ b/arch/mips/include/asm/dps_root.h @@ -0,0 +1,20 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ + +#ifndef _ASM_MIPS_DPS_ROOT_H +#define _ASM_MIPS_DPS_ROOT_H + +#ifdef CONFIG_CPU_LITTLE_ENDIAN +#ifdef CONFIG_64BIT +#define DPS_ROOT_PARTITION_TYPE_UUID "700bda43-7a34-4507-b179-eeb93d7a7ca3" +#else +#define DPS_ROOT_PARTITION_TYPE_UUID "37c58c8a-d913-4156-a25f-48b1b64e07f0" +#endif +#else +#ifdef CONFIG_64BIT +#define DPS_ROOT_PARTITION_TYPE_UUID "d113af76-80ef-41b4-bdb6-0cff4d3d4a25" +#else +#define DPS_ROOT_PARTITION_TYPE_UUID "e9434544-6e2c-47cc-bae2-12d6deafb44c" +#endif +#endif + +#endif /* _ASM_MIPS_DPS_ROOT_H */ diff --git a/arch/parisc/Kconfig b/arch/parisc/Kconfig index d3afac2f0d9b..94f0a758176a 100644 --- a/arch/parisc/Kconfig +++ b/arch/parisc/Kconfig @@ -24,6 +24,7 @@ config PARISC select ARCH_STACKWALK select ARCH_HAS_CACHE_LINE_SIZE select ARCH_HAS_DEBUG_VM_PGTABLE + select ARCH_HAS_DPS_ROOT_PARTITION_TYPE_UUID select HAVE_RELIABLE_STACKTRACE select RTC_CLASS select RTC_DRV_GENERIC diff --git a/arch/parisc/include/asm/dps_root.h b/arch/parisc/include/asm/dps_root.h new file mode 100644 index 000000000000..bf475cd5b01c --- /dev/null +++ b/arch/parisc/include/asm/dps_root.h @@ -0,0 +1,8 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ + +#ifndef _ASM_PARISC_DPS_ROOT_H +#define _ASM_PARISC_DPS_ROOT_H + +#define DPS_ROOT_PARTITION_TYPE_UUID "1aacdb3b-5444-4138-bd9e-e5c2239b2346" + +#endif /* _ASM_PARISC_DPS_ROOT_H */ diff --git a/arch/powerpc/Kconfig b/arch/powerpc/Kconfig index f7ce5fff81f0..0b070a552b50 100644 --- a/arch/powerpc/Kconfig +++ b/arch/powerpc/Kconfig @@ -134,6 +134,7 @@ config PPC select ARCH_HAS_DEVMEM_IS_ALLOWED select ARCH_HAS_DMA_MAP_DIRECT if PPC_PSERIES select ARCH_HAS_DMA_OPS if PPC64 + select ARCH_HAS_DPS_ROOT_PARTITION_TYPE_UUID select ARCH_HAS_FORTIFY_SOURCE select ARCH_HAS_GCOV_PROFILE_ALL select ARCH_HAS_GIGANTIC_PAGE if ARCH_SUPPORTS_HUGETLBFS diff --git a/arch/powerpc/include/asm/dps_root.h b/arch/powerpc/include/asm/dps_root.h new file mode 100644 index 000000000000..6b3592a73d3d --- /dev/null +++ b/arch/powerpc/include/asm/dps_root.h @@ -0,0 +1,16 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ + +#ifndef _ASM_POWERPC_DPS_ROOT_H +#define _ASM_POWERPC_DPS_ROOT_H + +#ifdef CONFIG_PPC64 +#ifdef CONFIG_CPU_LITTLE_ENDIAN +#define DPS_ROOT_PARTITION_TYPE_UUID "c31c45e6-3f39-412e-80fb-4809c4980599" +#else +#define DPS_ROOT_PARTITION_TYPE_UUID "912ade1d-a839-4913-8964-a10eee08fbd2" +#endif +#else +#define DPS_ROOT_PARTITION_TYPE_UUID "1de3f1ef-fa98-47b5-8dcd-4a860a654d78" +#endif + +#endif /* _ASM_POWERPC_DPS_ROOT_H */ diff --git a/arch/riscv/Kconfig b/arch/riscv/Kconfig index 3f0a647218e4..ea2dbd69c5a0 100644 --- a/arch/riscv/Kconfig +++ b/arch/riscv/Kconfig @@ -29,6 +29,7 @@ config RISCV select ARCH_HAS_DEBUG_VIRTUAL if MMU select ARCH_HAS_DEBUG_VM_PGTABLE select ARCH_HAS_DEBUG_WX + select ARCH_HAS_DPS_ROOT_PARTITION_TYPE_UUID select ARCH_HAS_ELF_CORE_EFLAGS if BINFMT_ELF && ELF_CORE select ARCH_HAS_FAST_MULTIPLIER select ARCH_HAS_FORTIFY_SOURCE diff --git a/arch/riscv/include/asm/dps_root.h b/arch/riscv/include/asm/dps_root.h new file mode 100644 index 000000000000..86f8e1dbd6ad --- /dev/null +++ b/arch/riscv/include/asm/dps_root.h @@ -0,0 +1,12 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ + +#ifndef _ASM_RISCV_DPS_ROOT_H +#define _ASM_RISCV_DPS_ROOT_H + +#ifdef CONFIG_64BIT +#define DPS_ROOT_PARTITION_TYPE_UUID "72ec70a6-cf74-40e6-bd49-4bda08e8f224" +#else +#define DPS_ROOT_PARTITION_TYPE_UUID "60d5a7fe-8e7d-435c-b714-3dd8162144e1" +#endif + +#endif /* _ASM_RISCV_DPS_ROOT_H */ diff --git a/arch/s390/Kconfig b/arch/s390/Kconfig index 84404e6778d5..33d2dce27170 100644 --- a/arch/s390/Kconfig +++ b/arch/s390/Kconfig @@ -91,6 +91,7 @@ config S390 select ARCH_HAS_DEBUG_WX select ARCH_HAS_DEVMEM_IS_ALLOWED select ARCH_HAS_DMA_OPS if PCI + select ARCH_HAS_DPS_ROOT_PARTITION_TYPE_UUID select ARCH_HAS_ELF_RANDOMIZE select ARCH_HAS_FORCE_DMA_UNENCRYPTED select ARCH_HAS_FORTIFY_SOURCE diff --git a/arch/s390/include/asm/dps_root.h b/arch/s390/include/asm/dps_root.h new file mode 100644 index 000000000000..e72e44a66097 --- /dev/null +++ b/arch/s390/include/asm/dps_root.h @@ -0,0 +1,12 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ + +#ifndef _ASM_S390_DPS_ROOT_H +#define _ASM_S390_DPS_ROOT_H + +#ifdef CONFIG_64BIT +#define DPS_ROOT_PARTITION_TYPE_UUID "5eead9a9-fe09-4a1e-a1d7-520d00531306" +#else +#define DPS_ROOT_PARTITION_TYPE_UUID "08a7acea-624c-4a20-91e8-6e0fa67d23f9" +#endif + +#endif /* _ASM_S390_DPS_ROOT_H */ diff --git a/arch/x86/Kconfig b/arch/x86/Kconfig index fa4f0079614c..0f35b399fd39 100644 --- a/arch/x86/Kconfig +++ b/arch/x86/Kconfig @@ -82,6 +82,7 @@ config X86 select ARCH_HAS_DEBUG_VM_PGTABLE if !X86_PAE select ARCH_HAS_DEVMEM_IS_ALLOWED select ARCH_HAS_DMA_OPS if GART_IOMMU || XEN + select ARCH_HAS_DPS_ROOT_PARTITION_TYPE_UUID select ARCH_HAS_EARLY_DEBUG if KGDB select ARCH_HAS_ELF_RANDOMIZE select ARCH_HAS_EXECMEM_ROX if X86_64 && STRICT_MODULE_RWX diff --git a/arch/x86/include/asm/dps_root.h b/arch/x86/include/asm/dps_root.h new file mode 100644 index 000000000000..7c6ba5519d88 --- /dev/null +++ b/arch/x86/include/asm/dps_root.h @@ -0,0 +1,12 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ + +#ifndef _ASM_X86_DPS_ROOT_H +#define _ASM_X86_DPS_ROOT_H + +#ifdef CONFIG_X86_64 +#define DPS_ROOT_PARTITION_TYPE_UUID "4f68bce3-e8cd-4db1-96e7-fbcaf984b709" +#else +#define DPS_ROOT_PARTITION_TYPE_UUID "44479540-f297-41b2-9af7-d131d5f0458a" +#endif + +#endif /* _ASM_X86_DPS_ROOT_H */ diff --git a/block/blk.h b/block/blk.h index 25af8ac5ef0f..8bf4ee55b22b 100644 --- a/block/blk.h +++ b/block/blk.h @@ -632,6 +632,7 @@ void blk_free_ext_minor(unsigned int minor); #define ADDPART_FLAG_RAID 1 #define ADDPART_FLAG_WHOLEDISK 2 #define ADDPART_FLAG_READONLY 4 +#define ADDPART_FLAG_NO_AUTO 8 int bdev_add_partition(struct gendisk *disk, int partno, sector_t start, sector_t length); int bdev_del_partition(struct gendisk *disk, int partno); diff --git a/block/early-lookup.c b/block/early-lookup.c index 3fb57f7d2b12..8db0abec141e 100644 --- a/block/early-lookup.c +++ b/block/early-lookup.c @@ -5,10 +5,12 @@ */ #include #include +#include struct uuidcmp { const char *uuid; int len; + struct gendisk *disk; }; /** @@ -45,13 +47,11 @@ static int __init match_dev_by_uuid(struct device *dev, const void *data) */ static int __init devt_from_partuuid(const char *uuid_str, dev_t *devt) { - struct uuidcmp cmp; + struct uuidcmp cmp = { .uuid = uuid_str }; struct device *dev = NULL; int offset = 0; char *slash; - cmp.uuid = uuid_str; - slash = strchr(uuid_str, '/'); /* Check for optional partition number offset attributes. */ if (slash) { @@ -252,6 +252,68 @@ int __init early_lookup_bdev(const char *name, dev_t *devt) return devt_from_devnum(name, devt); } +#ifdef CONFIG_DPS_ROOT_AUTO_DISCOVERY +/** + * match_dev_by_type_uuid - callback for finding a partition using its type UUID + * @dev: device passed in by the caller + * @data: opaque pointer to the desired struct uuidcmp to match + * + * Returns: 1 if the device matches, and 0 otherwise. + */ +static int __init match_dev_by_type_uuid(struct device *dev, const void *data) +{ + struct block_device *bdev = dev_to_bdev(dev); + const struct uuidcmp *cmp = data; + + return bdev->bd_disk == cmp->disk && bdev->bd_meta_info && + !bdev_test_flag(bdev, BD_NO_AUTO_DISCOVERY) && + !strcasecmp(cmp->uuid, bdev->bd_meta_info->type_uuid); +} + +/** + * early_lookup_bdev_by_type_uuid - look up a partition by its type UUID + * @type_uuid: partition type UUID to search for + * @efi_partuuid: partition UUID identifying the active EFI partition + * @devt: matching dev_t result + * + * This helper follows the Discoverable Partitions Specification rules. It uses + * @efi_partuuid to find the disk containing the active EFI System Partition, + * then searches only partitions on that disk for the partition type UUID + * specified by @type_uuid. + * + * Returns: 0 on success or a negative error code on failure. + */ +int __init early_lookup_bdev_by_type_uuid(const char *type_uuid, + const char *efi_partuuid, dev_t *devt) +{ + struct uuidcmp efi_cmp = { + .uuid = efi_partuuid, + .len = UUID_STRING_LEN, + }; + struct uuidcmp type_cmp = { + .uuid = type_uuid, + }; + struct device *efi_dev; + struct device *type_dev; + + efi_dev = class_find_device(&block_class, NULL, &efi_cmp, + &match_dev_by_uuid); + if (!efi_dev) + return -ENODEV; + + type_cmp.disk = dev_to_disk(efi_dev); + type_dev = class_find_device(&block_class, NULL, &type_cmp, + &match_dev_by_type_uuid); + put_device(efi_dev); + if (!type_dev) + return -ENODEV; + + *devt = type_dev->devt; + put_device(type_dev); + return 0; +} +#endif + static char __init *bdevt_str(dev_t devt, char *buf) { if (MAJOR(devt) <= 0xff && MINOR(devt) <= 0xff) { diff --git a/block/partitions/core.c b/block/partitions/core.c index b5c59b79ca7c..5a5638d9cee1 100644 --- a/block/partitions/core.c +++ b/block/partitions/core.c @@ -382,6 +382,8 @@ static struct block_device *add_partition(struct gendisk *disk, int partno, if (flags & ADDPART_FLAG_READONLY) bdev_set_flag(bdev, BD_READ_ONLY); + if (flags & ADDPART_FLAG_NO_AUTO) + bdev_set_flag(bdev, BD_NO_AUTO_DISCOVERY); /* everything is up and running, commence */ err = xa_insert(&disk->part_tbl, partno, bdev, GFP_KERNEL); diff --git a/block/partitions/efi.c b/block/partitions/efi.c index 9865d59093fa..50c21625e256 100644 --- a/block/partitions/efi.c +++ b/block/partitions/efi.c @@ -739,9 +739,12 @@ int efi_partition(struct parsed_partitions *state) /* If this is a RAID volume, tell md */ if (!efi_guidcmp(ptes[i].partition_type_guid, PARTITION_LINUX_RAID_GUID)) state->parts[i + 1].flags = ADDPART_FLAG_RAID; + if (le64_to_cpu(ptes[i].attributes) & GPT_ATTRIBUTE_NO_AUTO) + state->parts[i + 1].flags |= ADDPART_FLAG_NO_AUTO; info = &state->parts[i + 1].info; efi_guid_to_str(&ptes[i].unique_partition_guid, info->uuid); + efi_guid_to_str(&ptes[i].partition_type_guid, info->type_uuid); /* Naively convert UTF16-LE to 7 bits. */ label_max = min(ARRAY_SIZE(info->volname) - 1, diff --git a/block/partitions/efi.h b/block/partitions/efi.h index 84b9f36b9e47..fb50edb66e84 100644 --- a/block/partitions/efi.h +++ b/block/partitions/efi.h @@ -11,6 +11,7 @@ #ifndef FS_PART_EFI_H_INCLUDED #define FS_PART_EFI_H_INCLUDED +#include #include #include #include @@ -30,6 +31,8 @@ #define GPT_HEADER_REVISION_V1 0x00010000 #define GPT_PRIMARY_PARTITION_TABLE_LBA 1 +#define GPT_ATTRIBUTE_NO_AUTO BIT_U64(63) + #define PARTITION_SYSTEM_GUID \ EFI_GUID( 0xC12A7328, 0xF81F, 0x11d2, \ 0xBA, 0x4B, 0x00, 0xA0, 0xC9, 0x3E, 0xC9, 0x3B) @@ -75,18 +78,12 @@ typedef struct _gpt_header { */ } __packed gpt_header; -typedef struct _gpt_entry_attributes { - u64 required_to_function:1; - u64 reserved:47; - u64 type_guid_specific:16; -} __packed gpt_entry_attributes; - typedef struct _gpt_entry { efi_guid_t partition_type_guid; efi_guid_t unique_partition_guid; __le64 starting_lba; __le64 ending_lba; - gpt_entry_attributes attributes; + __le64 attributes; __le16 partition_name[72/sizeof(__le16)]; } __packed gpt_entry; diff --git a/include/linux/blk_types.h b/include/linux/blk_types.h index 8808ee76e73c..c6cdc99b0490 100644 --- a/include/linux/blk_types.h +++ b/include/linux/blk_types.h @@ -54,6 +54,7 @@ struct block_device { #ifdef CONFIG_FAIL_MAKE_REQUEST #define BD_MAKE_IT_FAIL (1u<<12) #endif +#define BD_NO_AUTO_DISCOVERY (1u<<13) dev_t bd_dev; struct address_space *bd_mapping; /* page cache */ diff --git a/include/linux/blkdev.h b/include/linux/blkdev.h index 5070851cf924..64572fd05521 100644 --- a/include/linux/blkdev.h +++ b/include/linux/blkdev.h @@ -67,6 +67,7 @@ extern const struct class block_class; struct partition_meta_info { char uuid[PARTITION_META_INFO_UUIDLTH]; + char type_uuid[PARTITION_META_INFO_UUIDLTH]; u8 volname[PARTITION_META_INFO_VOLNAMELTH]; }; @@ -1811,6 +1812,10 @@ void sync_bdevs(bool wait); void bdev_statx(const struct path *path, struct kstat *stat, u32 request_mask); void printk_all_partitions(void); int __init early_lookup_bdev(const char *pathname, dev_t *dev); +#ifdef CONFIG_DPS_ROOT_AUTO_DISCOVERY +int __init early_lookup_bdev_by_type_uuid(const char *type_uuid, + const char *efi_partuuid, dev_t *dev); +#endif #else static inline void invalidate_bdev(struct block_device *bdev) { diff --git a/include/linux/root_dev.h b/include/linux/root_dev.h index 847c9a06101b..6b52a10b0bca 100644 --- a/include/linux/root_dev.h +++ b/include/linux/root_dev.h @@ -5,6 +5,12 @@ #include #include #include +#include + +#ifdef CONFIG_DPS_ROOT_AUTO_DISCOVERY +#include +static_assert(sizeof(DPS_ROOT_PARTITION_TYPE_UUID) == UUID_STRING_LEN + 1); +#endif enum { Root_NFS = MKDEV(UNNAMED_MAJOR, 255), diff --git a/init/Kconfig b/init/Kconfig index 5230d4879b1c..6a7df83513ff 100644 --- a/init/Kconfig +++ b/init/Kconfig @@ -2307,6 +2307,12 @@ config ARCH_HAS_PREPARE_SYNC_CORE_CMD config ARCH_HAS_SYNC_CORE_BEFORE_USERMODE bool +config ARCH_HAS_DPS_ROOT_PARTITION_TYPE_UUID + bool + +config DPS_ROOT_AUTO_DISCOVERY + def_bool BLOCK && EFI && ARCH_HAS_DPS_ROOT_PARTITION_TYPE_UUID + # It may be useful for an architecture to override the definitions of the # SYSCALL_DEFINE() and __SYSCALL_DEFINEx() macros in # and the COMPAT_ variants in , in particular to use a diff --git a/init/do_mounts.c b/init/do_mounts.c index 95e0b3a0f711..20c176945b32 100644 --- a/init/do_mounts.c +++ b/init/do_mounts.c @@ -10,6 +10,7 @@ #include #include #include +#include #include #include #include @@ -19,6 +20,8 @@ #include #include #include +#include +#include #include #include @@ -402,6 +405,88 @@ void __init mount_root(char *root_device_name) } } +#ifdef CONFIG_DPS_ROOT_AUTO_DISCOVERY +static char efi_partuuid[EFI_VARIABLE_GUID_LEN + 1] __initdata; + +static int __init efi_loader_get_device_part_uuid(char *efi_uuid, size_t size) +{ + efi_char16_t efi_uuid_ucs2[EFI_VARIABLE_GUID_LEN + 1] = {}; + unsigned long efi_uuid_ucs2_size = sizeof(efi_uuid_ucs2); + efi_status_t status; + + if (!efi_rt_services_supported(EFI_RT_SUPPORTED_GET_VARIABLE)) + return -EOPNOTSUPP; + + status = efi.get_variable(L"LoaderDevicePartUUID", + &LINUX_EFI_LOADER_ENTRY_GUID, NULL, + &efi_uuid_ucs2_size, efi_uuid_ucs2); + if (status != EFI_SUCCESS) + return efi_status_to_err(status); + + if (ucs2_as_utf8((u8 *)efi_uuid, efi_uuid_ucs2, size) != UUID_STRING_LEN || + !uuid_is_valid(efi_uuid)) + return -EINVAL; + + return 0; +} + +static int __init lookup_dps_root(dev_t *dev) +{ + static const char dps_root_partition_type_uuid[] __initconst = + DPS_ROOT_PARTITION_TYPE_UUID; + int err; + + err = early_lookup_bdev_by_type_uuid(dps_root_partition_type_uuid, + efi_partuuid, dev); + if (!err) + pr_info("VFS: Discovered root partition with GPT type UUID %s\n", + dps_root_partition_type_uuid); + + return err; +} + +static dev_t __init try_dps_root_discovery(void) +{ + dev_t dev; + int err; + + err = efi_loader_get_device_part_uuid(efi_partuuid, + sizeof(efi_partuuid)); + if (err) { + pr_err("VFS: Unable to get LoaderDevicePartUUID EFI variable: %pe, skipping root partition discovery\n", + ERR_PTR(err)); + if (root_wait) { + pr_err("Disabling rootwait\n"); + root_wait = 0; + } + return 0; + } + + if (!lookup_dps_root(&dev)) + return dev; + + return 0; +} +#else +static int __init lookup_dps_root(dev_t *dev) +{ + return 0; +} + +static dev_t __init try_dps_root_discovery(void) +{ + return 0; +} +#endif + +static int __init lookup_root_device(char *root_device_name) +{ + if (root_device_name[0]) + return early_lookup_bdev(root_device_name, &ROOT_DEV); + else + return lookup_dps_root(&ROOT_DEV); +} + /* wait for any asynchronous scanning to complete */ static void __init wait_for_root(char *root_device_name) { @@ -410,12 +495,15 @@ static void __init wait_for_root(char *root_device_name) if (ROOT_DEV != 0) return; - pr_info("Waiting for root device %s...\n", root_device_name); + if (root_device_name[0]) + pr_info("Waiting for root device %s...\n", root_device_name); + else if (IS_ENABLED(CONFIG_DPS_ROOT_AUTO_DISCOVERY)) + pr_info("Waiting for discoverable root partition...\n"); end = ktime_add_ms(ktime_get_raw(), root_wait); while (!driver_probe_done() || - early_lookup_bdev(root_device_name, &ROOT_DEV) < 0) { + lookup_root_device(root_device_name) < 0) { msleep(5); if (root_wait > 0 && ktime_after(ktime_get_raw(), end)) break; @@ -475,6 +563,8 @@ void __init prepare_namespace(void) if (saved_root_name[0]) ROOT_DEV = parse_root_device(saved_root_name); + else + ROOT_DEV = try_dps_root_discovery(); initrd_load();