Merge tag 'media/v6.5-1' of git://git.kernel.org/pub/scm/linux/kernel/git/mchehab...
[platform/kernel/linux-starfive.git] / block / early-lookup.c
1 // SPDX-License-Identifier: GPL-2.0-only
2 /*
3  * Code for looking up block devices in the early boot code before mounting the
4  * root file system.
5  */
6 #include <linux/blkdev.h>
7 #include <linux/ctype.h>
8
9 struct uuidcmp {
10         const char *uuid;
11         int len;
12 };
13
14 /**
15  * match_dev_by_uuid - callback for finding a partition using its uuid
16  * @dev:        device passed in by the caller
17  * @data:       opaque pointer to the desired struct uuidcmp to match
18  *
19  * Returns 1 if the device matches, and 0 otherwise.
20  */
21 static int __init match_dev_by_uuid(struct device *dev, const void *data)
22 {
23         struct block_device *bdev = dev_to_bdev(dev);
24         const struct uuidcmp *cmp = data;
25
26         if (!bdev->bd_meta_info ||
27             strncasecmp(cmp->uuid, bdev->bd_meta_info->uuid, cmp->len))
28                 return 0;
29         return 1;
30 }
31
32 /**
33  * devt_from_partuuid - looks up the dev_t of a partition by its UUID
34  * @uuid_str:   char array containing ascii UUID
35  * @devt:       dev_t result
36  *
37  * The function will return the first partition which contains a matching
38  * UUID value in its partition_meta_info struct.  This does not search
39  * by filesystem UUIDs.
40  *
41  * If @uuid_str is followed by a "/PARTNROFF=%d", then the number will be
42  * extracted and used as an offset from the partition identified by the UUID.
43  *
44  * Returns 0 on success or a negative error code on failure.
45  */
46 static int __init devt_from_partuuid(const char *uuid_str, dev_t *devt)
47 {
48         struct uuidcmp cmp;
49         struct device *dev = NULL;
50         int offset = 0;
51         char *slash;
52
53         cmp.uuid = uuid_str;
54
55         slash = strchr(uuid_str, '/');
56         /* Check for optional partition number offset attributes. */
57         if (slash) {
58                 char c = 0;
59
60                 /* Explicitly fail on poor PARTUUID syntax. */
61                 if (sscanf(slash + 1, "PARTNROFF=%d%c", &offset, &c) != 1)
62                         goto out_invalid;
63                 cmp.len = slash - uuid_str;
64         } else {
65                 cmp.len = strlen(uuid_str);
66         }
67
68         if (!cmp.len)
69                 goto out_invalid;
70
71         dev = class_find_device(&block_class, NULL, &cmp, &match_dev_by_uuid);
72         if (!dev)
73                 return -ENODEV;
74
75         if (offset) {
76                 /*
77                  * Attempt to find the requested partition by adding an offset
78                  * to the partition number found by UUID.
79                  */
80                 *devt = part_devt(dev_to_disk(dev),
81                                   dev_to_bdev(dev)->bd_partno + offset);
82         } else {
83                 *devt = dev->devt;
84         }
85
86         put_device(dev);
87         return 0;
88
89 out_invalid:
90         pr_err("VFS: PARTUUID= is invalid.\n"
91                "Expected PARTUUID=<valid-uuid-id>[/PARTNROFF=%%d]\n");
92         return -EINVAL;
93 }
94
95 /**
96  * match_dev_by_label - callback for finding a partition using its label
97  * @dev:        device passed in by the caller
98  * @data:       opaque pointer to the label to match
99  *
100  * Returns 1 if the device matches, and 0 otherwise.
101  */
102 static int __init match_dev_by_label(struct device *dev, const void *data)
103 {
104         struct block_device *bdev = dev_to_bdev(dev);
105         const char *label = data;
106
107         if (!bdev->bd_meta_info || strcmp(label, bdev->bd_meta_info->volname))
108                 return 0;
109         return 1;
110 }
111
112 static int __init devt_from_partlabel(const char *label, dev_t *devt)
113 {
114         struct device *dev;
115
116         dev = class_find_device(&block_class, NULL, label, &match_dev_by_label);
117         if (!dev)
118                 return -ENODEV;
119         *devt = dev->devt;
120         put_device(dev);
121         return 0;
122 }
123
124 static dev_t __init blk_lookup_devt(const char *name, int partno)
125 {
126         dev_t devt = MKDEV(0, 0);
127         struct class_dev_iter iter;
128         struct device *dev;
129
130         class_dev_iter_init(&iter, &block_class, NULL, &disk_type);
131         while ((dev = class_dev_iter_next(&iter))) {
132                 struct gendisk *disk = dev_to_disk(dev);
133
134                 if (strcmp(dev_name(dev), name))
135                         continue;
136
137                 if (partno < disk->minors) {
138                         /* We need to return the right devno, even
139                          * if the partition doesn't exist yet.
140                          */
141                         devt = MKDEV(MAJOR(dev->devt),
142                                      MINOR(dev->devt) + partno);
143                 } else {
144                         devt = part_devt(disk, partno);
145                         if (devt)
146                                 break;
147                 }
148         }
149         class_dev_iter_exit(&iter);
150         return devt;
151 }
152
153 static int __init devt_from_devname(const char *name, dev_t *devt)
154 {
155         int part;
156         char s[32];
157         char *p;
158
159         if (strlen(name) > 31)
160                 return -EINVAL;
161         strcpy(s, name);
162         for (p = s; *p; p++) {
163                 if (*p == '/')
164                         *p = '!';
165         }
166
167         *devt = blk_lookup_devt(s, 0);
168         if (*devt)
169                 return 0;
170
171         /*
172          * Try non-existent, but valid partition, which may only exist after
173          * opening the device, like partitioned md devices.
174          */
175         while (p > s && isdigit(p[-1]))
176                 p--;
177         if (p == s || !*p || *p == '0')
178                 return -ENODEV;
179
180         /* try disk name without <part number> */
181         part = simple_strtoul(p, NULL, 10);
182         *p = '\0';
183         *devt = blk_lookup_devt(s, part);
184         if (*devt)
185                 return 0;
186
187         /* try disk name without p<part number> */
188         if (p < s + 2 || !isdigit(p[-2]) || p[-1] != 'p')
189                 return -ENODEV;
190         p[-1] = '\0';
191         *devt = blk_lookup_devt(s, part);
192         if (*devt)
193                 return 0;
194         return -ENODEV;
195 }
196
197 static int __init devt_from_devnum(const char *name, dev_t *devt)
198 {
199         unsigned maj, min, offset;
200         char *p, dummy;
201
202         if (sscanf(name, "%u:%u%c", &maj, &min, &dummy) == 2 ||
203             sscanf(name, "%u:%u:%u:%c", &maj, &min, &offset, &dummy) == 3) {
204                 *devt = MKDEV(maj, min);
205                 if (maj != MAJOR(*devt) || min != MINOR(*devt))
206                         return -EINVAL;
207         } else {
208                 *devt = new_decode_dev(simple_strtoul(name, &p, 16));
209                 if (*p)
210                         return -EINVAL;
211         }
212
213         return 0;
214 }
215
216 /*
217  *      Convert a name into device number.  We accept the following variants:
218  *
219  *      1) <hex_major><hex_minor> device number in hexadecimal represents itself
220  *         no leading 0x, for example b302.
221  *      3) /dev/<disk_name> represents the device number of disk
222  *      4) /dev/<disk_name><decimal> represents the device number
223  *         of partition - device number of disk plus the partition number
224  *      5) /dev/<disk_name>p<decimal> - same as the above, that form is
225  *         used when disk name of partitioned disk ends on a digit.
226  *      6) PARTUUID=00112233-4455-6677-8899-AABBCCDDEEFF representing the
227  *         unique id of a partition if the partition table provides it.
228  *         The UUID may be either an EFI/GPT UUID, or refer to an MSDOS
229  *         partition using the format SSSSSSSS-PP, where SSSSSSSS is a zero-
230  *         filled hex representation of the 32-bit "NT disk signature", and PP
231  *         is a zero-filled hex representation of the 1-based partition number.
232  *      7) PARTUUID=<UUID>/PARTNROFF=<int> to select a partition in relation to
233  *         a partition with a known unique id.
234  *      8) <major>:<minor> major and minor number of the device separated by
235  *         a colon.
236  *      9) PARTLABEL=<name> with name being the GPT partition label.
237  *         MSDOS partitions do not support labels!
238  *
239  *      If name doesn't have fall into the categories above, we return (0,0).
240  *      block_class is used to check if something is a disk name. If the disk
241  *      name contains slashes, the device name has them replaced with
242  *      bangs.
243  */
244 int __init early_lookup_bdev(const char *name, dev_t *devt)
245 {
246         if (strncmp(name, "PARTUUID=", 9) == 0)
247                 return devt_from_partuuid(name + 9, devt);
248         if (strncmp(name, "PARTLABEL=", 10) == 0)
249                 return devt_from_partlabel(name + 10, devt);
250         if (strncmp(name, "/dev/", 5) == 0)
251                 return devt_from_devname(name + 5, devt);
252         return devt_from_devnum(name, devt);
253 }
254
255 static char __init *bdevt_str(dev_t devt, char *buf)
256 {
257         if (MAJOR(devt) <= 0xff && MINOR(devt) <= 0xff) {
258                 char tbuf[BDEVT_SIZE];
259                 snprintf(tbuf, BDEVT_SIZE, "%02x%02x", MAJOR(devt), MINOR(devt));
260                 snprintf(buf, BDEVT_SIZE, "%-9s", tbuf);
261         } else
262                 snprintf(buf, BDEVT_SIZE, "%03x:%05x", MAJOR(devt), MINOR(devt));
263
264         return buf;
265 }
266
267 /*
268  * print a full list of all partitions - intended for places where the root
269  * filesystem can't be mounted and thus to give the victim some idea of what
270  * went wrong
271  */
272 void __init printk_all_partitions(void)
273 {
274         struct class_dev_iter iter;
275         struct device *dev;
276
277         class_dev_iter_init(&iter, &block_class, NULL, &disk_type);
278         while ((dev = class_dev_iter_next(&iter))) {
279                 struct gendisk *disk = dev_to_disk(dev);
280                 struct block_device *part;
281                 char devt_buf[BDEVT_SIZE];
282                 unsigned long idx;
283
284                 /*
285                  * Don't show empty devices or things that have been
286                  * suppressed
287                  */
288                 if (get_capacity(disk) == 0 || (disk->flags & GENHD_FL_HIDDEN))
289                         continue;
290
291                 /*
292                  * Note, unlike /proc/partitions, I am showing the numbers in
293                  * hex - the same format as the root= option takes.
294                  */
295                 rcu_read_lock();
296                 xa_for_each(&disk->part_tbl, idx, part) {
297                         if (!bdev_nr_sectors(part))
298                                 continue;
299                         printk("%s%s %10llu %pg %s",
300                                bdev_is_partition(part) ? "  " : "",
301                                bdevt_str(part->bd_dev, devt_buf),
302                                bdev_nr_sectors(part) >> 1, part,
303                                part->bd_meta_info ?
304                                         part->bd_meta_info->uuid : "");
305                         if (bdev_is_partition(part))
306                                 printk("\n");
307                         else if (dev->parent && dev->parent->driver)
308                                 printk(" driver: %s\n",
309                                         dev->parent->driver->name);
310                         else
311                                 printk(" (driver?)\n");
312                 }
313                 rcu_read_unlock();
314         }
315         class_dev_iter_exit(&iter);
316 }