2 * Copyright (c) 2010 Marcel Moolenaar
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
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.
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
27 #include <sys/cdefs.h>
28 __FBSDID("$FreeBSD$");
31 #include <sys/param.h>
33 #include <sys/queue.h>
37 #include <bootstrap.h>
44 static EFI_GUID blkio_guid = BLOCK_IO_PROTOCOL;
46 static int efipart_initfd(void);
47 static int efipart_initcd(void);
48 static int efipart_inithd(void);
50 static int efipart_strategy(void *, int, daddr_t, size_t, char *, size_t *);
51 static int efipart_realstrategy(void *, int, daddr_t, size_t, char *, size_t *);
53 static int efipart_open(struct open_file *, ...);
54 static int efipart_close(struct open_file *);
55 static int efipart_ioctl(struct open_file *, u_long, void *);
57 static int efipart_printfd(int);
58 static int efipart_printcd(int);
59 static int efipart_printhd(int);
61 struct devsw efipart_fddev = {
64 .dv_init = efipart_initfd,
65 .dv_strategy = efipart_strategy,
66 .dv_open = efipart_open,
67 .dv_close = efipart_close,
68 .dv_ioctl = efipart_ioctl,
69 .dv_print = efipart_printfd,
73 struct devsw efipart_cddev = {
76 .dv_init = efipart_initcd,
77 .dv_strategy = efipart_strategy,
78 .dv_open = efipart_open,
79 .dv_close = efipart_close,
80 .dv_ioctl = efipart_ioctl,
81 .dv_print = efipart_printcd,
85 struct devsw efipart_hddev = {
88 .dv_init = efipart_inithd,
89 .dv_strategy = efipart_strategy,
90 .dv_open = efipart_open,
91 .dv_close = efipart_close,
92 .dv_ioctl = efipart_ioctl,
93 .dv_print = efipart_printhd,
97 static pdinfo_list_t fdinfo;
98 static pdinfo_list_t cdinfo;
99 static pdinfo_list_t hdinfo;
101 static EFI_HANDLE *efipart_handles = NULL;
102 static UINTN efipart_nhandles = 0;
105 efiblk_get_pdinfo(pdinfo_list_t *pdi, int unit)
109 STAILQ_FOREACH(pd, pdi, pd_link) {
110 if (pd->pd_unit == unit)
117 efiblk_pdinfo_count(pdinfo_list_t *pdi)
122 STAILQ_FOREACH(pd, pdi, pd_link) {
129 efipart_inithandles(void)
135 if (efipart_nhandles != 0) {
136 free(efipart_handles);
137 efipart_handles = NULL;
138 efipart_nhandles = 0;
143 status = BS->LocateHandle(ByProtocol, &blkio_guid, 0, &sz, hin);
144 if (status == EFI_BUFFER_TOO_SMALL) {
146 status = BS->LocateHandle(ByProtocol, &blkio_guid, 0, &sz,
148 if (EFI_ERROR(status))
151 if (EFI_ERROR(status))
152 return (efi_status_to_errno(status));
154 efipart_handles = hin;
155 efipart_nhandles = sz;
159 static ACPI_HID_DEVICE_PATH *
160 efipart_floppy(EFI_DEVICE_PATH *node)
162 ACPI_HID_DEVICE_PATH *acpi = NULL;
164 if (DevicePathType(node) == ACPI_DEVICE_PATH &&
165 DevicePathSubType(node) == ACPI_DP) {
166 acpi = (ACPI_HID_DEVICE_PATH *) node;
167 if (acpi->HID == EISA_PNP_ID(0x604) ||
168 acpi->HID == EISA_PNP_ID(0x700) ||
169 acpi->HID == EISA_ID(0x41d1, 0x701)) {
177 * Add or update entries with new handle data.
180 efipart_fdinfo_add(EFI_HANDLE handle, uint32_t uid, EFI_DEVICE_PATH *devpath)
184 fd = malloc(sizeof(pdinfo_t));
186 printf("Failed to register floppy %d, out of memory\n", uid);
189 memset(fd, 0, sizeof(pdinfo_t));
190 STAILQ_INIT(&fd->pd_part);
193 fd->pd_handle = handle;
194 fd->pd_devpath = devpath;
195 STAILQ_INSERT_TAIL(&fdinfo, fd, pd_link);
200 efipart_updatefd(void)
202 EFI_DEVICE_PATH *devpath, *node;
203 ACPI_HID_DEVICE_PATH *acpi;
206 nin = efipart_nhandles / sizeof (*efipart_handles);
207 for (i = 0; i < nin; i++) {
208 devpath = efi_lookup_devpath(efipart_handles[i]);
212 if ((node = efi_devpath_last_node(devpath)) == NULL)
214 if ((acpi = efipart_floppy(node)) != NULL) {
215 efipart_fdinfo_add(efipart_handles[i], acpi->UID,
226 rv = efipart_inithandles();
229 STAILQ_INIT(&fdinfo);
233 bcache_add_dev(efiblk_pdinfo_count(&fdinfo));
238 * Add or update entries with new handle data.
241 efipart_cdinfo_add(EFI_HANDLE handle, EFI_HANDLE alias,
242 EFI_DEVICE_PATH *devpath)
249 STAILQ_FOREACH(pd, &cdinfo, pd_link) {
250 if (efi_devpath_match(pd->pd_devpath, devpath) != 0) {
251 pd->pd_handle = handle;
252 pd->pd_alias = alias;
258 cd = malloc(sizeof(pdinfo_t));
260 printf("Failed to add cd %d, out of memory\n", unit);
263 memset(cd, 0, sizeof(pdinfo_t));
264 STAILQ_INIT(&cd->pd_part);
266 cd->pd_handle = handle;
268 cd->pd_alias = alias;
269 cd->pd_devpath = devpath;
270 STAILQ_INSERT_TAIL(&cdinfo, cd, pd_link);
275 efipart_updatecd(void)
278 EFI_DEVICE_PATH *devpath, *devpathcpy, *tmpdevpath, *node;
283 nin = efipart_nhandles / sizeof (*efipart_handles);
284 for (i = 0; i < nin; i++) {
285 devpath = efi_lookup_devpath(efipart_handles[i]);
289 if ((node = efi_devpath_last_node(devpath)) == NULL)
291 if (efipart_floppy(node) != NULL)
294 status = BS->HandleProtocol(efipart_handles[i],
295 &blkio_guid, (void **)&blkio);
296 if (EFI_ERROR(status))
299 * If we come across a logical partition of subtype CDROM
300 * it doesn't refer to the CD filesystem itself, but rather
301 * to any usable El Torito boot image on it. In this case
302 * we try to find the parent device and add that instead as
303 * that will be the CD filesystem.
305 if (DevicePathType(node) == MEDIA_DEVICE_PATH &&
306 DevicePathSubType(node) == MEDIA_CDROM_DP) {
307 devpathcpy = efi_devpath_trim(devpath);
308 if (devpathcpy == NULL)
310 tmpdevpath = devpathcpy;
311 status = BS->LocateDevicePath(&blkio_guid, &tmpdevpath,
314 if (EFI_ERROR(status))
316 devpath = efi_lookup_devpath(handle);
317 efipart_cdinfo_add(handle, efipart_handles[i],
322 if (DevicePathType(node) == MESSAGING_DEVICE_PATH &&
323 DevicePathSubType(node) == MSG_ATAPI_DP) {
324 efipart_cdinfo_add(efipart_handles[i], NULL,
329 /* USB or SATA cd without the media. */
330 if (blkio->Media->RemovableMedia &&
331 !blkio->Media->MediaPresent) {
332 efipart_cdinfo_add(efipart_handles[i], NULL,
343 rv = efipart_inithandles();
346 STAILQ_INIT(&cdinfo);
350 bcache_add_dev(efiblk_pdinfo_count(&cdinfo));
355 efipart_hdinfo_add(EFI_HANDLE disk_handle, EFI_HANDLE part_handle)
357 EFI_DEVICE_PATH *disk_devpath, *part_devpath;
358 HARDDRIVE_DEVICE_PATH *node;
360 pdinfo_t *hd, *pd, *last;
362 disk_devpath = efi_lookup_devpath(disk_handle);
363 part_devpath = efi_lookup_devpath(part_handle);
364 if (disk_devpath == NULL || part_devpath == NULL) {
367 node = (HARDDRIVE_DEVICE_PATH *)efi_devpath_last_node(part_devpath);
369 return (ENOENT); /* This should not happen. */
371 pd = malloc(sizeof(pdinfo_t));
373 printf("Failed to add disk, out of memory\n");
376 memset(pd, 0, sizeof(pdinfo_t));
377 STAILQ_INIT(&pd->pd_part);
379 STAILQ_FOREACH(hd, &hdinfo, pd_link) {
380 if (efi_devpath_match(hd->pd_devpath, disk_devpath) != 0) {
381 /* Add the partition. */
382 pd->pd_handle = part_handle;
383 pd->pd_unit = node->PartitionNumber;
384 pd->pd_devpath = part_devpath;
385 STAILQ_INSERT_TAIL(&hd->pd_part, pd, pd_link);
390 last = STAILQ_LAST(&hdinfo, pdinfo, pd_link);
392 unit = last->pd_unit + 1;
398 hd->pd_handle = disk_handle;
400 hd->pd_devpath = disk_devpath;
401 STAILQ_INSERT_TAIL(&hdinfo, hd, pd_link);
403 pd = malloc(sizeof(pdinfo_t));
405 printf("Failed to add partition, out of memory\n");
408 memset(pd, 0, sizeof(pdinfo_t));
409 STAILQ_INIT(&pd->pd_part);
411 /* Add the partition. */
412 pd->pd_handle = part_handle;
413 pd->pd_unit = node->PartitionNumber;
414 pd->pd_devpath = part_devpath;
415 STAILQ_INSERT_TAIL(&hd->pd_part, pd, pd_link);
421 * The MEDIA_FILEPATH_DP has device name.
422 * From U-Boot sources it looks like names are in the form
423 * of typeN:M, where type is interface type, N is disk id
424 * and M is partition id.
427 efipart_hdinfo_add_filepath(EFI_HANDLE disk_handle)
429 EFI_DEVICE_PATH *devpath;
430 FILEPATH_DEVICE_PATH *node;
435 /* First collect and verify all the data */
436 if ((devpath = efi_lookup_devpath(disk_handle)) == NULL)
438 node = (FILEPATH_DEVICE_PATH *)efi_devpath_last_node(devpath);
440 return (ENOENT); /* This should not happen. */
442 pd = malloc(sizeof(pdinfo_t));
444 printf("Failed to add disk, out of memory\n");
447 memset(pd, 0, sizeof(pdinfo_t));
448 STAILQ_INIT(&pd->pd_part);
449 last = STAILQ_LAST(&hdinfo, pdinfo, pd_link);
451 unit = last->pd_unit + 1;
455 /* FILEPATH_DEVICE_PATH has 0 terminated string */
456 for (len = 0; node->PathName[len] != 0; len++)
458 if ((pathname = malloc(len + 1)) == NULL) {
459 printf("Failed to add disk, out of memory\n");
463 cpy16to8(node->PathName, pathname, len + 1);
464 p = strchr(pathname, ':');
467 * Assume we are receiving handles in order, first disk handle,
468 * then partitions for this disk. If this assumption proves
469 * false, this code would need update.
471 if (p == NULL) { /* no colon, add the disk */
472 pd->pd_handle = disk_handle;
474 pd->pd_devpath = devpath;
475 STAILQ_INSERT_TAIL(&hdinfo, pd, pd_link);
479 p++; /* skip the colon */
480 unit = (int)strtol(p, NULL, 0);
483 * We should have disk registered, if not, we are receiving
484 * handles out of order, and this code should be reworked
485 * to create "blank" disk for partition, and to find the
486 * disk based on PathName compares.
489 printf("BUG: No disk for partition \"%s\"\n", pathname);
494 /* Add the partition. */
495 pd->pd_handle = disk_handle;
497 pd->pd_devpath = devpath;
498 STAILQ_INSERT_TAIL(&last->pd_part, pd, pd_link);
504 efipart_updatehd(void)
507 EFI_DEVICE_PATH *devpath, *devpathcpy, *tmpdevpath, *node;
512 nin = efipart_nhandles / sizeof (*efipart_handles);
513 for (i = 0; i < nin; i++) {
514 devpath = efi_lookup_devpath(efipart_handles[i]);
518 if ((node = efi_devpath_last_node(devpath)) == NULL)
520 if (efipart_floppy(node) != NULL)
523 status = BS->HandleProtocol(efipart_handles[i],
524 &blkio_guid, (void **)&blkio);
525 if (EFI_ERROR(status))
528 if (DevicePathType(node) == MEDIA_DEVICE_PATH &&
529 DevicePathSubType(node) == MEDIA_HARDDRIVE_DP) {
530 devpathcpy = efi_devpath_trim(devpath);
531 if (devpathcpy == NULL)
533 tmpdevpath = devpathcpy;
534 status = BS->LocateDevicePath(&blkio_guid, &tmpdevpath,
537 if (EFI_ERROR(status))
540 * We do not support nested partitions.
542 devpathcpy = efi_lookup_devpath(handle);
543 if (devpathcpy == NULL)
545 if ((node = efi_devpath_last_node(devpathcpy)) == NULL)
547 if (DevicePathType(node) == MEDIA_DEVICE_PATH &&
548 DevicePathSubType(node) == MEDIA_HARDDRIVE_DP)
550 efipart_hdinfo_add(handle, efipart_handles[i]);
554 if (DevicePathType(node) == MEDIA_DEVICE_PATH &&
555 DevicePathSubType(node) == MEDIA_FILEPATH_DP) {
556 efipart_hdinfo_add_filepath(efipart_handles[i]);
567 rv = efipart_inithandles();
570 STAILQ_INIT(&hdinfo);
574 bcache_add_dev(efiblk_pdinfo_count(&hdinfo));
579 efipart_print_common(struct devsw *dev, pdinfo_list_t *pdlist, int verbose)
587 struct disk_devdesc pd_dev;
590 if (STAILQ_EMPTY(pdlist))
593 printf("%s devices:", dev->dv_name);
594 if ((ret = pager_output("\n")) != 0)
597 STAILQ_FOREACH(pd, pdlist, pd_link) {
599 if (verbose) { /* Output the device path. */
600 text = efi_devpath_name(efi_lookup_devpath(h));
603 efi_free_devpath_name(text);
604 if ((ret = pager_output("\n")) != 0)
608 snprintf(line, sizeof(line),
609 " %s%d", dev->dv_name, pd->pd_unit);
611 status = BS->HandleProtocol(h, &blkio_guid, (void **)&blkio);
612 if (!EFI_ERROR(status)) {
614 blkio->Media->LastBlock == 0? 0:
615 (unsigned long long) (blkio->Media->LastBlock + 1));
616 if (blkio->Media->LastBlock != 0) {
617 printf(" X %u", blkio->Media->BlockSize);
620 if (blkio->Media->MediaPresent) {
621 if (blkio->Media->RemovableMedia)
622 printf(" (removable)");
624 printf(" (no media)");
625 if ((ret = pager_output("\n")) != 0)
627 if (!blkio->Media->MediaPresent)
630 pd->pd_blkio = blkio;
632 pd_dev.d_unit = pd->pd_unit;
634 pd_dev.d_partition = -1;
635 pd_dev.d_opendata = blkio;
636 ret = disk_open(&pd_dev, blkio->Media->BlockSize *
637 (blkio->Media->LastBlock + 1),
638 blkio->Media->BlockSize);
640 ret = disk_print(&pd_dev, line, verbose);
645 /* Do not fail from disk_open() */
649 if ((ret = pager_output("\n")) != 0)
657 efipart_printfd(int verbose)
659 return (efipart_print_common(&efipart_fddev, &fdinfo, verbose));
663 efipart_printcd(int verbose)
665 return (efipart_print_common(&efipart_cddev, &cdinfo, verbose));
669 efipart_printhd(int verbose)
671 return (efipart_print_common(&efipart_hddev, &hdinfo, verbose));
675 efiblk_get_pdinfo_list(struct devsw *dev)
677 if (dev->dv_type == DEVT_DISK)
679 if (dev->dv_type == DEVT_CD)
681 if (dev->dv_type == DEVT_FD)
687 efipart_open(struct open_file *f, ...)
690 struct disk_devdesc *dev;
697 dev = va_arg(args, struct disk_devdesc*);
702 pdi = efiblk_get_pdinfo_list(dev->d_dev);
706 pd = efiblk_get_pdinfo(pdi, dev->d_unit);
710 if (pd->pd_blkio == NULL) {
711 status = BS->HandleProtocol(pd->pd_handle, &blkio_guid,
712 (void **)&pd->pd_blkio);
713 if (EFI_ERROR(status))
714 return (efi_status_to_errno(status));
717 blkio = pd->pd_blkio;
718 if (!blkio->Media->MediaPresent)
722 if (pd->pd_bcache == NULL)
723 pd->pd_bcache = bcache_allocate();
725 if (dev->d_dev->dv_type == DEVT_DISK) {
726 return (disk_open(dev,
727 blkio->Media->BlockSize * (blkio->Media->LastBlock + 1),
728 blkio->Media->BlockSize));
734 efipart_close(struct open_file *f)
736 struct disk_devdesc *dev;
740 dev = (struct disk_devdesc *)(f->f_devdata);
743 pdi = efiblk_get_pdinfo_list(dev->d_dev);
747 pd = efiblk_get_pdinfo(pdi, dev->d_unit);
752 if (pd->pd_open == 0) {
754 bcache_free(pd->pd_bcache);
755 pd->pd_bcache = NULL;
757 if (dev->d_dev->dv_type == DEVT_DISK)
758 return (disk_close(dev));
763 efipart_ioctl(struct open_file *f, u_long cmd, void *data)
765 struct disk_devdesc *dev;
770 dev = (struct disk_devdesc *)(f->f_devdata);
773 pdi = efiblk_get_pdinfo_list(dev->d_dev);
777 pd = efiblk_get_pdinfo(pdi, dev->d_unit);
781 if (dev->d_dev->dv_type == DEVT_DISK) {
782 rc = disk_ioctl(dev, cmd, data);
788 case DIOCGSECTORSIZE:
789 *(u_int *)data = pd->pd_blkio->Media->BlockSize;
792 *(uint64_t *)data = pd->pd_blkio->Media->BlockSize *
793 (pd->pd_blkio->Media->LastBlock + 1);
803 * efipart_readwrite()
804 * Internal equivalent of efipart_strategy(), which operates on the
805 * media-native block size. This function expects all I/O requests
806 * to be within the media size and returns an error if such is not
810 efipart_readwrite(EFI_BLOCK_IO *blkio, int rw, daddr_t blk, daddr_t nblks,
817 if (blk < 0 || blk > blkio->Media->LastBlock)
819 if ((blk + nblks - 1) > blkio->Media->LastBlock)
822 switch (rw & F_MASK) {
824 status = blkio->ReadBlocks(blkio, blkio->Media->MediaId, blk,
825 nblks * blkio->Media->BlockSize, buf);
828 if (blkio->Media->ReadOnly)
830 status = blkio->WriteBlocks(blkio, blkio->Media->MediaId, blk,
831 nblks * blkio->Media->BlockSize, buf);
837 if (EFI_ERROR(status)) {
838 printf("%s: rw=%d, blk=%ju size=%ju status=%lu\n", __func__, rw,
839 blk, nblks, EFI_ERROR_CODE(status));
841 return (efi_status_to_errno(status));
845 efipart_strategy(void *devdata, int rw, daddr_t blk, size_t size,
846 char *buf, size_t *rsize)
848 struct bcache_devdata bcd;
849 struct disk_devdesc *dev;
853 dev = (struct disk_devdesc *)devdata;
856 pdi = efiblk_get_pdinfo_list(dev->d_dev);
860 pd = efiblk_get_pdinfo(pdi, dev->d_unit);
864 if (pd->pd_blkio->Media->RemovableMedia &&
865 !pd->pd_blkio->Media->MediaPresent)
868 bcd.dv_strategy = efipart_realstrategy;
869 bcd.dv_devdata = devdata;
870 bcd.dv_cache = pd->pd_bcache;
872 if (dev->d_dev->dv_type == DEVT_DISK) {
873 return (bcache_strategy(&bcd, rw, blk + dev->d_offset,
876 return (bcache_strategy(&bcd, rw, blk, size, buf, rsize));
880 efipart_realstrategy(void *devdata, int rw, daddr_t blk, size_t size,
881 char *buf, size_t *rsize)
883 struct disk_devdesc *dev = (struct disk_devdesc *)devdata;
887 uint64_t off, disk_blocks, d_offset = 0;
889 size_t blkoff, blksz;
892 if (dev == NULL || blk < 0)
895 pdi = efiblk_get_pdinfo_list(dev->d_dev);
899 pd = efiblk_get_pdinfo(pdi, dev->d_unit);
903 blkio = pd->pd_blkio;
907 if (size == 0 || (size % 512) != 0)
912 * Get disk blocks, this value is either for whole disk or for
916 if (dev->d_dev->dv_type == DEVT_DISK) {
917 if (disk_ioctl(dev, DIOCGMEDIASIZE, &disk_blocks) == 0) {
918 /* DIOCGMEDIASIZE does return bytes. */
919 disk_blocks /= blkio->Media->BlockSize;
921 d_offset = dev->d_offset;
923 if (disk_blocks == 0)
924 disk_blocks = blkio->Media->LastBlock + 1 - d_offset;
926 /* make sure we don't read past disk end */
927 if ((off + size) / blkio->Media->BlockSize > d_offset + disk_blocks) {
928 size = d_offset + disk_blocks - off / blkio->Media->BlockSize;
929 size = size * blkio->Media->BlockSize;
935 if ((size % blkio->Media->BlockSize == 0) &&
936 (off % blkio->Media->BlockSize == 0))
937 return (efipart_readwrite(blkio, rw,
938 off / blkio->Media->BlockSize,
939 size / blkio->Media->BlockSize, buf));
942 * The block size of the media is not a multiple of I/O.
944 blkbuf = malloc(blkio->Media->BlockSize);
949 blk = off / blkio->Media->BlockSize;
950 blkoff = off % blkio->Media->BlockSize;
951 blksz = blkio->Media->BlockSize - blkoff;
953 error = efipart_readwrite(blkio, rw, blk, 1, blkbuf);
958 bcopy(blkbuf + blkoff, buf, blksz);
963 blksz = blkio->Media->BlockSize;