162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * Code for looking up block devices in the early boot code before mounting the 462306a36Sopenharmony_ci * root file system. 562306a36Sopenharmony_ci */ 662306a36Sopenharmony_ci#include <linux/blkdev.h> 762306a36Sopenharmony_ci#include <linux/ctype.h> 862306a36Sopenharmony_ci 962306a36Sopenharmony_cistruct uuidcmp { 1062306a36Sopenharmony_ci const char *uuid; 1162306a36Sopenharmony_ci int len; 1262306a36Sopenharmony_ci}; 1362306a36Sopenharmony_ci 1462306a36Sopenharmony_ci/** 1562306a36Sopenharmony_ci * match_dev_by_uuid - callback for finding a partition using its uuid 1662306a36Sopenharmony_ci * @dev: device passed in by the caller 1762306a36Sopenharmony_ci * @data: opaque pointer to the desired struct uuidcmp to match 1862306a36Sopenharmony_ci * 1962306a36Sopenharmony_ci * Returns 1 if the device matches, and 0 otherwise. 2062306a36Sopenharmony_ci */ 2162306a36Sopenharmony_cistatic int __init match_dev_by_uuid(struct device *dev, const void *data) 2262306a36Sopenharmony_ci{ 2362306a36Sopenharmony_ci struct block_device *bdev = dev_to_bdev(dev); 2462306a36Sopenharmony_ci const struct uuidcmp *cmp = data; 2562306a36Sopenharmony_ci 2662306a36Sopenharmony_ci if (!bdev->bd_meta_info || 2762306a36Sopenharmony_ci strncasecmp(cmp->uuid, bdev->bd_meta_info->uuid, cmp->len)) 2862306a36Sopenharmony_ci return 0; 2962306a36Sopenharmony_ci return 1; 3062306a36Sopenharmony_ci} 3162306a36Sopenharmony_ci 3262306a36Sopenharmony_ci/** 3362306a36Sopenharmony_ci * devt_from_partuuid - looks up the dev_t of a partition by its UUID 3462306a36Sopenharmony_ci * @uuid_str: char array containing ascii UUID 3562306a36Sopenharmony_ci * @devt: dev_t result 3662306a36Sopenharmony_ci * 3762306a36Sopenharmony_ci * The function will return the first partition which contains a matching 3862306a36Sopenharmony_ci * UUID value in its partition_meta_info struct. This does not search 3962306a36Sopenharmony_ci * by filesystem UUIDs. 4062306a36Sopenharmony_ci * 4162306a36Sopenharmony_ci * If @uuid_str is followed by a "/PARTNROFF=%d", then the number will be 4262306a36Sopenharmony_ci * extracted and used as an offset from the partition identified by the UUID. 4362306a36Sopenharmony_ci * 4462306a36Sopenharmony_ci * Returns 0 on success or a negative error code on failure. 4562306a36Sopenharmony_ci */ 4662306a36Sopenharmony_cistatic int __init devt_from_partuuid(const char *uuid_str, dev_t *devt) 4762306a36Sopenharmony_ci{ 4862306a36Sopenharmony_ci struct uuidcmp cmp; 4962306a36Sopenharmony_ci struct device *dev = NULL; 5062306a36Sopenharmony_ci int offset = 0; 5162306a36Sopenharmony_ci char *slash; 5262306a36Sopenharmony_ci 5362306a36Sopenharmony_ci cmp.uuid = uuid_str; 5462306a36Sopenharmony_ci 5562306a36Sopenharmony_ci slash = strchr(uuid_str, '/'); 5662306a36Sopenharmony_ci /* Check for optional partition number offset attributes. */ 5762306a36Sopenharmony_ci if (slash) { 5862306a36Sopenharmony_ci char c = 0; 5962306a36Sopenharmony_ci 6062306a36Sopenharmony_ci /* Explicitly fail on poor PARTUUID syntax. */ 6162306a36Sopenharmony_ci if (sscanf(slash + 1, "PARTNROFF=%d%c", &offset, &c) != 1) 6262306a36Sopenharmony_ci goto out_invalid; 6362306a36Sopenharmony_ci cmp.len = slash - uuid_str; 6462306a36Sopenharmony_ci } else { 6562306a36Sopenharmony_ci cmp.len = strlen(uuid_str); 6662306a36Sopenharmony_ci } 6762306a36Sopenharmony_ci 6862306a36Sopenharmony_ci if (!cmp.len) 6962306a36Sopenharmony_ci goto out_invalid; 7062306a36Sopenharmony_ci 7162306a36Sopenharmony_ci dev = class_find_device(&block_class, NULL, &cmp, &match_dev_by_uuid); 7262306a36Sopenharmony_ci if (!dev) 7362306a36Sopenharmony_ci return -ENODEV; 7462306a36Sopenharmony_ci 7562306a36Sopenharmony_ci if (offset) { 7662306a36Sopenharmony_ci /* 7762306a36Sopenharmony_ci * Attempt to find the requested partition by adding an offset 7862306a36Sopenharmony_ci * to the partition number found by UUID. 7962306a36Sopenharmony_ci */ 8062306a36Sopenharmony_ci *devt = part_devt(dev_to_disk(dev), 8162306a36Sopenharmony_ci dev_to_bdev(dev)->bd_partno + offset); 8262306a36Sopenharmony_ci } else { 8362306a36Sopenharmony_ci *devt = dev->devt; 8462306a36Sopenharmony_ci } 8562306a36Sopenharmony_ci 8662306a36Sopenharmony_ci put_device(dev); 8762306a36Sopenharmony_ci return 0; 8862306a36Sopenharmony_ci 8962306a36Sopenharmony_ciout_invalid: 9062306a36Sopenharmony_ci pr_err("VFS: PARTUUID= is invalid.\n" 9162306a36Sopenharmony_ci "Expected PARTUUID=<valid-uuid-id>[/PARTNROFF=%%d]\n"); 9262306a36Sopenharmony_ci return -EINVAL; 9362306a36Sopenharmony_ci} 9462306a36Sopenharmony_ci 9562306a36Sopenharmony_ci/** 9662306a36Sopenharmony_ci * match_dev_by_label - callback for finding a partition using its label 9762306a36Sopenharmony_ci * @dev: device passed in by the caller 9862306a36Sopenharmony_ci * @data: opaque pointer to the label to match 9962306a36Sopenharmony_ci * 10062306a36Sopenharmony_ci * Returns 1 if the device matches, and 0 otherwise. 10162306a36Sopenharmony_ci */ 10262306a36Sopenharmony_cistatic int __init match_dev_by_label(struct device *dev, const void *data) 10362306a36Sopenharmony_ci{ 10462306a36Sopenharmony_ci struct block_device *bdev = dev_to_bdev(dev); 10562306a36Sopenharmony_ci const char *label = data; 10662306a36Sopenharmony_ci 10762306a36Sopenharmony_ci if (!bdev->bd_meta_info || strcmp(label, bdev->bd_meta_info->volname)) 10862306a36Sopenharmony_ci return 0; 10962306a36Sopenharmony_ci return 1; 11062306a36Sopenharmony_ci} 11162306a36Sopenharmony_ci 11262306a36Sopenharmony_cistatic int __init devt_from_partlabel(const char *label, dev_t *devt) 11362306a36Sopenharmony_ci{ 11462306a36Sopenharmony_ci struct device *dev; 11562306a36Sopenharmony_ci 11662306a36Sopenharmony_ci dev = class_find_device(&block_class, NULL, label, &match_dev_by_label); 11762306a36Sopenharmony_ci if (!dev) 11862306a36Sopenharmony_ci return -ENODEV; 11962306a36Sopenharmony_ci *devt = dev->devt; 12062306a36Sopenharmony_ci put_device(dev); 12162306a36Sopenharmony_ci return 0; 12262306a36Sopenharmony_ci} 12362306a36Sopenharmony_ci 12462306a36Sopenharmony_cistatic dev_t __init blk_lookup_devt(const char *name, int partno) 12562306a36Sopenharmony_ci{ 12662306a36Sopenharmony_ci dev_t devt = MKDEV(0, 0); 12762306a36Sopenharmony_ci struct class_dev_iter iter; 12862306a36Sopenharmony_ci struct device *dev; 12962306a36Sopenharmony_ci 13062306a36Sopenharmony_ci class_dev_iter_init(&iter, &block_class, NULL, &disk_type); 13162306a36Sopenharmony_ci while ((dev = class_dev_iter_next(&iter))) { 13262306a36Sopenharmony_ci struct gendisk *disk = dev_to_disk(dev); 13362306a36Sopenharmony_ci 13462306a36Sopenharmony_ci if (strcmp(dev_name(dev), name)) 13562306a36Sopenharmony_ci continue; 13662306a36Sopenharmony_ci 13762306a36Sopenharmony_ci if (partno < disk->minors) { 13862306a36Sopenharmony_ci /* We need to return the right devno, even 13962306a36Sopenharmony_ci * if the partition doesn't exist yet. 14062306a36Sopenharmony_ci */ 14162306a36Sopenharmony_ci devt = MKDEV(MAJOR(dev->devt), 14262306a36Sopenharmony_ci MINOR(dev->devt) + partno); 14362306a36Sopenharmony_ci } else { 14462306a36Sopenharmony_ci devt = part_devt(disk, partno); 14562306a36Sopenharmony_ci if (devt) 14662306a36Sopenharmony_ci break; 14762306a36Sopenharmony_ci } 14862306a36Sopenharmony_ci } 14962306a36Sopenharmony_ci class_dev_iter_exit(&iter); 15062306a36Sopenharmony_ci return devt; 15162306a36Sopenharmony_ci} 15262306a36Sopenharmony_ci 15362306a36Sopenharmony_cistatic int __init devt_from_devname(const char *name, dev_t *devt) 15462306a36Sopenharmony_ci{ 15562306a36Sopenharmony_ci int part; 15662306a36Sopenharmony_ci char s[32]; 15762306a36Sopenharmony_ci char *p; 15862306a36Sopenharmony_ci 15962306a36Sopenharmony_ci if (strlen(name) > 31) 16062306a36Sopenharmony_ci return -EINVAL; 16162306a36Sopenharmony_ci strcpy(s, name); 16262306a36Sopenharmony_ci for (p = s; *p; p++) { 16362306a36Sopenharmony_ci if (*p == '/') 16462306a36Sopenharmony_ci *p = '!'; 16562306a36Sopenharmony_ci } 16662306a36Sopenharmony_ci 16762306a36Sopenharmony_ci *devt = blk_lookup_devt(s, 0); 16862306a36Sopenharmony_ci if (*devt) 16962306a36Sopenharmony_ci return 0; 17062306a36Sopenharmony_ci 17162306a36Sopenharmony_ci /* 17262306a36Sopenharmony_ci * Try non-existent, but valid partition, which may only exist after 17362306a36Sopenharmony_ci * opening the device, like partitioned md devices. 17462306a36Sopenharmony_ci */ 17562306a36Sopenharmony_ci while (p > s && isdigit(p[-1])) 17662306a36Sopenharmony_ci p--; 17762306a36Sopenharmony_ci if (p == s || !*p || *p == '0') 17862306a36Sopenharmony_ci return -ENODEV; 17962306a36Sopenharmony_ci 18062306a36Sopenharmony_ci /* try disk name without <part number> */ 18162306a36Sopenharmony_ci part = simple_strtoul(p, NULL, 10); 18262306a36Sopenharmony_ci *p = '\0'; 18362306a36Sopenharmony_ci *devt = blk_lookup_devt(s, part); 18462306a36Sopenharmony_ci if (*devt) 18562306a36Sopenharmony_ci return 0; 18662306a36Sopenharmony_ci 18762306a36Sopenharmony_ci /* try disk name without p<part number> */ 18862306a36Sopenharmony_ci if (p < s + 2 || !isdigit(p[-2]) || p[-1] != 'p') 18962306a36Sopenharmony_ci return -ENODEV; 19062306a36Sopenharmony_ci p[-1] = '\0'; 19162306a36Sopenharmony_ci *devt = blk_lookup_devt(s, part); 19262306a36Sopenharmony_ci if (*devt) 19362306a36Sopenharmony_ci return 0; 19462306a36Sopenharmony_ci return -ENODEV; 19562306a36Sopenharmony_ci} 19662306a36Sopenharmony_ci 19762306a36Sopenharmony_cistatic int __init devt_from_devnum(const char *name, dev_t *devt) 19862306a36Sopenharmony_ci{ 19962306a36Sopenharmony_ci unsigned maj, min, offset; 20062306a36Sopenharmony_ci char *p, dummy; 20162306a36Sopenharmony_ci 20262306a36Sopenharmony_ci if (sscanf(name, "%u:%u%c", &maj, &min, &dummy) == 2 || 20362306a36Sopenharmony_ci sscanf(name, "%u:%u:%u:%c", &maj, &min, &offset, &dummy) == 3) { 20462306a36Sopenharmony_ci *devt = MKDEV(maj, min); 20562306a36Sopenharmony_ci if (maj != MAJOR(*devt) || min != MINOR(*devt)) 20662306a36Sopenharmony_ci return -EINVAL; 20762306a36Sopenharmony_ci } else { 20862306a36Sopenharmony_ci *devt = new_decode_dev(simple_strtoul(name, &p, 16)); 20962306a36Sopenharmony_ci if (*p) 21062306a36Sopenharmony_ci return -EINVAL; 21162306a36Sopenharmony_ci } 21262306a36Sopenharmony_ci 21362306a36Sopenharmony_ci return 0; 21462306a36Sopenharmony_ci} 21562306a36Sopenharmony_ci 21662306a36Sopenharmony_ci/* 21762306a36Sopenharmony_ci * Convert a name into device number. We accept the following variants: 21862306a36Sopenharmony_ci * 21962306a36Sopenharmony_ci * 1) <hex_major><hex_minor> device number in hexadecimal represents itself 22062306a36Sopenharmony_ci * no leading 0x, for example b302. 22162306a36Sopenharmony_ci * 3) /dev/<disk_name> represents the device number of disk 22262306a36Sopenharmony_ci * 4) /dev/<disk_name><decimal> represents the device number 22362306a36Sopenharmony_ci * of partition - device number of disk plus the partition number 22462306a36Sopenharmony_ci * 5) /dev/<disk_name>p<decimal> - same as the above, that form is 22562306a36Sopenharmony_ci * used when disk name of partitioned disk ends on a digit. 22662306a36Sopenharmony_ci * 6) PARTUUID=00112233-4455-6677-8899-AABBCCDDEEFF representing the 22762306a36Sopenharmony_ci * unique id of a partition if the partition table provides it. 22862306a36Sopenharmony_ci * The UUID may be either an EFI/GPT UUID, or refer to an MSDOS 22962306a36Sopenharmony_ci * partition using the format SSSSSSSS-PP, where SSSSSSSS is a zero- 23062306a36Sopenharmony_ci * filled hex representation of the 32-bit "NT disk signature", and PP 23162306a36Sopenharmony_ci * is a zero-filled hex representation of the 1-based partition number. 23262306a36Sopenharmony_ci * 7) PARTUUID=<UUID>/PARTNROFF=<int> to select a partition in relation to 23362306a36Sopenharmony_ci * a partition with a known unique id. 23462306a36Sopenharmony_ci * 8) <major>:<minor> major and minor number of the device separated by 23562306a36Sopenharmony_ci * a colon. 23662306a36Sopenharmony_ci * 9) PARTLABEL=<name> with name being the GPT partition label. 23762306a36Sopenharmony_ci * MSDOS partitions do not support labels! 23862306a36Sopenharmony_ci * 23962306a36Sopenharmony_ci * If name doesn't have fall into the categories above, we return (0,0). 24062306a36Sopenharmony_ci * block_class is used to check if something is a disk name. If the disk 24162306a36Sopenharmony_ci * name contains slashes, the device name has them replaced with 24262306a36Sopenharmony_ci * bangs. 24362306a36Sopenharmony_ci */ 24462306a36Sopenharmony_ciint __init early_lookup_bdev(const char *name, dev_t *devt) 24562306a36Sopenharmony_ci{ 24662306a36Sopenharmony_ci if (strncmp(name, "PARTUUID=", 9) == 0) 24762306a36Sopenharmony_ci return devt_from_partuuid(name + 9, devt); 24862306a36Sopenharmony_ci if (strncmp(name, "PARTLABEL=", 10) == 0) 24962306a36Sopenharmony_ci return devt_from_partlabel(name + 10, devt); 25062306a36Sopenharmony_ci if (strncmp(name, "/dev/", 5) == 0) 25162306a36Sopenharmony_ci return devt_from_devname(name + 5, devt); 25262306a36Sopenharmony_ci return devt_from_devnum(name, devt); 25362306a36Sopenharmony_ci} 25462306a36Sopenharmony_ci 25562306a36Sopenharmony_cistatic char __init *bdevt_str(dev_t devt, char *buf) 25662306a36Sopenharmony_ci{ 25762306a36Sopenharmony_ci if (MAJOR(devt) <= 0xff && MINOR(devt) <= 0xff) { 25862306a36Sopenharmony_ci char tbuf[BDEVT_SIZE]; 25962306a36Sopenharmony_ci snprintf(tbuf, BDEVT_SIZE, "%02x%02x", MAJOR(devt), MINOR(devt)); 26062306a36Sopenharmony_ci snprintf(buf, BDEVT_SIZE, "%-9s", tbuf); 26162306a36Sopenharmony_ci } else 26262306a36Sopenharmony_ci snprintf(buf, BDEVT_SIZE, "%03x:%05x", MAJOR(devt), MINOR(devt)); 26362306a36Sopenharmony_ci 26462306a36Sopenharmony_ci return buf; 26562306a36Sopenharmony_ci} 26662306a36Sopenharmony_ci 26762306a36Sopenharmony_ci/* 26862306a36Sopenharmony_ci * print a full list of all partitions - intended for places where the root 26962306a36Sopenharmony_ci * filesystem can't be mounted and thus to give the victim some idea of what 27062306a36Sopenharmony_ci * went wrong 27162306a36Sopenharmony_ci */ 27262306a36Sopenharmony_civoid __init printk_all_partitions(void) 27362306a36Sopenharmony_ci{ 27462306a36Sopenharmony_ci struct class_dev_iter iter; 27562306a36Sopenharmony_ci struct device *dev; 27662306a36Sopenharmony_ci 27762306a36Sopenharmony_ci class_dev_iter_init(&iter, &block_class, NULL, &disk_type); 27862306a36Sopenharmony_ci while ((dev = class_dev_iter_next(&iter))) { 27962306a36Sopenharmony_ci struct gendisk *disk = dev_to_disk(dev); 28062306a36Sopenharmony_ci struct block_device *part; 28162306a36Sopenharmony_ci char devt_buf[BDEVT_SIZE]; 28262306a36Sopenharmony_ci unsigned long idx; 28362306a36Sopenharmony_ci 28462306a36Sopenharmony_ci /* 28562306a36Sopenharmony_ci * Don't show empty devices or things that have been 28662306a36Sopenharmony_ci * suppressed 28762306a36Sopenharmony_ci */ 28862306a36Sopenharmony_ci if (get_capacity(disk) == 0 || (disk->flags & GENHD_FL_HIDDEN)) 28962306a36Sopenharmony_ci continue; 29062306a36Sopenharmony_ci 29162306a36Sopenharmony_ci /* 29262306a36Sopenharmony_ci * Note, unlike /proc/partitions, I am showing the numbers in 29362306a36Sopenharmony_ci * hex - the same format as the root= option takes. 29462306a36Sopenharmony_ci */ 29562306a36Sopenharmony_ci rcu_read_lock(); 29662306a36Sopenharmony_ci xa_for_each(&disk->part_tbl, idx, part) { 29762306a36Sopenharmony_ci if (!bdev_nr_sectors(part)) 29862306a36Sopenharmony_ci continue; 29962306a36Sopenharmony_ci printk("%s%s %10llu %pg %s", 30062306a36Sopenharmony_ci bdev_is_partition(part) ? " " : "", 30162306a36Sopenharmony_ci bdevt_str(part->bd_dev, devt_buf), 30262306a36Sopenharmony_ci bdev_nr_sectors(part) >> 1, part, 30362306a36Sopenharmony_ci part->bd_meta_info ? 30462306a36Sopenharmony_ci part->bd_meta_info->uuid : ""); 30562306a36Sopenharmony_ci if (bdev_is_partition(part)) 30662306a36Sopenharmony_ci printk("\n"); 30762306a36Sopenharmony_ci else if (dev->parent && dev->parent->driver) 30862306a36Sopenharmony_ci printk(" driver: %s\n", 30962306a36Sopenharmony_ci dev->parent->driver->name); 31062306a36Sopenharmony_ci else 31162306a36Sopenharmony_ci printk(" (driver?)\n"); 31262306a36Sopenharmony_ci } 31362306a36Sopenharmony_ci rcu_read_unlock(); 31462306a36Sopenharmony_ci } 31562306a36Sopenharmony_ci class_dev_iter_exit(&iter); 31662306a36Sopenharmony_ci} 317