]>
Commit | Line | Data |
---|---|---|
83d290c5 | 1 | // SPDX-License-Identifier: GPL-2.0+ |
affae2bf WD |
2 | /* |
3 | * (C) Copyright 2001 | |
4 | * Wolfgang Denk, DENX Software Engineering, [email protected]. | |
affae2bf WD |
5 | */ |
6 | ||
e6f6f9e6 | 7 | #include <blk.h> |
affae2bf | 8 | #include <command.h> |
7b51b576 | 9 | #include <env.h> |
96e5b03c | 10 | #include <errno.h> |
affae2bf | 11 | #include <ide.h> |
f7ae49fc | 12 | #include <log.h> |
10a37fd7 | 13 | #include <malloc.h> |
735dd97b | 14 | #include <part.h> |
251cee0d | 15 | #include <ubifs_uboot.h> |
51b2f4f0 | 16 | #include <dm/uclass.h> |
affae2bf WD |
17 | |
18 | #undef PART_DEBUG | |
19 | ||
20 | #ifdef PART_DEBUG | |
21 | #define PRINTF(fmt,args...) printf (fmt ,##args) | |
22 | #else | |
23 | #define PRINTF(fmt,args...) | |
24 | #endif | |
25 | ||
30789095 SP |
26 | /* Check all partition types */ |
27 | #define PART_TYPE_ALL -1 | |
28 | ||
ade2316d SG |
29 | /** |
30 | * part_driver_get_type() - Get a driver given its type | |
31 | * | |
32 | * @part_type: Partition type to find the driver for | |
33 | * Return: Driver for that type, or NULL if none | |
34 | */ | |
387f8be5 JW |
35 | static struct part_driver *part_driver_get_type(int part_type) |
36 | { | |
37 | struct part_driver *drv = | |
38 | ll_entry_start(struct part_driver, part_driver); | |
39 | const int n_ents = ll_entry_count(struct part_driver, part_driver); | |
40 | struct part_driver *entry; | |
41 | ||
42 | for (entry = drv; entry != drv + n_ents; entry++) { | |
43 | if (part_type == entry->part_type) | |
44 | return entry; | |
45 | } | |
46 | ||
47 | /* Not found */ | |
48 | return NULL; | |
49 | } | |
50 | ||
ade2316d SG |
51 | /** |
52 | * part_driver_lookup_type() - Look up the partition driver for a blk device | |
53 | * | |
54 | * If @desc->part_type is PART_TYPE_UNKNOWN, this checks each parition driver | |
55 | * against the blk device to see if there is a valid partition table acceptable | |
56 | * to that driver. | |
57 | * | |
58 | * If @desc->part_type is already set, it just returns the driver for that | |
59 | * type, without testing if the driver can find a valid partition on the | |
60 | * descriptor. | |
61 | * | |
62 | * On success it updates @desc->part_type if set to PART_TYPE_UNKNOWN on entry | |
63 | * | |
64 | * @dev_desc: Device descriptor | |
65 | * Return: Driver found, or NULL if none | |
66 | */ | |
5aab05d9 | 67 | static struct part_driver *part_driver_lookup_type(struct blk_desc *desc) |
96e5b03c SG |
68 | { |
69 | struct part_driver *drv = | |
70 | ll_entry_start(struct part_driver, part_driver); | |
71 | const int n_ents = ll_entry_count(struct part_driver, part_driver); | |
72 | struct part_driver *entry; | |
73 | ||
5aab05d9 | 74 | if (desc->part_type == PART_TYPE_UNKNOWN) { |
d4729269 KY |
75 | for (entry = drv; entry != drv + n_ents; entry++) { |
76 | int ret; | |
77 | ||
5aab05d9 | 78 | ret = entry->test(desc); |
d4729269 | 79 | if (!ret) { |
5aab05d9 | 80 | desc->part_type = entry->part_type; |
d4729269 KY |
81 | return entry; |
82 | } | |
83 | } | |
84 | } else { | |
5aab05d9 | 85 | return part_driver_get_type(desc->part_type); |
96e5b03c SG |
86 | } |
87 | ||
88 | /* Not found */ | |
89 | return NULL; | |
90 | } | |
91 | ||
125194e6 SG |
92 | int part_get_type_by_name(const char *name) |
93 | { | |
94 | struct part_driver *drv = | |
95 | ll_entry_start(struct part_driver, part_driver); | |
96 | const int n_ents = ll_entry_count(struct part_driver, part_driver); | |
97 | struct part_driver *entry; | |
98 | ||
99 | for (entry = drv; entry != drv + n_ents; entry++) { | |
100 | if (!strcasecmp(name, entry->name)) | |
101 | return entry->part_type; | |
102 | } | |
103 | ||
104 | /* Not found */ | |
105 | return PART_TYPE_UNKNOWN; | |
106 | } | |
107 | ||
ade2316d SG |
108 | /** |
109 | * get_dev_hwpart() - Get the descriptor for a device with hardware partitions | |
110 | * | |
111 | * @ifname: Interface name (e.g. "ide", "scsi") | |
112 | * @dev: Device number (0 for first device on that interface, 1 for | |
113 | * second, etc. | |
114 | * @hwpart: Hardware partition, or 0 if none (used for MMC) | |
115 | * Return: pointer to the block device, or NULL if not available, or an | |
116 | * error occurred. | |
117 | */ | |
4101f687 | 118 | static struct blk_desc *get_dev_hwpart(const char *ifname, int dev, int hwpart) |
735dd97b | 119 | { |
5aab05d9 | 120 | struct blk_desc *desc; |
6dd9faf8 | 121 | int ret; |
735dd97b | 122 | |
a51eb8de SG |
123 | if (!blk_enabled()) |
124 | return NULL; | |
5aab05d9 SG |
125 | desc = blk_get_devnum_by_uclass_idname(ifname, dev); |
126 | if (!desc) { | |
6dd9faf8 SG |
127 | debug("%s: No device for iface '%s', dev %d\n", __func__, |
128 | ifname, dev); | |
7e71dc68 | 129 | return NULL; |
735dd97b | 130 | } |
5aab05d9 | 131 | ret = blk_dselect_hwpart(desc, hwpart); |
6dd9faf8 SG |
132 | if (ret) { |
133 | debug("%s: Failed to select h/w partition: err-%d\n", __func__, | |
134 | ret); | |
135 | return NULL; | |
136 | } | |
137 | ||
5aab05d9 | 138 | return desc; |
735dd97b | 139 | } |
336b6f90 | 140 | |
db1d9e78 | 141 | struct blk_desc *blk_get_dev(const char *ifname, int dev) |
336b6f90 | 142 | { |
a51eb8de SG |
143 | if (!blk_enabled()) |
144 | return NULL; | |
336b6f90 | 145 | |
a51eb8de | 146 | return get_dev_hwpart(ifname, dev, 0); |
735dd97b | 147 | } |
affae2bf WD |
148 | |
149 | /* ------------------------------------------------------------------------- */ | |
150 | /* | |
151 | * reports device info to the user | |
152 | */ | |
69a2a4d9 | 153 | |
42dfe7a1 | 154 | #ifdef CONFIG_LBA48 |
69a2a4d9 | 155 | typedef uint64_t lba512_t; |
c40b2956 | 156 | #else |
69a2a4d9 | 157 | typedef lbaint_t lba512_t; |
c40b2956 | 158 | #endif |
affae2bf | 159 | |
69a2a4d9 | 160 | /* |
a9d1c0e2 HS |
161 | * Overflowless variant of (block_count * mul_by / 2**right_shift) |
162 | * when 2**right_shift > mul_by | |
69a2a4d9 | 163 | */ |
a9d1c0e2 HS |
164 | static lba512_t lba512_muldiv(lba512_t block_count, lba512_t mul_by, |
165 | int right_shift) | |
69a2a4d9 ST |
166 | { |
167 | lba512_t bc_quot, bc_rem; | |
168 | ||
169 | /* x * m / d == x / d * m + (x % d) * m / d */ | |
a9d1c0e2 HS |
170 | bc_quot = block_count >> right_shift; |
171 | bc_rem = block_count - (bc_quot << right_shift); | |
172 | return bc_quot * mul_by + ((bc_rem * mul_by) >> right_shift); | |
69a2a4d9 ST |
173 | } |
174 | ||
5aab05d9 | 175 | void dev_print(struct blk_desc *desc) |
69a2a4d9 ST |
176 | { |
177 | lba512_t lba512; /* number of blocks if 512bytes block size */ | |
178 | ||
5aab05d9 | 179 | if (desc->type == DEV_TYPE_UNKNOWN) { |
af75a45d WD |
180 | puts ("not available\n"); |
181 | return; | |
182 | } | |
183 | ||
5aab05d9 | 184 | switch (desc->uclass_id) { |
e33a5c6b | 185 | case UCLASS_SCSI: |
5aab05d9 SG |
186 | printf("(%d:%d) Vendor: %s Prod.: %s Rev: %s\n", desc->target, |
187 | desc->lun, desc->vendor, desc->product, desc->revision); | |
574b3195 | 188 | break; |
e33a5c6b SG |
189 | case UCLASS_IDE: |
190 | case UCLASS_AHCI: | |
5aab05d9 SG |
191 | printf("Model: %s Firm: %s Ser#: %s\n", desc->vendor, |
192 | desc->revision, desc->product); | |
574b3195 | 193 | break; |
e33a5c6b SG |
194 | case UCLASS_MMC: |
195 | case UCLASS_USB: | |
196 | case UCLASS_NVME: | |
197 | case UCLASS_PVBLOCK: | |
95201811 | 198 | case UCLASS_HOST: |
bb56da11 | 199 | case UCLASS_BLKMAP: |
900c303a | 200 | case UCLASS_RKMTD: |
47bebe34 | 201 | printf ("Vendor: %s Rev: %s Prod: %s\n", |
5aab05d9 SG |
202 | desc->vendor, |
203 | desc->revision, | |
204 | desc->product); | |
47bebe34 | 205 | break; |
e33a5c6b | 206 | case UCLASS_VIRTIO: |
5aab05d9 | 207 | printf("%s VirtIO Block Device\n", desc->vendor); |
4ad54ec4 | 208 | break; |
ef4b66bc | 209 | case UCLASS_EFI_MEDIA: |
5aab05d9 | 210 | printf("EFI media Block Device %d\n", desc->devnum); |
ef4b66bc | 211 | break; |
e33a5c6b | 212 | case UCLASS_INVALID: |
6e24a1eb RB |
213 | puts("device type unknown\n"); |
214 | return; | |
574b3195 | 215 | default: |
5aab05d9 | 216 | printf("Unhandled device type: %i\n", desc->uclass_id); |
574b3195 | 217 | return; |
affae2bf WD |
218 | } |
219 | puts (" Type: "); | |
5aab05d9 | 220 | if (desc->removable) |
affae2bf | 221 | puts ("Removable "); |
5aab05d9 | 222 | switch (desc->type & 0x1F) { |
726c0f1e DZ |
223 | case DEV_TYPE_HARDDISK: |
224 | puts ("Hard Disk"); | |
225 | break; | |
226 | case DEV_TYPE_CDROM: | |
227 | puts ("CD ROM"); | |
228 | break; | |
229 | case DEV_TYPE_OPDISK: | |
230 | puts ("Optical Device"); | |
231 | break; | |
232 | case DEV_TYPE_TAPE: | |
233 | puts ("Tape"); | |
234 | break; | |
235 | default: | |
5aab05d9 | 236 | printf("# %02X #", desc->type & 0x1F); |
726c0f1e | 237 | break; |
affae2bf WD |
238 | } |
239 | puts ("\n"); | |
5aab05d9 | 240 | if (desc->lba > 0L && desc->blksz > 0L) { |
affae2bf | 241 | ulong mb, mb_quot, mb_rem, gb, gb_quot, gb_rem; |
c40b2956 | 242 | lbaint_t lba; |
6e592385 | 243 | |
5aab05d9 | 244 | lba = desc->lba; |
affae2bf | 245 | |
5aab05d9 | 246 | lba512 = lba * (desc->blksz / 512); |
affae2bf | 247 | /* round to 1 digit */ |
214b3f31 | 248 | /* 2048 = (1024 * 1024) / 512 MB */ |
17416eaf | 249 | mb = lba512_muldiv(lba512, 10, 11); |
69a2a4d9 | 250 | |
affae2bf WD |
251 | mb_quot = mb / 10; |
252 | mb_rem = mb - (10 * mb_quot); | |
253 | ||
254 | gb = mb / 1024; | |
255 | gb_quot = gb / 10; | |
256 | gb_rem = gb - (10 * gb_quot); | |
42dfe7a1 | 257 | #ifdef CONFIG_LBA48 |
5aab05d9 | 258 | if (desc->lba48) |
c40b2956 WD |
259 | printf (" Supports 48-bit addressing\n"); |
260 | #endif | |
4b142feb | 261 | #if defined(CONFIG_SYS_64BIT_LBA) |
139f7b1d | 262 | printf (" Capacity: %lu.%lu MB = %lu.%lu GB (%llu x %lu)\n", |
c40b2956 WD |
263 | mb_quot, mb_rem, |
264 | gb_quot, gb_rem, | |
265 | lba, | |
5aab05d9 | 266 | desc->blksz); |
c40b2956 | 267 | #else |
139f7b1d | 268 | printf (" Capacity: %lu.%lu MB = %lu.%lu GB (%lu x %lu)\n", |
affae2bf WD |
269 | mb_quot, mb_rem, |
270 | gb_quot, gb_rem, | |
c40b2956 | 271 | (ulong)lba, |
5aab05d9 | 272 | desc->blksz); |
c40b2956 | 273 | #endif |
affae2bf WD |
274 | } else { |
275 | puts (" Capacity: not available\n"); | |
276 | } | |
277 | } | |
affae2bf | 278 | |
5aab05d9 | 279 | void part_init(struct blk_desc *desc) |
affae2bf | 280 | { |
96e5b03c SG |
281 | struct part_driver *drv = |
282 | ll_entry_start(struct part_driver, part_driver); | |
283 | const int n_ents = ll_entry_count(struct part_driver, part_driver); | |
284 | struct part_driver *entry; | |
affae2bf | 285 | |
5aab05d9 | 286 | blkcache_invalidate(desc->uclass_id, desc->devnum); |
e40cf34a | 287 | |
6b0c9f2c AR |
288 | if (desc->part_type != PART_TYPE_UNKNOWN) { |
289 | for (entry = drv; entry != drv + n_ents; entry++) { | |
290 | if (entry->part_type == desc->part_type && !entry->test(desc)) | |
291 | return; | |
292 | } | |
293 | } | |
294 | ||
5aab05d9 | 295 | desc->part_type = PART_TYPE_UNKNOWN; |
96e5b03c SG |
296 | for (entry = drv; entry != drv + n_ents; entry++) { |
297 | int ret; | |
298 | ||
5aab05d9 | 299 | ret = entry->test(desc); |
96e5b03c SG |
300 | debug("%s: try '%s': ret=%d\n", __func__, entry->name, ret); |
301 | if (!ret) { | |
5aab05d9 | 302 | desc->part_type = entry->part_type; |
96e5b03c SG |
303 | break; |
304 | } | |
305 | } | |
affae2bf WD |
306 | } |
307 | ||
5aab05d9 | 308 | static void print_part_header(const char *type, struct blk_desc *desc) |
96e5b03c | 309 | { |
f18fa31c | 310 | #if CONFIG_IS_ENABLED(MAC_PARTITION) || \ |
b0cf7339 | 311 | CONFIG_IS_ENABLED(DOS_PARTITION) || \ |
1acc0087 | 312 | CONFIG_IS_ENABLED(ISO_PARTITION) || \ |
863c5b6c | 313 | CONFIG_IS_ENABLED(AMIGA_PARTITION) || \ |
c29a6dae AR |
314 | CONFIG_IS_ENABLED(EFI_PARTITION) || \ |
315 | CONFIG_IS_ENABLED(MTD_PARTITIONS) | |
51b2f4f0 HS |
316 | printf("\nPartition Map for %s device %d -- Partition Type: %s\n\n", |
317 | uclass_get_name(desc->uclass_id), desc->devnum, type); | |
0c9c8fb5 | 318 | #endif /* any CONFIG_..._PARTITION */ |
96e5b03c | 319 | } |
0c9c8fb5 | 320 | |
5aab05d9 | 321 | void part_print(struct blk_desc *desc) |
affae2bf | 322 | { |
96e5b03c | 323 | struct part_driver *drv; |
affae2bf | 324 | |
5aab05d9 | 325 | drv = part_driver_lookup_type(desc); |
96e5b03c SG |
326 | if (!drv) { |
327 | printf("## Unknown partition table type %x\n", | |
5aab05d9 | 328 | desc->part_type); |
affae2bf | 329 | return; |
affae2bf | 330 | } |
96e5b03c SG |
331 | |
332 | PRINTF("## Testing for valid %s partition ##\n", drv->name); | |
5aab05d9 | 333 | print_part_header(drv->name, desc); |
96e5b03c | 334 | if (drv->print) |
5aab05d9 | 335 | drv->print(desc); |
affae2bf WD |
336 | } |
337 | ||
5aab05d9 | 338 | int part_get_info_by_type(struct blk_desc *desc, int part, int part_type, |
387f8be5 | 339 | struct disk_partition *info) |
2f501646 | 340 | { |
96e5b03c | 341 | struct part_driver *drv; |
2f501646 | 342 | |
a51eb8de | 343 | if (blk_enabled()) { |
a51eb8de | 344 | /* The common case is no UUID support */ |
c5f1d005 | 345 | disk_partition_clr_uuid(info); |
bcd64542 | 346 | disk_partition_clr_type_guid(info); |
894bfbbf | 347 | |
387f8be5 | 348 | if (part_type == PART_TYPE_UNKNOWN) { |
5aab05d9 | 349 | drv = part_driver_lookup_type(desc); |
387f8be5 JW |
350 | } else { |
351 | drv = part_driver_get_type(part_type); | |
352 | } | |
353 | ||
a51eb8de SG |
354 | if (!drv) { |
355 | debug("## Unknown partition table type %x\n", | |
5aab05d9 | 356 | desc->part_type); |
a51eb8de SG |
357 | return -EPROTONOSUPPORT; |
358 | } | |
359 | if (!drv->get_info) { | |
360 | PRINTF("## Driver %s does not have the get_info() method\n", | |
361 | drv->name); | |
362 | return -ENOSYS; | |
363 | } | |
5aab05d9 | 364 | if (drv->get_info(desc, part, info) == 0) { |
a51eb8de SG |
365 | PRINTF("## Valid %s partition found ##\n", drv->name); |
366 | return 0; | |
367 | } | |
2f501646 | 368 | } |
2f501646 | 369 | |
59715754 | 370 | return -ENOENT; |
2f501646 | 371 | } |
99d2c205 | 372 | |
5aab05d9 | 373 | int part_get_info(struct blk_desc *desc, int part, |
387f8be5 JW |
374 | struct disk_partition *info) |
375 | { | |
5aab05d9 | 376 | return part_get_info_by_type(desc, part, PART_TYPE_UNKNOWN, info); |
387f8be5 JW |
377 | } |
378 | ||
5aab05d9 | 379 | int part_get_info_whole_disk(struct blk_desc *desc, |
0528979f | 380 | struct disk_partition *info) |
4bbcc965 RC |
381 | { |
382 | info->start = 0; | |
5aab05d9 SG |
383 | info->size = desc->lba; |
384 | info->blksz = desc->blksz; | |
4bbcc965 RC |
385 | info->bootable = 0; |
386 | strcpy((char *)info->type, BOOT_PART_TYPE); | |
387 | strcpy((char *)info->name, "Whole Disk"); | |
c5f1d005 | 388 | disk_partition_clr_uuid(info); |
bcd64542 | 389 | disk_partition_clr_type_guid(info); |
4bbcc965 RC |
390 | |
391 | return 0; | |
392 | } | |
393 | ||
ebac37cf | 394 | int blk_get_device_by_str(const char *ifname, const char *dev_hwpart_str, |
5aab05d9 | 395 | struct blk_desc **desc) |
2023e608 SW |
396 | { |
397 | char *ep; | |
336b6f90 SW |
398 | char *dup_str = NULL; |
399 | const char *dev_str, *hwpart_str; | |
400 | int dev, hwpart; | |
401 | ||
402 | hwpart_str = strchr(dev_hwpart_str, '.'); | |
403 | if (hwpart_str) { | |
404 | dup_str = strdup(dev_hwpart_str); | |
405 | dup_str[hwpart_str - dev_hwpart_str] = 0; | |
406 | dev_str = dup_str; | |
407 | hwpart_str++; | |
408 | } else { | |
409 | dev_str = dev_hwpart_str; | |
ecdd57e2 | 410 | hwpart = 0; |
336b6f90 | 411 | } |
2023e608 | 412 | |
7e5f460e | 413 | dev = hextoul(dev_str, &ep); |
2023e608 SW |
414 | if (*ep) { |
415 | printf("** Bad device specification %s %s **\n", | |
416 | ifname, dev_str); | |
1598dfcb | 417 | dev = -EINVAL; |
336b6f90 SW |
418 | goto cleanup; |
419 | } | |
420 | ||
421 | if (hwpart_str) { | |
7e5f460e | 422 | hwpart = hextoul(hwpart_str, &ep); |
336b6f90 SW |
423 | if (*ep) { |
424 | printf("** Bad HW partition specification %s %s **\n", | |
425 | ifname, hwpart_str); | |
1598dfcb | 426 | dev = -EINVAL; |
336b6f90 SW |
427 | goto cleanup; |
428 | } | |
2023e608 SW |
429 | } |
430 | ||
5aab05d9 SG |
431 | *desc = get_dev_hwpart(ifname, dev, hwpart); |
432 | if (!(*desc) || ((*desc)->type == DEV_TYPE_UNKNOWN)) { | |
8f690848 | 433 | debug("** Bad device %s %s **\n", ifname, dev_hwpart_str); |
59715754 | 434 | dev = -ENODEV; |
336b6f90 | 435 | goto cleanup; |
2023e608 SW |
436 | } |
437 | ||
a51eb8de SG |
438 | if (blk_enabled()) { |
439 | /* | |
440 | * Updates the partition table for the specified hw partition. | |
441 | * Always should be done, otherwise hw partition 0 will return | |
442 | * stale data after displaying a non-zero hw partition. | |
443 | */ | |
5aab05d9 SG |
444 | if ((*desc)->uclass_id == UCLASS_MMC) |
445 | part_init(*desc); | |
a51eb8de | 446 | } |
99e7fc8a | 447 | |
336b6f90 SW |
448 | cleanup: |
449 | free(dup_str); | |
2023e608 SW |
450 | return dev; |
451 | } | |
452 | ||
10a37fd7 SW |
453 | #define PART_UNSPECIFIED -2 |
454 | #define PART_AUTO -1 | |
e35929e4 | 455 | int blk_get_device_part_str(const char *ifname, const char *dev_part_str, |
5aab05d9 | 456 | struct blk_desc **desc, |
0528979f | 457 | struct disk_partition *info, int allow_whole_dev) |
99d2c205 | 458 | { |
59715754 | 459 | int ret; |
10a37fd7 SW |
460 | const char *part_str; |
461 | char *dup_str = NULL; | |
462 | const char *dev_str; | |
99d2c205 | 463 | int dev; |
10a37fd7 SW |
464 | char *ep; |
465 | int p; | |
466 | int part; | |
0528979f | 467 | struct disk_partition tmpinfo; |
10a37fd7 | 468 | |
5aab05d9 | 469 | *desc = NULL; |
be2e42fa HS |
470 | memset(info, 0, sizeof(*info)); |
471 | ||
f676b451 | 472 | #if IS_ENABLED(CONFIG_SANDBOX) || IS_ENABLED(CONFIG_SEMIHOSTING) |
4d907025 | 473 | /* |
3f9eb6e1 | 474 | * Special-case a pseudo block device "hostfs", to allow access to the |
4d907025 SW |
475 | * host's own filesystem. |
476 | */ | |
be2e42fa | 477 | if (!strcmp(ifname, "hostfs")) { |
4d907025 | 478 | strcpy((char *)info->type, BOOT_PART_TYPE); |
f676b451 | 479 | strcpy((char *)info->name, "Host filesystem"); |
4d907025 SW |
480 | |
481 | return 0; | |
482 | } | |
afc1744e | 483 | #endif |
4d907025 | 484 | |
2f03a639 | 485 | #if IS_ENABLED(CONFIG_CMD_UBIFS) && !IS_ENABLED(CONFIG_SPL_BUILD) |
251cee0d | 486 | /* |
3174cafd | 487 | * Special-case ubi, ubi goes through a mtd, rather than through |
251cee0d HG |
488 | * a regular block device. |
489 | */ | |
be2e42fa | 490 | if (!strcmp(ifname, "ubi")) { |
251cee0d HG |
491 | if (!ubifs_is_mounted()) { |
492 | printf("UBIFS not mounted, use ubifsmount to mount volume first!\n"); | |
59715754 | 493 | return -EINVAL; |
251cee0d HG |
494 | } |
495 | ||
251cee0d HG |
496 | strcpy((char *)info->type, BOOT_PART_TYPE); |
497 | strcpy((char *)info->name, "UBI"); | |
251cee0d HG |
498 | return 0; |
499 | } | |
500 | #endif | |
501 | ||
10a37fd7 | 502 | /* If no dev_part_str, use bootdevice environment variable */ |
350635fe HS |
503 | if (CONFIG_IS_ENABLED(ENV_SUPPORT)) { |
504 | if (!dev_part_str || !strlen(dev_part_str) || | |
505 | !strcmp(dev_part_str, "-")) | |
506 | dev_part_str = env_get("bootdevice"); | |
507 | } | |
10a37fd7 SW |
508 | |
509 | /* If still no dev_part_str, it's an error */ | |
510 | if (!dev_part_str) { | |
511 | printf("** No device specified **\n"); | |
59715754 | 512 | ret = -ENODEV; |
10a37fd7 SW |
513 | goto cleanup; |
514 | } | |
515 | ||
516 | /* Separate device and partition ID specification */ | |
517 | part_str = strchr(dev_part_str, ':'); | |
518 | if (part_str) { | |
519 | dup_str = strdup(dev_part_str); | |
520 | dup_str[part_str - dev_part_str] = 0; | |
521 | dev_str = dup_str; | |
522 | part_str++; | |
523 | } else { | |
524 | dev_str = dev_part_str; | |
99d2c205 RH |
525 | } |
526 | ||
10a37fd7 | 527 | /* Look up the device */ |
5aab05d9 | 528 | dev = blk_get_device_by_str(ifname, dev_str, desc); |
59715754 | 529 | if (dev < 0) { |
192e7012 OB |
530 | printf("** Bad device specification %s %s **\n", |
531 | ifname, dev_str); | |
59715754 | 532 | ret = dev; |
10a37fd7 | 533 | goto cleanup; |
59715754 | 534 | } |
10a37fd7 SW |
535 | |
536 | /* Convert partition ID string to number */ | |
537 | if (!part_str || !*part_str) { | |
538 | part = PART_UNSPECIFIED; | |
539 | } else if (!strcmp(part_str, "auto")) { | |
540 | part = PART_AUTO; | |
541 | } else { | |
542 | /* Something specified -> use exactly that */ | |
7e5f460e | 543 | part = (int)hextoul(part_str, &ep); |
10a37fd7 SW |
544 | /* |
545 | * Less than whole string converted, | |
546 | * or request for whole device, but caller requires partition. | |
547 | */ | |
548 | if (*ep || (part == 0 && !allow_whole_dev)) { | |
549 | printf("** Bad partition specification %s %s **\n", | |
550 | ifname, dev_part_str); | |
59715754 | 551 | ret = -ENOENT; |
10a37fd7 SW |
552 | goto cleanup; |
553 | } | |
554 | } | |
555 | ||
556 | /* | |
557 | * No partition table on device, | |
558 | * or user requested partition 0 (entire device). | |
559 | */ | |
5aab05d9 SG |
560 | if (((*desc)->part_type == PART_TYPE_UNKNOWN) || !part) { |
561 | if (!(*desc)->lba) { | |
10a37fd7 SW |
562 | printf("** Bad device size - %s %s **\n", ifname, |
563 | dev_str); | |
59715754 | 564 | ret = -EINVAL; |
10a37fd7 SW |
565 | goto cleanup; |
566 | } | |
99d2c205 | 567 | |
10a37fd7 SW |
568 | /* |
569 | * If user specified a partition ID other than 0, | |
570 | * or the calling command only accepts partitions, | |
571 | * it's an error. | |
572 | */ | |
573 | if ((part > 0) || (!allow_whole_dev)) { | |
574 | printf("** No partition table - %s %s **\n", ifname, | |
575 | dev_str); | |
59715754 | 576 | ret = -EPROTONOSUPPORT; |
10a37fd7 | 577 | goto cleanup; |
99d2c205 | 578 | } |
10a37fd7 | 579 | |
5aab05d9 | 580 | (*desc)->log2blksz = LOG2((*desc)->blksz); |
50ffc3b6 | 581 | |
5aab05d9 | 582 | part_get_info_whole_disk(*desc, info); |
99d2c205 | 583 | |
10a37fd7 SW |
584 | ret = 0; |
585 | goto cleanup; | |
99d2c205 RH |
586 | } |
587 | ||
10a37fd7 SW |
588 | /* |
589 | * Now there's known to be a partition table, | |
590 | * not specifying a partition means to pick partition 1. | |
591 | */ | |
592 | if (part == PART_UNSPECIFIED) | |
593 | part = 1; | |
594 | ||
595 | /* | |
596 | * If user didn't specify a partition number, or did specify something | |
597 | * other than "auto", use that partition number directly. | |
598 | */ | |
599 | if (part != PART_AUTO) { | |
5aab05d9 | 600 | ret = part_get_info(*desc, part, info); |
10a37fd7 SW |
601 | if (ret) { |
602 | printf("** Invalid partition %d **\n", part); | |
603 | goto cleanup; | |
604 | } | |
605 | } else { | |
606 | /* | |
607 | * Find the first bootable partition. | |
608 | * If none are bootable, fall back to the first valid partition. | |
609 | */ | |
610 | part = 0; | |
611 | for (p = 1; p <= MAX_SEARCH_PARTITIONS; p++) { | |
5aab05d9 | 612 | ret = part_get_info(*desc, p, info); |
10a37fd7 SW |
613 | if (ret) |
614 | continue; | |
615 | ||
616 | /* | |
617 | * First valid partition, or new better partition? | |
618 | * If so, save partition ID. | |
619 | */ | |
620 | if (!part || info->bootable) | |
621 | part = p; | |
622 | ||
623 | /* Best possible partition? Stop searching. */ | |
624 | if (info->bootable) | |
625 | break; | |
626 | ||
627 | /* | |
628 | * We now need to search further for best possible. | |
629 | * If we what we just queried was the best so far, | |
630 | * save the info since we over-write it next loop. | |
631 | */ | |
632 | if (part == p) | |
633 | tmpinfo = *info; | |
634 | } | |
635 | /* If we found any acceptable partition */ | |
636 | if (part) { | |
637 | /* | |
638 | * If we searched all possible partition IDs, | |
639 | * return the first valid partition we found. | |
640 | */ | |
641 | if (p == MAX_SEARCH_PARTITIONS + 1) | |
642 | *info = tmpinfo; | |
10a37fd7 SW |
643 | } else { |
644 | printf("** No valid partitions found **\n"); | |
645 | goto cleanup; | |
646 | } | |
99d2c205 RH |
647 | } |
648 | if (strncmp((char *)info->type, BOOT_PART_TYPE, sizeof(info->type)) != 0) { | |
649 | printf("** Invalid partition type \"%.32s\"" | |
650 | " (expect \"" BOOT_PART_TYPE "\")\n", | |
651 | info->type); | |
59715754 | 652 | ret = -EINVAL; |
10a37fd7 | 653 | goto cleanup; |
99d2c205 RH |
654 | } |
655 | ||
5aab05d9 | 656 | (*desc)->log2blksz = LOG2((*desc)->blksz); |
50ffc3b6 | 657 | |
10a37fd7 SW |
658 | ret = part; |
659 | goto cleanup; | |
99d2c205 | 660 | |
10a37fd7 SW |
661 | cleanup: |
662 | free(dup_str); | |
663 | return ret; | |
99d2c205 | 664 | } |
87b8530f | 665 | |
5aab05d9 | 666 | int part_get_info_by_name(struct blk_desc *desc, const char *name, |
8faeb1d7 | 667 | struct disk_partition *info) |
87b8530f | 668 | { |
87b8530f | 669 | struct part_driver *part_drv; |
56670d6f KY |
670 | int ret; |
671 | int i; | |
672 | ||
5aab05d9 | 673 | part_drv = part_driver_lookup_type(desc); |
56670d6f KY |
674 | if (!part_drv) |
675 | return -1; | |
50f7b2ef | 676 | |
677 | if (!part_drv->get_info) { | |
678 | log_debug("## Driver %s does not have the get_info() method\n", | |
679 | part_drv->name); | |
680 | return -ENOSYS; | |
681 | } | |
682 | ||
56670d6f | 683 | for (i = 1; i < part_drv->max_entries; i++) { |
5aab05d9 | 684 | ret = part_drv->get_info(desc, i, info); |
56670d6f | 685 | if (ret != 0) { |
ac0cd526 SP |
686 | /* |
687 | * Partition with this index can't be obtained, but | |
688 | * further partitions might be, so keep checking. | |
689 | */ | |
690 | continue; | |
56670d6f KY |
691 | } |
692 | if (strcmp(name, (const char *)info->name) == 0) { | |
693 | /* matched */ | |
694 | return i; | |
87b8530f PK |
695 | } |
696 | } | |
56670d6f | 697 | |
59715754 | 698 | return -ENOENT; |
87b8530f | 699 | } |
da2ee24d | 700 | |
6eccd1f7 RT |
701 | /** |
702 | * Get partition info from device number and partition name. | |
703 | * | |
704 | * Parse a device number and partition name string in the form of | |
81c1aa89 SA |
705 | * "devicenum.hwpartnum#partition_name", for example "0.1#misc". devicenum and |
706 | * hwpartnum are both optional, defaulting to 0. If the partition is found, | |
5aab05d9 | 707 | * sets desc and part_info accordingly with the information of the |
81c1aa89 | 708 | * partition with the given partition_name. |
6eccd1f7 RT |
709 | * |
710 | * @param[in] dev_iface Device interface | |
81c1aa89 | 711 | * @param[in] dev_part_str Input string argument, like "0.1#misc" |
5aab05d9 | 712 | * @param[out] desc Place to store the device description pointer |
6eccd1f7 | 713 | * @param[out] part_info Place to store the partition information |
185f812c | 714 | * Return: 0 on success, or a negative on error |
6eccd1f7 RT |
715 | */ |
716 | static int part_get_info_by_dev_and_name(const char *dev_iface, | |
717 | const char *dev_part_str, | |
5aab05d9 | 718 | struct blk_desc **desc, |
0528979f | 719 | struct disk_partition *part_info) |
6eccd1f7 | 720 | { |
81c1aa89 SA |
721 | char *dup_str = NULL; |
722 | const char *dev_str, *part_str; | |
723 | int ret; | |
6eccd1f7 | 724 | |
81c1aa89 | 725 | /* Separate device and partition name specification */ |
26de4296 SA |
726 | if (dev_part_str) |
727 | part_str = strchr(dev_part_str, '#'); | |
728 | else | |
729 | part_str = NULL; | |
730 | ||
81c1aa89 SA |
731 | if (part_str) { |
732 | dup_str = strdup(dev_part_str); | |
733 | dup_str[part_str - dev_part_str] = 0; | |
734 | dev_str = dup_str; | |
735 | part_str++; | |
736 | } else { | |
6eccd1f7 RT |
737 | return -EINVAL; |
738 | } | |
6eccd1f7 | 739 | |
5aab05d9 | 740 | ret = blk_get_device_by_str(dev_iface, dev_str, desc); |
fe5a5091 | 741 | if (ret < 0) |
81c1aa89 SA |
742 | goto cleanup; |
743 | ||
5aab05d9 | 744 | ret = part_get_info_by_name(*desc, part_str, part_info); |
59715754 | 745 | if (ret < 0) |
6eccd1f7 | 746 | printf("Could not find \"%s\" partition\n", part_str); |
81c1aa89 SA |
747 | |
748 | cleanup: | |
749 | free(dup_str); | |
59715754 | 750 | return ret; |
6eccd1f7 RT |
751 | } |
752 | ||
753 | int part_get_info_by_dev_and_name_or_num(const char *dev_iface, | |
754 | const char *dev_part_str, | |
5aab05d9 | 755 | struct blk_desc **desc, |
9f7bb282 SA |
756 | struct disk_partition *part_info, |
757 | int allow_whole_dev) | |
6eccd1f7 | 758 | { |
59715754 SA |
759 | int ret; |
760 | ||
6eccd1f7 | 761 | /* Split the part_name if passed as "$dev_num#part_name". */ |
5aab05d9 SG |
762 | ret = part_get_info_by_dev_and_name(dev_iface, dev_part_str, desc, |
763 | part_info); | |
59715754 SA |
764 | if (ret >= 0) |
765 | return ret; | |
6eccd1f7 RT |
766 | /* |
767 | * Couldn't lookup by name, try looking up the partition description | |
768 | * directly. | |
769 | */ | |
5aab05d9 SG |
770 | ret = blk_get_device_part_str(dev_iface, dev_part_str, desc, part_info, |
771 | allow_whole_dev); | |
59715754 | 772 | if (ret < 0) |
6eccd1f7 RT |
773 | printf("Couldn't find partition %s %s\n", |
774 | dev_iface, dev_part_str); | |
59715754 | 775 | return ret; |
6eccd1f7 RT |
776 | } |
777 | ||
5aab05d9 SG |
778 | void part_set_generic_name(const struct blk_desc *desc, int part_num, |
779 | char *name) | |
da2ee24d PK |
780 | { |
781 | char *devtype; | |
782 | ||
5aab05d9 | 783 | switch (desc->uclass_id) { |
e33a5c6b SG |
784 | case UCLASS_IDE: |
785 | case UCLASS_AHCI: | |
da2ee24d PK |
786 | devtype = "hd"; |
787 | break; | |
e33a5c6b | 788 | case UCLASS_SCSI: |
da2ee24d PK |
789 | devtype = "sd"; |
790 | break; | |
e33a5c6b | 791 | case UCLASS_USB: |
da2ee24d PK |
792 | devtype = "usbd"; |
793 | break; | |
e33a5c6b | 794 | case UCLASS_MMC: |
da2ee24d PK |
795 | devtype = "mmcsd"; |
796 | break; | |
797 | default: | |
798 | devtype = "xx"; | |
799 | break; | |
800 | } | |
801 | ||
5aab05d9 | 802 | sprintf(name, "%s%c%d", devtype, 'a' + desc->devnum, part_num); |
da2ee24d | 803 | } |
dcffa442 SG |
804 | |
805 | int part_get_bootable(struct blk_desc *desc) | |
806 | { | |
807 | struct disk_partition info; | |
808 | int p; | |
809 | ||
810 | for (p = 1; p <= MAX_SEARCH_PARTITIONS; p++) { | |
811 | int ret; | |
812 | ||
813 | ret = part_get_info(desc, p, &info); | |
814 | if (!ret && info.bootable) | |
815 | return p; | |
816 | } | |
817 | ||
818 | return 0; | |
819 | } |