From cdadbf097128130443a6dc84e756ffcbe925d3d9 Mon Sep 17 00:00:00 2001 From: Toomas Soome Date: Wed, 22 Nov 2017 08:48:00 +0000 Subject: [PATCH] loader.efi: efipart does not recognize partitionless disks Rework the block device handle check to allow more robust device classification. This is mostly usability issue - it can be quite confusing for user when no disks are listed with lsdev. Add more comments about what and why is done. Reviewed by: imp Differential Revision: https://reviews.freebsd.org/D13026 --- stand/efi/libefi/efipart.c | 111 +++++++++++++++++++++++++++++++++---- 1 file changed, 100 insertions(+), 11 deletions(-) diff --git a/stand/efi/libefi/efipart.c b/stand/efi/libefi/efipart.c index 724233c5ab08..1e161c2bb39d 100644 --- a/stand/efi/libefi/efipart.c +++ b/stand/efi/libefi/efipart.c @@ -195,6 +195,72 @@ efipart_floppy(EFI_DEVICE_PATH *node) return (NULL); } +/* + * Determine if the provided device path is hdd. + * + * There really is no simple fool proof way to classify the devices. + * Since we do build three lists of devices - floppy, cd and hdd, we + * will try to see if the device is floppy or cd, and list anything else + * as hdd. + */ +static bool +efipart_hdd(EFI_DEVICE_PATH *dp) +{ + unsigned i, nin; + EFI_DEVICE_PATH *devpath, *node; + EFI_BLOCK_IO *blkio; + EFI_STATUS status; + + if (dp == NULL) + return (false); + + if ((node = efi_devpath_last_node(dp)) == NULL) + return (false); + + if (efipart_floppy(node) != NULL) + return (false); + + /* + * Test every EFI BLOCK IO handle to make sure dp is not device path + * for CD/DVD. + */ + nin = efipart_nhandles / sizeof (*efipart_handles); + for (i = 0; i < nin; i++) { + devpath = efi_lookup_devpath(efipart_handles[i]); + if (devpath == NULL) + return (false); + + /* Only continue testing when dp is prefix in devpath. */ + if (!efi_devpath_is_prefix(dp, devpath)) + continue; + + /* + * The device path has to have last node describing the + * device, or we can not test the type. + */ + if ((node = efi_devpath_last_node(devpath)) == NULL) + return (false); + + if (DevicePathType(node) == MEDIA_DEVICE_PATH && + DevicePathSubType(node) == MEDIA_CDROM_DP) { + return (false); + } + + /* Make sure we do have the media. */ + status = BS->HandleProtocol(efipart_handles[i], + &blkio_guid, (void **)&blkio); + if (EFI_ERROR(status)) + return (false); + + /* USB or SATA cd without the media. */ + if (blkio->Media->RemovableMedia && + !blkio->Media->MediaPresent) { + return (false); + } + } + return (true); +} + /* * Add or update entries with new handle data. */ @@ -308,9 +374,13 @@ efipart_updatecd(void) if ((node = efi_devpath_last_node(devpath)) == NULL) continue; + if (efipart_floppy(node) != NULL) continue; + if (efipart_hdd(devpath)) + continue; + status = BS->HandleProtocol(efipart_handles[i], &blkio_guid, (void **)&blkio); if (EFI_ERROR(status)) @@ -380,13 +450,21 @@ efipart_hdinfo_add(EFI_HANDLE disk_handle, EFI_HANDLE part_handle) pdinfo_t *hd, *pd, *last; disk_devpath = efi_lookup_devpath(disk_handle); - part_devpath = efi_lookup_devpath(part_handle); - if (disk_devpath == NULL || part_devpath == NULL) { + if (disk_devpath == NULL) return (ENOENT); + + if (part_handle != NULL) { + part_devpath = efi_lookup_devpath(part_handle); + if (part_devpath == NULL) + return (ENOENT); + node = (HARDDRIVE_DEVICE_PATH *) + efi_devpath_last_node(part_devpath); + if (node == NULL) + return (ENOENT); /* This should not happen. */ + } else { + part_devpath = NULL; + node = NULL; } - node = (HARDDRIVE_DEVICE_PATH *)efi_devpath_last_node(part_devpath); - if (node == NULL) - return (ENOENT); /* This should not happen. */ pd = calloc(1, sizeof(pdinfo_t)); if (pd == NULL) { @@ -397,6 +475,9 @@ efipart_hdinfo_add(EFI_HANDLE disk_handle, EFI_HANDLE part_handle) STAILQ_FOREACH(hd, &hdinfo, pd_link) { if (efi_devpath_match(hd->pd_devpath, disk_devpath) == true) { + if (part_devpath == NULL) + return (0); + /* Add the partition. */ pd->pd_handle = part_handle; pd->pd_unit = node->PartitionNumber; @@ -419,6 +500,9 @@ efipart_hdinfo_add(EFI_HANDLE disk_handle, EFI_HANDLE part_handle) hd->pd_devpath = disk_devpath; STAILQ_INSERT_TAIL(&hdinfo, hd, pd_link); + if (part_devpath == NULL) + return (0); + pd = calloc(1, sizeof(pdinfo_t)); if (pd == NULL) { printf("Failed to add partition, out of memory\n"); @@ -541,7 +625,8 @@ efipart_updatehd(void) if ((node = efi_devpath_last_node(devpath)) == NULL) continue; - if (efipart_floppy(node) != NULL) + + if (!efipart_hdd(devpath)) continue; status = BS->HandleProtocol(efipart_handles[i], @@ -549,6 +634,12 @@ efipart_updatehd(void) if (EFI_ERROR(status)) continue; + if (DevicePathType(node) == MEDIA_DEVICE_PATH && + DevicePathSubType(node) == MEDIA_FILEPATH_DP) { + efipart_hdinfo_add_filepath(efipart_handles[i]); + continue; + } + if (DevicePathType(node) == MEDIA_DEVICE_PATH && DevicePathSubType(node) == MEDIA_HARDDRIVE_DP) { devpathcpy = efi_devpath_trim(devpath); @@ -568,18 +659,16 @@ efipart_updatehd(void) continue; if ((node = efi_devpath_last_node(devpathcpy)) == NULL) continue; + if (DevicePathType(node) == MEDIA_DEVICE_PATH && DevicePathSubType(node) == MEDIA_HARDDRIVE_DP) continue; + efipart_hdinfo_add(handle, efipart_handles[i]); continue; } - if (DevicePathType(node) == MEDIA_DEVICE_PATH && - DevicePathSubType(node) == MEDIA_FILEPATH_DP) { - efipart_hdinfo_add_filepath(efipart_handles[i]); - continue; - } + efipart_hdinfo_add(efipart_handles[i], NULL); } }