xref: /freebsd/stand/efi/libefi/efipart.c (revision 52f72944b8f5abb2386eae924357dee8aea17d5b)
1 /*-
2  * Copyright (c) 2010 Marcel Moolenaar
3  * All rights reserved.
4  *
5  * Redistribution and use in source and binary forms, with or without
6  * modification, are permitted provided that the following conditions
7  * are met:
8  * 1. Redistributions of source code must retain the above copyright
9  *    notice, this list of conditions and the following disclaimer.
10  * 2. Redistributions in binary form must reproduce the above copyright
11  *    notice, this list of conditions and the following disclaimer in the
12  *    documentation and/or other materials provided with the distribution.
13  *
14  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
15  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
16  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
17  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
18  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
19  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
20  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
21  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
22  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
23  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
24  * SUCH DAMAGE.
25  */
26 
27 #include <sys/cdefs.h>
28 __FBSDID("$FreeBSD$");
29 
30 #include <sys/disk.h>
31 #include <sys/param.h>
32 #include <sys/time.h>
33 #include <sys/queue.h>
34 #include <stddef.h>
35 #include <stdarg.h>
36 
37 #include <bootstrap.h>
38 
39 #include <efi.h>
40 #include <efilib.h>
41 #include <efiprot.h>
42 #include <efichar.h>
43 #include <disk.h>
44 
45 static EFI_GUID blkio_guid = BLOCK_IO_PROTOCOL;
46 
47 static int efipart_initfd(void);
48 static int efipart_initcd(void);
49 static int efipart_inithd(void);
50 
51 static int efipart_strategy(void *, int, daddr_t, size_t, char *, size_t *);
52 static int efipart_realstrategy(void *, int, daddr_t, size_t, char *, size_t *);
53 
54 static int efipart_open(struct open_file *, ...);
55 static int efipart_close(struct open_file *);
56 static int efipart_ioctl(struct open_file *, u_long, void *);
57 
58 static int efipart_printfd(int);
59 static int efipart_printcd(int);
60 static int efipart_printhd(int);
61 
62 /* EISA PNP ID's for floppy controllers */
63 #define	PNP0604	0x604
64 #define	PNP0700	0x700
65 #define	PNP0701	0x701
66 
67 struct devsw efipart_fddev = {
68 	.dv_name = "fd",
69 	.dv_type = DEVT_FD,
70 	.dv_init = efipart_initfd,
71 	.dv_strategy = efipart_strategy,
72 	.dv_open = efipart_open,
73 	.dv_close = efipart_close,
74 	.dv_ioctl = efipart_ioctl,
75 	.dv_print = efipart_printfd,
76 	.dv_cleanup = NULL
77 };
78 
79 struct devsw efipart_cddev = {
80 	.dv_name = "cd",
81 	.dv_type = DEVT_CD,
82 	.dv_init = efipart_initcd,
83 	.dv_strategy = efipart_strategy,
84 	.dv_open = efipart_open,
85 	.dv_close = efipart_close,
86 	.dv_ioctl = efipart_ioctl,
87 	.dv_print = efipart_printcd,
88 	.dv_cleanup = NULL
89 };
90 
91 struct devsw efipart_hddev = {
92 	.dv_name = "disk",
93 	.dv_type = DEVT_DISK,
94 	.dv_init = efipart_inithd,
95 	.dv_strategy = efipart_strategy,
96 	.dv_open = efipart_open,
97 	.dv_close = efipart_close,
98 	.dv_ioctl = efipart_ioctl,
99 	.dv_print = efipart_printhd,
100 	.dv_cleanup = NULL
101 };
102 
103 static pdinfo_list_t fdinfo;
104 static pdinfo_list_t cdinfo;
105 static pdinfo_list_t hdinfo;
106 
107 static EFI_HANDLE *efipart_handles = NULL;
108 static UINTN efipart_nhandles = 0;
109 
110 pdinfo_list_t *
111 efiblk_get_pdinfo_list(struct devsw *dev)
112 {
113 	if (dev->dv_type == DEVT_DISK)
114 		return (&hdinfo);
115 	if (dev->dv_type == DEVT_CD)
116 		return (&cdinfo);
117 	if (dev->dv_type == DEVT_FD)
118 		return (&fdinfo);
119 	return (NULL);
120 }
121 
122 pdinfo_t *
123 efiblk_get_pdinfo(struct devdesc *dev)
124 {
125 	pdinfo_list_t *pdi;
126 	pdinfo_t *pd = NULL;
127 
128 	pdi = efiblk_get_pdinfo_list(dev->d_dev);
129 	if (pdi == NULL)
130 		return (pd);
131 
132 	STAILQ_FOREACH(pd, pdi, pd_link) {
133 		if (pd->pd_unit == dev->d_unit)
134 			return (pd);
135 	}
136 	return (pd);
137 }
138 
139 static int
140 efiblk_pdinfo_count(pdinfo_list_t *pdi)
141 {
142 	pdinfo_t *pd;
143 	int i = 0;
144 
145 	STAILQ_FOREACH(pd, pdi, pd_link) {
146 		i++;
147 	}
148 	return (i);
149 }
150 
151 int
152 efipart_inithandles(void)
153 {
154 	UINTN sz;
155 	EFI_HANDLE *hin;
156 	EFI_STATUS status;
157 
158 	if (efipart_nhandles != 0) {
159 		free(efipart_handles);
160 		efipart_handles = NULL;
161 		efipart_nhandles = 0;
162 	}
163 
164 	sz = 0;
165 	hin = NULL;
166 	status = BS->LocateHandle(ByProtocol, &blkio_guid, 0, &sz, hin);
167 	if (status == EFI_BUFFER_TOO_SMALL) {
168 		hin = malloc(sz);
169 		status = BS->LocateHandle(ByProtocol, &blkio_guid, 0, &sz,
170 		    hin);
171 		if (EFI_ERROR(status))
172 			free(hin);
173 	}
174 	if (EFI_ERROR(status))
175 		return (efi_status_to_errno(status));
176 
177 	efipart_handles = hin;
178 	efipart_nhandles = sz;
179 #ifdef EFIPART_DEBUG
180 	printf("%s: Got %d BLOCK IO MEDIA handle(s)\n", __func__,
181 	    efipart_nhandles);
182 #endif
183 	return (0);
184 }
185 
186 static ACPI_HID_DEVICE_PATH *
187 efipart_floppy(EFI_DEVICE_PATH *node)
188 {
189 	ACPI_HID_DEVICE_PATH *acpi;
190 
191 	if (DevicePathType(node) == ACPI_DEVICE_PATH &&
192 	    DevicePathSubType(node) == ACPI_DP) {
193 		acpi = (ACPI_HID_DEVICE_PATH *) node;
194 		if (acpi->HID == EISA_PNP_ID(PNP0604) ||
195 		    acpi->HID == EISA_PNP_ID(PNP0700) ||
196 		    acpi->HID == EISA_PNP_ID(PNP0701)) {
197 			return (acpi);
198 		}
199 	}
200 	return (NULL);
201 }
202 
203 /*
204  * Determine if the provided device path is hdd.
205  *
206  * There really is no simple fool proof way to classify the devices.
207  * Since we do build three lists of devices - floppy, cd and hdd, we
208  * will try to see  if the device is floppy or cd, and list anything else
209  * as hdd.
210  */
211 static bool
212 efipart_hdd(EFI_DEVICE_PATH *dp)
213 {
214 	unsigned i, nin;
215 	EFI_DEVICE_PATH *devpath, *node;
216 	EFI_BLOCK_IO *blkio;
217 	EFI_STATUS status;
218 
219 	if (dp == NULL)
220 		return (false);
221 
222 	if ((node = efi_devpath_last_node(dp)) == NULL)
223 		return (false);
224 
225 	if (efipart_floppy(node) != NULL)
226 		return (false);
227 
228 	/*
229 	 * Test every EFI BLOCK IO handle to make sure dp is not device path
230 	 * for CD/DVD.
231 	 */
232 	nin = efipart_nhandles / sizeof (*efipart_handles);
233 	for (i = 0; i < nin; i++) {
234 		devpath = efi_lookup_devpath(efipart_handles[i]);
235 		if (devpath == NULL)
236 			return (false);
237 
238 		/* Only continue testing when dp is prefix in devpath. */
239 		if (!efi_devpath_is_prefix(dp, devpath))
240 			continue;
241 
242 		/*
243 		 * The device path has to have last node describing the
244 		 *  device, or we can not test the type.
245 		 */
246 		if ((node = efi_devpath_last_node(devpath)) == NULL)
247 			return (false);
248 
249 		if (DevicePathType(node) == MEDIA_DEVICE_PATH &&
250 		    DevicePathSubType(node) == MEDIA_CDROM_DP) {
251 			return (false);
252 		}
253 
254 		/* Make sure we do have the media. */
255 		status = BS->HandleProtocol(efipart_handles[i],
256 		    &blkio_guid, (void **)&blkio);
257 		if (EFI_ERROR(status))
258 			return (false);
259 
260 		/* USB or SATA cd without the media. */
261 		if (blkio->Media->RemovableMedia &&
262 		    !blkio->Media->MediaPresent) {
263 			return (false);
264 		}
265 
266 		/*
267 		 * We assume the block size 512 or greater power of 2.
268 		 * iPXE is known to insert stub BLOCK IO device with
269 		 * BlockSize 1.
270 		 */
271 		if (blkio->Media->BlockSize < 512 ||
272 		    !powerof2(blkio->Media->BlockSize)) {
273 			return (false);
274 		}
275 	}
276 	return (true);
277 }
278 
279 /*
280  * Add or update entries with new handle data.
281  */
282 static int
283 efipart_fdinfo_add(EFI_HANDLE handle, uint32_t uid, EFI_DEVICE_PATH *devpath)
284 {
285 	pdinfo_t *fd;
286 
287 	fd = calloc(1, sizeof(pdinfo_t));
288 	if (fd == NULL) {
289 		printf("Failed to register floppy %d, out of memory\n", uid);
290 		return (ENOMEM);
291 	}
292 	STAILQ_INIT(&fd->pd_part);
293 
294 	fd->pd_unit = uid;
295 	fd->pd_handle = handle;
296 	fd->pd_devpath = devpath;
297 	STAILQ_INSERT_TAIL(&fdinfo, fd, pd_link);
298 	return (0);
299 }
300 
301 static void
302 efipart_updatefd(void)
303 {
304 	EFI_DEVICE_PATH *devpath, *node;
305 	ACPI_HID_DEVICE_PATH *acpi;
306 	int i, nin;
307 
308 	nin = efipart_nhandles / sizeof (*efipart_handles);
309 	for (i = 0; i < nin; i++) {
310 		devpath = efi_lookup_devpath(efipart_handles[i]);
311 		if (devpath == NULL)
312 			continue;
313 
314 		if ((node = efi_devpath_last_node(devpath)) == NULL)
315 			continue;
316 		if ((acpi = efipart_floppy(node)) != NULL) {
317 			efipart_fdinfo_add(efipart_handles[i], acpi->UID,
318 			    devpath);
319 		}
320 	}
321 }
322 
323 static int
324 efipart_initfd(void)
325 {
326 
327 	STAILQ_INIT(&fdinfo);
328 
329 	efipart_updatefd();
330 
331 	bcache_add_dev(efiblk_pdinfo_count(&fdinfo));
332 	return (0);
333 }
334 
335 /*
336  * Add or update entries with new handle data.
337  */
338 static int
339 efipart_cdinfo_add(EFI_HANDLE handle, EFI_HANDLE alias,
340     EFI_DEVICE_PATH *devpath)
341 {
342 	int unit;
343 	pdinfo_t *cd;
344 	pdinfo_t *pd;
345 
346 	unit = 0;
347 	STAILQ_FOREACH(pd, &cdinfo, pd_link) {
348 		if (efi_devpath_match(pd->pd_devpath, devpath) == true) {
349 			pd->pd_handle = handle;
350 			pd->pd_alias = alias;
351 			return (0);
352 		}
353 		unit++;
354 	}
355 
356 	cd = calloc(1, sizeof(pdinfo_t));
357 	if (cd == NULL) {
358 		printf("Failed to add cd %d, out of memory\n", unit);
359 		return (ENOMEM);
360 	}
361 	STAILQ_INIT(&cd->pd_part);
362 
363 	cd->pd_handle = handle;
364 	cd->pd_unit = unit;
365 	cd->pd_alias = alias;
366 	cd->pd_devpath = devpath;
367 	STAILQ_INSERT_TAIL(&cdinfo, cd, pd_link);
368 	return (0);
369 }
370 
371 static void
372 efipart_updatecd(void)
373 {
374 	int i, nin;
375 	EFI_DEVICE_PATH *devpath, *devpathcpy, *tmpdevpath, *node;
376 	EFI_HANDLE handle;
377 	EFI_BLOCK_IO *blkio;
378 	EFI_STATUS status;
379 
380 	nin = efipart_nhandles / sizeof (*efipart_handles);
381 	for (i = 0; i < nin; i++) {
382 		devpath = efi_lookup_devpath(efipart_handles[i]);
383 		if (devpath == NULL)
384 			continue;
385 
386 		if ((node = efi_devpath_last_node(devpath)) == NULL)
387 			continue;
388 
389 		if (efipart_floppy(node) != NULL)
390 			continue;
391 
392 		if (efipart_hdd(devpath))
393 			continue;
394 
395 		status = BS->HandleProtocol(efipart_handles[i],
396 		    &blkio_guid, (void **)&blkio);
397 		if (EFI_ERROR(status))
398 			continue;
399 		/*
400 		 * If we come across a logical partition of subtype CDROM
401 		 * it doesn't refer to the CD filesystem itself, but rather
402 		 * to any usable El Torito boot image on it. In this case
403 		 * we try to find the parent device and add that instead as
404 		 * that will be the CD filesystem.
405 		 */
406 		if (DevicePathType(node) == MEDIA_DEVICE_PATH &&
407 		    DevicePathSubType(node) == MEDIA_CDROM_DP) {
408 			devpathcpy = efi_devpath_trim(devpath);
409 			if (devpathcpy == NULL)
410 				continue;
411 			tmpdevpath = devpathcpy;
412 			status = BS->LocateDevicePath(&blkio_guid, &tmpdevpath,
413 			    &handle);
414 			free(devpathcpy);
415 			if (EFI_ERROR(status))
416 				continue;
417 			devpath = efi_lookup_devpath(handle);
418 			efipart_cdinfo_add(handle, efipart_handles[i],
419 			    devpath);
420 			continue;
421 		}
422 
423 		if (DevicePathType(node) == MESSAGING_DEVICE_PATH &&
424 		    DevicePathSubType(node) == MSG_ATAPI_DP) {
425 			efipart_cdinfo_add(efipart_handles[i], NULL,
426 			    devpath);
427 			continue;
428 		}
429 
430 		/* USB or SATA cd without the media. */
431 		if (blkio->Media->RemovableMedia &&
432 		    !blkio->Media->MediaPresent) {
433 			efipart_cdinfo_add(efipart_handles[i], NULL,
434 			    devpath);
435 		}
436 	}
437 }
438 
439 static int
440 efipart_initcd(void)
441 {
442 
443 	STAILQ_INIT(&cdinfo);
444 
445 	efipart_updatecd();
446 
447 	bcache_add_dev(efiblk_pdinfo_count(&cdinfo));
448 	return (0);
449 }
450 
451 static int
452 efipart_hdinfo_add(EFI_HANDLE disk_handle, EFI_HANDLE part_handle)
453 {
454 	EFI_DEVICE_PATH *disk_devpath, *part_devpath;
455 	HARDDRIVE_DEVICE_PATH *node;
456 	int unit;
457 	pdinfo_t *hd, *pd, *last;
458 
459 	disk_devpath = efi_lookup_devpath(disk_handle);
460 	if (disk_devpath == NULL)
461 		return (ENOENT);
462 
463 	if (part_handle != NULL) {
464 		part_devpath = efi_lookup_devpath(part_handle);
465 		if (part_devpath == NULL)
466 			return (ENOENT);
467 		node = (HARDDRIVE_DEVICE_PATH *)
468 		    efi_devpath_last_node(part_devpath);
469 		if (node == NULL)
470 			return (ENOENT);	/* This should not happen. */
471 	} else {
472 		part_devpath = NULL;
473 		node = NULL;
474 	}
475 
476 	pd = calloc(1, sizeof(pdinfo_t));
477 	if (pd == NULL) {
478 		printf("Failed to add disk, out of memory\n");
479 		return (ENOMEM);
480 	}
481 	STAILQ_INIT(&pd->pd_part);
482 
483 	STAILQ_FOREACH(hd, &hdinfo, pd_link) {
484 		if (efi_devpath_match(hd->pd_devpath, disk_devpath) == true) {
485 			if (part_devpath == NULL)
486 				return (0);
487 
488 			/* Add the partition. */
489 			pd->pd_handle = part_handle;
490 			pd->pd_unit = node->PartitionNumber;
491 			pd->pd_devpath = part_devpath;
492 			STAILQ_INSERT_TAIL(&hd->pd_part, pd, pd_link);
493 			return (0);
494 		}
495 	}
496 
497 	last = STAILQ_LAST(&hdinfo, pdinfo, pd_link);
498 	if (last != NULL)
499 		unit = last->pd_unit + 1;
500 	else
501 		unit = 0;
502 
503 	/* Add the disk. */
504 	hd = pd;
505 	hd->pd_handle = disk_handle;
506 	hd->pd_unit = unit;
507 	hd->pd_devpath = disk_devpath;
508 	STAILQ_INSERT_TAIL(&hdinfo, hd, pd_link);
509 
510 	if (part_devpath == NULL)
511 		return (0);
512 
513 	pd = calloc(1, sizeof(pdinfo_t));
514 	if (pd == NULL) {
515 		printf("Failed to add partition, out of memory\n");
516 		return (ENOMEM);
517 	}
518 	STAILQ_INIT(&pd->pd_part);
519 
520 	/* Add the partition. */
521 	pd->pd_handle = part_handle;
522 	pd->pd_unit = node->PartitionNumber;
523 	pd->pd_devpath = part_devpath;
524 	STAILQ_INSERT_TAIL(&hd->pd_part, pd, pd_link);
525 
526 	return (0);
527 }
528 
529 /*
530  * The MEDIA_FILEPATH_DP has device name.
531  * From U-Boot sources it looks like names are in the form
532  * of typeN:M, where type is interface type, N is disk id
533  * and M is partition id.
534  */
535 static int
536 efipart_hdinfo_add_filepath(EFI_HANDLE disk_handle)
537 {
538 	EFI_DEVICE_PATH *devpath;
539 	FILEPATH_DEVICE_PATH *node;
540 	char *pathname, *p;
541 	int unit, len;
542 	pdinfo_t *pd, *last;
543 
544 	/* First collect and verify all the data */
545 	if ((devpath = efi_lookup_devpath(disk_handle)) == NULL)
546 		return (ENOENT);
547 	node = (FILEPATH_DEVICE_PATH *)efi_devpath_last_node(devpath);
548 	if (node == NULL)
549 		return (ENOENT);	/* This should not happen. */
550 
551 	pd = calloc(1, sizeof(pdinfo_t));
552 	if (pd == NULL) {
553 		printf("Failed to add disk, out of memory\n");
554 		return (ENOMEM);
555 	}
556 	STAILQ_INIT(&pd->pd_part);
557 	last = STAILQ_LAST(&hdinfo, pdinfo, pd_link);
558 	if (last != NULL)
559 		unit = last->pd_unit + 1;
560 	else
561 		unit = 0;
562 
563 	/* FILEPATH_DEVICE_PATH has 0 terminated string */
564 	len = ucs2len(node->PathName);
565 	if ((pathname = malloc(len + 1)) == NULL) {
566 		printf("Failed to add disk, out of memory\n");
567 		free(pd);
568 		return (ENOMEM);
569 	}
570 	cpy16to8(node->PathName, pathname, len + 1);
571 	p = strchr(pathname, ':');
572 
573 	/*
574 	 * Assume we are receiving handles in order, first disk handle,
575 	 * then partitions for this disk. If this assumption proves
576 	 * false, this code would need update.
577 	 */
578 	if (p == NULL) {	/* no colon, add the disk */
579 		pd->pd_handle = disk_handle;
580 		pd->pd_unit = unit;
581 		pd->pd_devpath = devpath;
582 		STAILQ_INSERT_TAIL(&hdinfo, pd, pd_link);
583 		free(pathname);
584 		return (0);
585 	}
586 	p++;	/* skip the colon */
587 	errno = 0;
588 	unit = (int)strtol(p, NULL, 0);
589 	if (errno != 0) {
590 		printf("Bad unit number for partition \"%s\"\n", pathname);
591 		free(pathname);
592 		free(pd);
593 		return (EUNIT);
594 	}
595 
596 	/*
597 	 * We should have disk registered, if not, we are receiving
598 	 * handles out of order, and this code should be reworked
599 	 * to create "blank" disk for partition, and to find the
600 	 * disk based on PathName compares.
601 	 */
602 	if (last == NULL) {
603 		printf("BUG: No disk for partition \"%s\"\n", pathname);
604 		free(pathname);
605 		free(pd);
606 		return (EINVAL);
607 	}
608 	/* Add the partition. */
609 	pd->pd_handle = disk_handle;
610 	pd->pd_unit = unit;
611 	pd->pd_devpath = devpath;
612 	STAILQ_INSERT_TAIL(&last->pd_part, pd, pd_link);
613 	free(pathname);
614 	return (0);
615 }
616 
617 static void
618 efipart_updatehd(void)
619 {
620 	int i, nin;
621 	EFI_DEVICE_PATH *devpath, *devpathcpy, *tmpdevpath, *node;
622 	EFI_HANDLE handle;
623 	EFI_BLOCK_IO *blkio;
624 	EFI_STATUS status;
625 
626 	nin = efipart_nhandles / sizeof (*efipart_handles);
627 	for (i = 0; i < nin; i++) {
628 		devpath = efi_lookup_devpath(efipart_handles[i]);
629 		if (devpath == NULL)
630 			continue;
631 
632 		if ((node = efi_devpath_last_node(devpath)) == NULL)
633 			continue;
634 
635 		if (!efipart_hdd(devpath))
636 			continue;
637 
638 		status = BS->HandleProtocol(efipart_handles[i],
639 		    &blkio_guid, (void **)&blkio);
640 		if (EFI_ERROR(status))
641 			continue;
642 
643 		if (DevicePathType(node) == MEDIA_DEVICE_PATH &&
644 		    DevicePathSubType(node) == MEDIA_FILEPATH_DP) {
645 			efipart_hdinfo_add_filepath(efipart_handles[i]);
646 			continue;
647 		}
648 
649 		if (DevicePathType(node) == MEDIA_DEVICE_PATH &&
650 		    DevicePathSubType(node) == MEDIA_HARDDRIVE_DP) {
651 			devpathcpy = efi_devpath_trim(devpath);
652 			if (devpathcpy == NULL)
653 				continue;
654 			tmpdevpath = devpathcpy;
655 			status = BS->LocateDevicePath(&blkio_guid, &tmpdevpath,
656 			    &handle);
657 			free(devpathcpy);
658 			if (EFI_ERROR(status))
659 				continue;
660 			/*
661 			 * We do not support nested partitions.
662 			 */
663 			devpathcpy = efi_lookup_devpath(handle);
664 			if (devpathcpy == NULL)
665 				continue;
666 			if ((node = efi_devpath_last_node(devpathcpy)) == NULL)
667 				continue;
668 
669 			if (DevicePathType(node) == MEDIA_DEVICE_PATH &&
670 			    DevicePathSubType(node) == MEDIA_HARDDRIVE_DP)
671 				continue;
672 
673 			efipart_hdinfo_add(handle, efipart_handles[i]);
674 			continue;
675 		}
676 
677 		efipart_hdinfo_add(efipart_handles[i], NULL);
678 	}
679 }
680 
681 static int
682 efipart_inithd(void)
683 {
684 
685 	STAILQ_INIT(&hdinfo);
686 
687 	efipart_updatehd();
688 
689 	bcache_add_dev(efiblk_pdinfo_count(&hdinfo));
690 	return (0);
691 }
692 
693 static int
694 efipart_print_common(struct devsw *dev, pdinfo_list_t *pdlist, int verbose)
695 {
696 	int ret = 0;
697 	EFI_BLOCK_IO *blkio;
698 	EFI_STATUS status;
699 	EFI_HANDLE h;
700 	pdinfo_t *pd;
701 	CHAR16 *text;
702 	struct disk_devdesc pd_dev;
703 	char line[80];
704 
705 	if (STAILQ_EMPTY(pdlist))
706 		return (0);
707 
708 	printf("%s devices:", dev->dv_name);
709 	if ((ret = pager_output("\n")) != 0)
710 		return (ret);
711 
712 	STAILQ_FOREACH(pd, pdlist, pd_link) {
713 		h = pd->pd_handle;
714 		if (verbose) {	/* Output the device path. */
715 			text = efi_devpath_name(efi_lookup_devpath(h));
716 			if (text != NULL) {
717 				printf("  %S", text);
718 				efi_free_devpath_name(text);
719 				if ((ret = pager_output("\n")) != 0)
720 					break;
721 			}
722 		}
723 		snprintf(line, sizeof(line),
724 		    "    %s%d", dev->dv_name, pd->pd_unit);
725 		printf("%s:", line);
726 		status = BS->HandleProtocol(h, &blkio_guid, (void **)&blkio);
727 		if (!EFI_ERROR(status)) {
728 			printf("    %llu",
729 			    blkio->Media->LastBlock == 0? 0:
730 			    (unsigned long long) (blkio->Media->LastBlock + 1));
731 			if (blkio->Media->LastBlock != 0) {
732 				printf(" X %u", blkio->Media->BlockSize);
733 			}
734 			printf(" blocks");
735 			if (blkio->Media->MediaPresent) {
736 				if (blkio->Media->RemovableMedia)
737 					printf(" (removable)");
738 			} else {
739 				printf(" (no media)");
740 			}
741 			if ((ret = pager_output("\n")) != 0)
742 				break;
743 			if (!blkio->Media->MediaPresent)
744 				continue;
745 
746 			pd->pd_blkio = blkio;
747 			pd_dev.dd.d_dev = dev;
748 			pd_dev.dd.d_unit = pd->pd_unit;
749 			pd_dev.d_slice = -1;
750 			pd_dev.d_partition = -1;
751 			ret = disk_open(&pd_dev, blkio->Media->BlockSize *
752 			    (blkio->Media->LastBlock + 1),
753 			    blkio->Media->BlockSize);
754 			if (ret == 0) {
755 				ret = disk_print(&pd_dev, line, verbose);
756 				disk_close(&pd_dev);
757 				if (ret != 0)
758 					return (ret);
759 			} else {
760 				/* Do not fail from disk_open() */
761 				ret = 0;
762 			}
763 		} else {
764 			if ((ret = pager_output("\n")) != 0)
765 				break;
766 		}
767 	}
768 	return (ret);
769 }
770 
771 static int
772 efipart_printfd(int verbose)
773 {
774 	return (efipart_print_common(&efipart_fddev, &fdinfo, verbose));
775 }
776 
777 static int
778 efipart_printcd(int verbose)
779 {
780 	return (efipart_print_common(&efipart_cddev, &cdinfo, verbose));
781 }
782 
783 static int
784 efipart_printhd(int verbose)
785 {
786 	return (efipart_print_common(&efipart_hddev, &hdinfo, verbose));
787 }
788 
789 static int
790 efipart_open(struct open_file *f, ...)
791 {
792 	va_list args;
793 	struct disk_devdesc *dev;
794 	pdinfo_t *pd;
795 	EFI_BLOCK_IO *blkio;
796 	EFI_STATUS status;
797 
798 	va_start(args, f);
799 	dev = va_arg(args, struct disk_devdesc*);
800 	va_end(args);
801 	if (dev == NULL)
802 		return (EINVAL);
803 
804 	pd = efiblk_get_pdinfo((struct devdesc *)dev);
805 	if (pd == NULL)
806 		return (EIO);
807 
808 	if (pd->pd_blkio == NULL) {
809 		status = BS->HandleProtocol(pd->pd_handle, &blkio_guid,
810 		    (void **)&pd->pd_blkio);
811 		if (EFI_ERROR(status))
812 			return (efi_status_to_errno(status));
813 	}
814 
815 	blkio = pd->pd_blkio;
816 	if (!blkio->Media->MediaPresent)
817 		return (EAGAIN);
818 
819 	pd->pd_open++;
820 	if (pd->pd_bcache == NULL)
821 		pd->pd_bcache = bcache_allocate();
822 
823 	if (dev->dd.d_dev->dv_type == DEVT_DISK) {
824 		int rc;
825 
826 		rc = disk_open(dev,
827 		    blkio->Media->BlockSize * (blkio->Media->LastBlock + 1),
828 		    blkio->Media->BlockSize);
829 		if (rc != 0) {
830 			pd->pd_open--;
831 			if (pd->pd_open == 0) {
832 				pd->pd_blkio = NULL;
833 				bcache_free(pd->pd_bcache);
834 				pd->pd_bcache = NULL;
835 			}
836 		}
837 		return (rc);
838 	}
839 	return (0);
840 }
841 
842 static int
843 efipart_close(struct open_file *f)
844 {
845 	struct disk_devdesc *dev;
846 	pdinfo_t *pd;
847 
848 	dev = (struct disk_devdesc *)(f->f_devdata);
849 	if (dev == NULL)
850 		return (EINVAL);
851 
852 	pd = efiblk_get_pdinfo((struct devdesc *)dev);
853 	if (pd == NULL)
854 		return (EINVAL);
855 
856 	pd->pd_open--;
857 	if (pd->pd_open == 0) {
858 		pd->pd_blkio = NULL;
859 		bcache_free(pd->pd_bcache);
860 		pd->pd_bcache = NULL;
861 	}
862 	if (dev->dd.d_dev->dv_type == DEVT_DISK)
863 		return (disk_close(dev));
864 	return (0);
865 }
866 
867 static int
868 efipart_ioctl(struct open_file *f, u_long cmd, void *data)
869 {
870 	struct disk_devdesc *dev;
871 	pdinfo_t *pd;
872 	int rc;
873 
874 	dev = (struct disk_devdesc *)(f->f_devdata);
875 	if (dev == NULL)
876 		return (EINVAL);
877 
878 	pd = efiblk_get_pdinfo((struct devdesc *)dev);
879 	if (pd == NULL)
880 		return (EINVAL);
881 
882 	if (dev->dd.d_dev->dv_type == DEVT_DISK) {
883 		rc = disk_ioctl(dev, cmd, data);
884 		if (rc != ENOTTY)
885 			return (rc);
886 	}
887 
888 	switch (cmd) {
889 	case DIOCGSECTORSIZE:
890 		*(u_int *)data = pd->pd_blkio->Media->BlockSize;
891 		break;
892 	case DIOCGMEDIASIZE:
893 		*(uint64_t *)data = pd->pd_blkio->Media->BlockSize *
894 		    (pd->pd_blkio->Media->LastBlock + 1);
895 		break;
896 	default:
897 		return (ENOTTY);
898 	}
899 
900 	return (0);
901 }
902 
903 /*
904  * efipart_readwrite()
905  * Internal equivalent of efipart_strategy(), which operates on the
906  * media-native block size. This function expects all I/O requests
907  * to be within the media size and returns an error if such is not
908  * the case.
909  */
910 static int
911 efipart_readwrite(EFI_BLOCK_IO *blkio, int rw, daddr_t blk, daddr_t nblks,
912     char *buf)
913 {
914 	EFI_STATUS status;
915 
916 	if (blkio == NULL)
917 		return (ENXIO);
918 	if (blk < 0 || blk > blkio->Media->LastBlock)
919 		return (EIO);
920 	if ((blk + nblks - 1) > blkio->Media->LastBlock)
921 		return (EIO);
922 
923 	switch (rw & F_MASK) {
924 	case F_READ:
925 		status = blkio->ReadBlocks(blkio, blkio->Media->MediaId, blk,
926 		    nblks * blkio->Media->BlockSize, buf);
927 		break;
928 	case F_WRITE:
929 		if (blkio->Media->ReadOnly)
930 			return (EROFS);
931 		status = blkio->WriteBlocks(blkio, blkio->Media->MediaId, blk,
932 		    nblks * blkio->Media->BlockSize, buf);
933 		break;
934 	default:
935 		return (ENOSYS);
936 	}
937 
938 	if (EFI_ERROR(status)) {
939 		printf("%s: rw=%d, blk=%ju size=%ju status=%lu\n", __func__, rw,
940 		    blk, nblks, EFI_ERROR_CODE(status));
941 	}
942 	return (efi_status_to_errno(status));
943 }
944 
945 static int
946 efipart_strategy(void *devdata, int rw, daddr_t blk, size_t size,
947     char *buf, size_t *rsize)
948 {
949 	struct bcache_devdata bcd;
950 	struct disk_devdesc *dev;
951 	pdinfo_t *pd;
952 
953 	dev = (struct disk_devdesc *)devdata;
954 	if (dev == NULL)
955 		return (EINVAL);
956 
957 	pd = efiblk_get_pdinfo((struct devdesc *)dev);
958 	if (pd == NULL)
959 		return (EINVAL);
960 
961 	if (pd->pd_blkio->Media->RemovableMedia &&
962 	    !pd->pd_blkio->Media->MediaPresent)
963 		return (ENXIO);
964 
965 	bcd.dv_strategy = efipart_realstrategy;
966 	bcd.dv_devdata = devdata;
967 	bcd.dv_cache = pd->pd_bcache;
968 
969 	if (dev->dd.d_dev->dv_type == DEVT_DISK) {
970 		daddr_t offset;
971 
972 		offset = dev->d_offset * pd->pd_blkio->Media->BlockSize;
973 		offset /= 512;
974 		return (bcache_strategy(&bcd, rw, blk + offset,
975 		    size, buf, rsize));
976 	}
977 	return (bcache_strategy(&bcd, rw, blk, size, buf, rsize));
978 }
979 
980 static int
981 efipart_realstrategy(void *devdata, int rw, daddr_t blk, size_t size,
982     char *buf, size_t *rsize)
983 {
984 	struct disk_devdesc *dev = (struct disk_devdesc *)devdata;
985 	pdinfo_t *pd;
986 	EFI_BLOCK_IO *blkio;
987 	uint64_t off, disk_blocks, d_offset = 0;
988 	char *blkbuf;
989 	size_t blkoff, blksz;
990 	int error;
991 	size_t diskend, readstart;
992 
993 	if (dev == NULL || blk < 0)
994 		return (EINVAL);
995 
996 	pd = efiblk_get_pdinfo((struct devdesc *)dev);
997 	if (pd == NULL)
998 		return (EINVAL);
999 
1000 	blkio = pd->pd_blkio;
1001 	if (blkio == NULL)
1002 		return (ENXIO);
1003 
1004 	if (size == 0 || (size % 512) != 0)
1005 		return (EIO);
1006 
1007 	off = blk * 512;
1008 	/*
1009 	 * Get disk blocks, this value is either for whole disk or for
1010 	 * partition.
1011 	 */
1012 	disk_blocks = 0;
1013 	if (dev->dd.d_dev->dv_type == DEVT_DISK) {
1014 		if (disk_ioctl(dev, DIOCGMEDIASIZE, &disk_blocks) == 0) {
1015 			/* DIOCGMEDIASIZE does return bytes. */
1016 			disk_blocks /= blkio->Media->BlockSize;
1017 		}
1018 		d_offset = dev->d_offset;
1019 	}
1020 	if (disk_blocks == 0)
1021 		disk_blocks = blkio->Media->LastBlock + 1 - d_offset;
1022 
1023 	/* make sure we don't read past disk end */
1024 	if ((off + size) / blkio->Media->BlockSize > d_offset + disk_blocks) {
1025 		diskend = d_offset + disk_blocks;
1026 		readstart = off / blkio->Media->BlockSize;
1027 
1028 		if (diskend <= readstart) {
1029 			if (rsize != NULL)
1030 				*rsize = 0;
1031 
1032 			return (EIO);
1033 		}
1034 		size = diskend - readstart;
1035 		size = size * blkio->Media->BlockSize;
1036 	}
1037 
1038 	if (rsize != NULL)
1039 		*rsize = size;
1040 
1041 	if ((size % blkio->Media->BlockSize == 0) &&
1042 	    (off % blkio->Media->BlockSize == 0))
1043 		return (efipart_readwrite(blkio, rw,
1044 		    off / blkio->Media->BlockSize,
1045 		    size / blkio->Media->BlockSize, buf));
1046 
1047 	/*
1048 	 * The block size of the media is not a multiple of I/O.
1049 	 */
1050 	blkbuf = malloc(blkio->Media->BlockSize);
1051 	if (blkbuf == NULL)
1052 		return (ENOMEM);
1053 
1054 	error = 0;
1055 	blk = off / blkio->Media->BlockSize;
1056 	blkoff = off % blkio->Media->BlockSize;
1057 	blksz = blkio->Media->BlockSize - blkoff;
1058 	while (size > 0) {
1059 		error = efipart_readwrite(blkio, rw, blk, 1, blkbuf);
1060 		if (error)
1061 			break;
1062 		if (size < blksz)
1063 			blksz = size;
1064 		bcopy(blkbuf + blkoff, buf, blksz);
1065 		buf += blksz;
1066 		size -= blksz;
1067 		blk++;
1068 		blkoff = 0;
1069 		blksz = blkio->Media->BlockSize;
1070 	}
1071 
1072 	free(blkbuf);
1073 	return (error);
1074 }
1075