svn commit: r355224 - in head/stand/efi: include libefi

Toomas Soome tsoome at FreeBSD.org
Sat Nov 30 09:11:29 UTC 2019


Author: tsoome
Date: Sat Nov 30 09:11:28 2019
New Revision: 355224
URL: https://svnweb.freebsd.org/changeset/base/355224

Log:
  loader.efi: efipart needs better support detecting nested partitions
  
  Just as disks can have nested partitions, the same happens with cd devices,
  so we need to detect device paths and make sure we will not mix the handles.
  
  To address this:
  
  we fetch handle array and create linked list of block devices.
  we walk the list and detect parent devices and set children pd_parent.
  for {fd, cd, hd}, we walk device list and pick up our devices and store to
  corresponding list. We make sure we store parent device first.
  
  For sorting we use 3 steps: We check for floppy, we check for cd and then
  everything else must be hd.
  
  In general, it seems the floppy devices have no parent.
  CD can have both parents and children (multiple boot entries, partitions
  from the hybrid disk image).
  
  Tested by: cross+freebsd at distal.com on Cisco UCS systems, C200 series (C220M5, C240M4).
  Also on MBP with UEFI 1.10
  
  Reported by:	Chriss Ross
  MFC after:	1w
  Differential Revision:	https://reviews.freebsd.org/D22553

Modified:
  head/stand/efi/include/efidevp.h
  head/stand/efi/include/efilib.h
  head/stand/efi/libefi/devpath.c
  head/stand/efi/libefi/efipart.c

Modified: head/stand/efi/include/efidevp.h
==============================================================================
--- head/stand/efi/include/efidevp.h	Sat Nov 30 06:02:13 2019	(r355223)
+++ head/stand/efi/include/efidevp.h	Sat Nov 30 09:11:28 2019	(r355224)
@@ -288,6 +288,14 @@ typedef struct _UART_DEVICE_PATH {
 #define DEVICE_PATH_MESSAGING_VT_UTF8 \
     { 0xad15a0d6, 0x8bec, 0x4acf, {0xa0, 0x73, 0xd0, 0x1d, 0xe7, 0x7e, 0x2d, 0x88} }
 
+/* Device Logical Unit SubType. */
+#define	MSG_DEVICE_LOGICAL_UNIT_DP	0x11
+typedef struct {
+	EFI_DEVICE_PATH			Header;
+	/* Logical Unit Number for the interface. */
+	UINT8				Lun;
+} DEVICE_LOGICAL_UNIT_DEVICE_PATH;
+
 #define MSG_SATA_DP			0x12
 typedef struct _SATA_DEVICE_PATH {
 	EFI_DEVICE_PATH			Header;

Modified: head/stand/efi/include/efilib.h
==============================================================================
--- head/stand/efi/include/efilib.h	Sat Nov 30 06:02:13 2019	(r355223)
+++ head/stand/efi/include/efilib.h	Sat Nov 30 09:11:28 2019	(r355224)
@@ -84,6 +84,7 @@ int efi_handle_update_dev(EFI_HANDLE, struct devsw *, 
 
 EFI_DEVICE_PATH *efi_lookup_image_devpath(EFI_HANDLE);
 EFI_DEVICE_PATH *efi_lookup_devpath(EFI_HANDLE);
+void efi_close_devpath(EFI_HANDLE);
 EFI_HANDLE efi_devpath_handle(EFI_DEVICE_PATH *);
 EFI_DEVICE_PATH *efi_devpath_last_node(EFI_DEVICE_PATH *);
 EFI_DEVICE_PATH *efi_devpath_trim(EFI_DEVICE_PATH *);

Modified: head/stand/efi/libefi/devpath.c
==============================================================================
--- head/stand/efi/libefi/devpath.c	Sat Nov 30 06:02:13 2019	(r355223)
+++ head/stand/efi/libefi/devpath.c	Sat Nov 30 09:11:28 2019	(r355224)
@@ -67,6 +67,16 @@ efi_lookup_devpath(EFI_HANDLE handle)
 	return (devpath);
 }
 
+void
+efi_close_devpath(EFI_HANDLE handle)
+{
+	EFI_STATUS status;
+
+	status = BS->CloseProtocol(handle, &DevicePathGUID, IH, NULL);
+	if (EFI_ERROR(status))
+		printf("CloseProtocol error: %lu\n", EFI_ERROR_CODE(status));
+}
+
 static char *
 efi_make_tail(char *suffix)
 {

Modified: head/stand/efi/libefi/efipart.c
==============================================================================
--- head/stand/efi/libefi/efipart.c	Sat Nov 30 06:02:13 2019	(r355223)
+++ head/stand/efi/libefi/efipart.c	Sat Nov 30 09:11:28 2019	(r355224)
@@ -44,9 +44,11 @@ __FBSDID("$FreeBSD$");
 
 static EFI_GUID blkio_guid = BLOCK_IO_PROTOCOL;
 
+typedef bool (*pd_test_cb_t)(pdinfo_t *, pdinfo_t *);
 static int efipart_initfd(void);
 static int efipart_initcd(void);
 static int efipart_inithd(void);
+static void efipart_cdinfo_add(pdinfo_t *);
 
 static int efipart_strategy(void *, int, daddr_t, size_t, char *, size_t *);
 static int efipart_realstrategy(void *, int, daddr_t, size_t, char *, size_t *);
@@ -209,6 +211,123 @@ efiblk_pdinfo_count(pdinfo_list_t *pdi)
 	return (i);
 }
 
+static pdinfo_t *
+efipart_find_parent(pdinfo_list_t *pdi, EFI_DEVICE_PATH *devpath)
+{
+	pdinfo_t *pd;
+	EFI_DEVICE_PATH *parent;
+
+	/* We want to find direct parent */
+	parent = efi_devpath_trim(devpath);
+	/* We should not get out of memory here but be careful. */
+	if (parent == NULL)
+		return (NULL);
+
+	STAILQ_FOREACH(pd, pdi, pd_link) {
+		/* We must have exact match. */
+		if (efi_devpath_match(pd->pd_devpath, parent))
+			break;
+	}
+	free(parent);
+	return (pd);
+}
+
+/*
+ * Return true when we should ignore this device.
+ */
+static bool
+efipart_ignore_device(EFI_HANDLE h, EFI_BLOCK_IO *blkio,
+    EFI_DEVICE_PATH *devpath)
+{
+	EFI_DEVICE_PATH *node, *parent;
+
+	/*
+	 * We assume the block size 512 or greater power of 2.
+	 * Also skip devices with block size > 64k (16 is max
+	 * ashift supported by zfs).
+	 * iPXE is known to insert stub BLOCK IO device with
+	 * BlockSize 1.
+	 */
+	if (blkio->Media->BlockSize < 512 ||
+	    blkio->Media->BlockSize > (1 << 16) ||
+	    !powerof2(blkio->Media->BlockSize)) {
+		efi_close_devpath(h);
+		return (true);
+	}
+
+	/* Allowed values are 0, 1 and power of 2. */
+	if (blkio->Media->IoAlign > 1 &&
+	    !powerof2(blkio->Media->IoAlign)) {
+		efi_close_devpath(h);
+		return (true);
+	}
+
+	/*
+	 * With device tree setup:
+	 * PciRoot(0x0)/Pci(0x14,0x0)/USB(0x5,0)/USB(0x2,0x0)
+	 * PciRoot(0x0)/Pci(0x14,0x0)/USB(0x5,0)/USB(0x2,0x0)/Unit(0x1)
+	 * PciRoot(0x0)/Pci(0x14,0x0)/USB(0x5,0)/USB(0x2,0x0)/Unit(0x2)
+	 * PciRoot(0x0)/Pci(0x14,0x0)/USB(0x5,0)/USB(0x2,0x0)/Unit(0x3)
+	 * PciRoot(0x0)/Pci(0x14,0x0)/USB(0x5,0)/USB(0x2,0x0)/Unit(0x3)/CDROM..
+	 * PciRoot(0x0)/Pci(0x14,0x0)/USB(0x5,0)/USB(0x2,0x0)/Unit(0x3)/CDROM..
+	 * PciRoot(0x0)/Pci(0x14,0x0)/USB(0x5,0)/USB(0x2,0x0)/Unit(0x4)
+	 * PciRoot(0x0)/Pci(0x14,0x0)/USB(0x5,0)/USB(0x2,0x0)/Unit(0x5)
+	 * PciRoot(0x0)/Pci(0x14,0x0)/USB(0x5,0)/USB(0x2,0x0)/Unit(0x6)
+	 * PciRoot(0x0)/Pci(0x14,0x0)/USB(0x5,0)/USB(0x2,0x0)/Unit(0x7)
+	 *
+	 * In above exmple only Unit(0x3) has media, all other nodes are
+	 * missing media and should not be used.
+	 *
+	 * No media does not always mean there is no device, but in above
+	 * case, we can not really assume there is any device.
+	 * Therefore, if this node is USB, or this node is Unit (LUN) and
+	 * direct parent is USB and we have no media, we will ignore this
+	 * device.
+	 */
+
+	/* Do not ignore device with media. */
+	if (blkio->Media->MediaPresent)
+		return (false);
+
+	node = efi_devpath_last_node(devpath);
+	if (node == NULL)
+		return (false);
+
+	/* USB without media present */
+	if (DevicePathType(node) == MESSAGING_DEVICE_PATH &&
+	    DevicePathSubType(node) == MSG_USB_DP) {
+		efi_close_devpath(h);
+		return (true);
+	}
+
+	parent = efi_devpath_trim(devpath);
+	if (parent != NULL) {
+		bool parent_is_usb = false;
+
+		node = efi_devpath_last_node(parent);
+		if (node == NULL) {
+			free(parent);
+			return (false);
+		}
+		if (DevicePathType(node) == MESSAGING_DEVICE_PATH &&
+		    DevicePathSubType(node) == MSG_USB_DP)
+			parent_is_usb = true;
+		free(parent);
+
+		/* no media, parent is USB and devicepath is lun. */
+		node = efi_devpath_last_node(devpath);
+		if (node == NULL)
+			return (false);
+		if (parent_is_usb &&
+		    DevicePathType(node) == MESSAGING_DEVICE_PATH &&
+	    	    DevicePathSubType(node) == MSG_DEVICE_LOGICAL_UNIT_DP) {
+			efi_close_devpath(h);
+			return (true);
+		}
+	}
+	return (false);
+}
+
 int
 efipart_inithandles(void)
 {
@@ -256,25 +375,9 @@ efipart_inithandles(void)
 			continue;
 		}
 
-		/*
-		 * We assume the block size 512 or greater power of 2.
-		 * Also skip devices with block size > 64k (16 is max
-		 * ashift supported by zfs).
-		 * iPXE is known to insert stub BLOCK IO device with
-		 * BlockSize 1.
-		 */
-		if (blkio->Media->BlockSize < 512 ||
-		    blkio->Media->BlockSize > (1 << 16) ||
-		    !powerof2(blkio->Media->BlockSize)) {
+		if (efipart_ignore_device(hin[i], blkio, devpath))
 			continue;
-		}
 
-		/* Allowed values are 0, 1 and power of 2. */
-		if (blkio->Media->IoAlign > 1 &&
-		    !powerof2(blkio->Media->IoAlign)) {
-			continue;
-		}
-
 		/* This is bad. */
 		if ((pd = calloc(1, sizeof(*pd))) == NULL) {
 			printf("efipart_inithandles: Out of memory.\n");
@@ -289,10 +392,32 @@ efipart_inithandles(void)
 		STAILQ_INSERT_TAIL(&pdinfo, pd, pd_link);
 	}
 
+	/*
+	 * Walk pdinfo and set parents based on device path.
+	 */
+	STAILQ_FOREACH(pd, &pdinfo, pd_link) {
+		pd->pd_parent = efipart_find_parent(&pdinfo, pd->pd_devpath);
+	}
 	free(hin);
 	return (0);
 }
 
+/*
+ * Get node identified by pd_test() from plist.
+ */
+static pdinfo_t *
+efipart_get_pd(pdinfo_list_t *plist, pd_test_cb_t pd_test, pdinfo_t *data)
+{
+	pdinfo_t *pd;
+
+	STAILQ_FOREACH(pd, plist, pd_link) {
+		if (pd_test(pd, data))
+			break;
+	}
+
+	return (pd);
+}
+
 static ACPI_HID_DEVICE_PATH *
 efipart_floppy(EFI_DEVICE_PATH *node)
 {
@@ -310,19 +435,19 @@ efipart_floppy(EFI_DEVICE_PATH *node)
 	return (NULL);
 }
 
-static pdinfo_t *
-efipart_find_parent(pdinfo_list_t *pdi, EFI_DEVICE_PATH *devpath)
+static bool
+efipart_testfd(pdinfo_t *fd, pdinfo_t *data __unused)
 {
-	pdinfo_t *pd, *part;
+	EFI_DEVICE_PATH *node;
 
-	STAILQ_FOREACH(pd, pdi, pd_link) {
-		if (efi_devpath_is_prefix(pd->pd_devpath, devpath))
-			return (pd);
-		part = efipart_find_parent(&pd->pd_part, devpath);
-		if (part != NULL)
-			return (part);
-	}
-	return (NULL);
+	node = efi_devpath_last_node(fd->pd_devpath);
+	if (node == NULL)
+		return (false);
+
+	if (efipart_floppy(node) != NULL)
+		return (true);
+
+	return (false);
 }
 
 static int
@@ -332,8 +457,7 @@ efipart_initfd(void)
 	ACPI_HID_DEVICE_PATH *acpi;
 	pdinfo_t *parent, *fd;
 
-restart:
-	STAILQ_FOREACH(fd, &pdinfo, pd_link) {
+	while ((fd = efipart_get_pd(&pdinfo, efipart_testfd, NULL)) != NULL) {
 		if ((node = efi_devpath_last_node(fd->pd_devpath)) == NULL)
 			continue;
 
@@ -341,7 +465,7 @@ restart:
 			continue;
 
 		STAILQ_REMOVE(&pdinfo, fd, pdinfo, pd_link);
-		parent = efipart_find_parent(&pdinfo, fd->pd_devpath);
+		parent = fd->pd_parent;
 		if (parent != NULL) {
 			STAILQ_REMOVE(&pdinfo, parent, pdinfo, pd_link);
 			parent->pd_alias = fd->pd_handle;
@@ -353,7 +477,6 @@ restart:
 		}
 		fd->pd_devsw = &efipart_fddev;
 		STAILQ_INSERT_TAIL(&fdinfo, fd, pd_link);
-		goto restart;
 	}
 
 	bcache_add_dev(efiblk_pdinfo_count(&fdinfo));
@@ -366,208 +489,136 @@ restart:
 static void
 efipart_cdinfo_add(pdinfo_t *cd)
 {
-	pdinfo_t *pd, *last;
+	pdinfo_t *parent, *pd, *last;
 
-	STAILQ_FOREACH(pd, &cdinfo, pd_link) {
-		if (efi_devpath_is_prefix(pd->pd_devpath, cd->pd_devpath)) {
-			last = STAILQ_LAST(&pd->pd_part, pdinfo, pd_link);
-			if (last != NULL)
-				cd->pd_unit = last->pd_unit + 1;
-			else
-				cd->pd_unit = 0;
-			cd->pd_parent = pd;
-			cd->pd_devsw = &efipart_cddev;
-			STAILQ_INSERT_TAIL(&pd->pd_part, cd, pd_link);
-			return;
+	if (cd == NULL)
+		return;
+
+	parent = cd->pd_parent;
+	/* Make sure we have parent added */
+	efipart_cdinfo_add(parent);
+
+	STAILQ_FOREACH(pd, &pdinfo, pd_link) {
+		if (efi_devpath_match(pd->pd_devpath, cd->pd_devpath)) {
+			STAILQ_REMOVE(&pdinfo, cd, pdinfo, pd_link);
+			break;
 		}
 	}
+	if (pd == NULL) {
+		/* This device is already added. */
+		return;
+	}
 
+	if (parent != NULL) {
+		last = STAILQ_LAST(&parent->pd_part, pdinfo, pd_link);
+		if (last != NULL)
+			cd->pd_unit = last->pd_unit + 1;
+		else
+			cd->pd_unit = 0;
+		cd->pd_devsw = &efipart_cddev;
+		STAILQ_INSERT_TAIL(&parent->pd_part, cd, pd_link);
+		return;
+	}
+
 	last = STAILQ_LAST(&cdinfo, pdinfo, pd_link);
 	if (last != NULL)
 		cd->pd_unit = last->pd_unit + 1;
 	else
 		cd->pd_unit = 0;
 
-	cd->pd_parent = NULL;
 	cd->pd_devsw = &efipart_cddev;
 	STAILQ_INSERT_TAIL(&cdinfo, cd, pd_link);
 }
 
 static bool
-efipart_testcd(EFI_DEVICE_PATH *node, EFI_BLOCK_IO *blkio)
+efipart_testcd(pdinfo_t *cd, pdinfo_t *data __unused)
 {
+	EFI_DEVICE_PATH *node;
+
+	node = efi_devpath_last_node(cd->pd_devpath);
+	if (node == NULL)
+		return (false);
+
+	if (efipart_floppy(node) != NULL)
+		return (false);
+
 	if (DevicePathType(node) == MEDIA_DEVICE_PATH &&
 	    DevicePathSubType(node) == MEDIA_CDROM_DP) {
 		return (true);
 	}
 
 	/* cd drive without the media. */
-	if (blkio->Media->RemovableMedia &&
-	    !blkio->Media->MediaPresent) {
+	if (cd->pd_blkio->Media->RemovableMedia &&
+	    !cd->pd_blkio->Media->MediaPresent) {
 		return (true);
 	}
 
 	return (false);
 }
 
-static void
-efipart_updatecd(void)
+/*
+ * Test if pd is parent for device.
+ */
+static bool
+efipart_testchild(pdinfo_t *dev, pdinfo_t *pd)
 {
-	EFI_DEVICE_PATH *devpath, *node;
-	EFI_STATUS status;
-	pdinfo_t *parent, *cd;
+	/* device with no parent. */
+	if (dev->pd_parent == NULL)
+		return (false);
 
-restart:
-	STAILQ_FOREACH(cd, &pdinfo, pd_link) {
-		if ((node = efi_devpath_last_node(cd->pd_devpath)) == NULL)
-			continue;
-
-		if (efipart_floppy(node) != NULL)
-			continue;
-
-		/* Is parent of this device already registered? */
-		parent = efipart_find_parent(&cdinfo, cd->pd_devpath);
-		if (parent != NULL) {
-			STAILQ_REMOVE(&pdinfo, cd, pdinfo, pd_link);
-			efipart_cdinfo_add(cd);
-			goto restart;
-		}
-
-		if (!efipart_testcd(node, cd->pd_blkio))
-			continue;
-
-		/* Find parent and unlink both parent and cd from pdinfo */
-		STAILQ_REMOVE(&pdinfo, cd, pdinfo, pd_link);
-		parent = efipart_find_parent(&pdinfo, cd->pd_devpath);
-		if (parent != NULL) {
-			STAILQ_REMOVE(&pdinfo, parent, pdinfo, pd_link);
-			efipart_cdinfo_add(parent);
-		}
-
-		if (parent == NULL)
-			parent = efipart_find_parent(&cdinfo, cd->pd_devpath);
-
-		/*
-		 * If we come across a logical partition of subtype CDROM
-		 * it doesn't refer to the CD filesystem itself, but rather
-		 * to any usable El Torito boot image on it. In this case
-		 * we try to find the parent device and add that instead as
-		 * that will be the CD filesystem.
-		 */
-		if (DevicePathType(node) == MEDIA_DEVICE_PATH &&
-		    DevicePathSubType(node) == MEDIA_CDROM_DP &&
-		    parent == NULL) {
-			parent = calloc(1, sizeof(*parent));
-			if (parent == NULL) {
-				printf("efipart_updatecd: out of memory\n");
-				/* this device is lost but try again. */
-				free(cd);
-				goto restart;
-			}
-
-			devpath = efi_devpath_trim(cd->pd_devpath);
-			if (devpath == NULL) {
-				printf("efipart_updatecd: out of memory\n");
-				/* this device is lost but try again. */
-				free(parent);
-				free(cd);
-				goto restart;
-			}
-			parent->pd_devpath = devpath;
-			status = BS->LocateDevicePath(&blkio_guid,
-			    &parent->pd_devpath, &parent->pd_handle);
-			free(devpath);
-			if (EFI_ERROR(status)) {
-				printf("efipart_updatecd: error %lu\n",
-				    EFI_ERROR_CODE(status));
-				free(parent);
-				free(cd);
-				goto restart;
-			}
-			parent->pd_devpath =
-			    efi_lookup_devpath(parent->pd_handle);
-			efipart_cdinfo_add(parent);
-		}
-
-		efipart_cdinfo_add(cd);
-		goto restart;
+	if (efi_devpath_match(dev->pd_parent->pd_devpath, pd->pd_devpath)) {
+		return (true);
 	}
+	return (false);
 }
 
 static int
 efipart_initcd(void)
 {
-	efipart_updatecd();
+	pdinfo_t *cd;
 
+	while ((cd = efipart_get_pd(&pdinfo, efipart_testcd, NULL)) != NULL)
+		efipart_cdinfo_add(cd);
+
+	/* Find all children of CD devices we did add above. */
+	STAILQ_FOREACH(cd, &cdinfo, pd_link) {
+		pdinfo_t *child;
+
+		for (child = efipart_get_pd(&pdinfo, efipart_testchild, cd);
+		    child != NULL;
+		    child = efipart_get_pd(&pdinfo, efipart_testchild, cd))
+			efipart_cdinfo_add(child);
+	}
 	bcache_add_dev(efiblk_pdinfo_count(&cdinfo));
 	return (0);
 }
 
-static bool
+static void
 efipart_hdinfo_add_node(pdinfo_t *hd, EFI_DEVICE_PATH *node)
 {
-	pdinfo_t *pd, *ptr;
+	pdinfo_t *parent, *ptr;
 
 	if (node == NULL)
-		return (false);
+		return;
 
-	/* Find our disk device. */
-	STAILQ_FOREACH(pd, &hdinfo, pd_link) {
-		if (efi_devpath_is_prefix(pd->pd_devpath, hd->pd_devpath))
-			break;
-	}
-	if (pd == NULL)
-		return (false);
-
-	/* If the node is not MEDIA_HARDDRIVE_DP, it is sub-partition. */
+	parent = hd->pd_parent;
+	/*
+	 * If the node is not MEDIA_HARDDRIVE_DP, it is sub-partition.
+	 * This can happen with Vendor nodes, and since we do not know
+	 * the more about those nodes, we just count them.
+	 */
 	if (DevicePathSubType(node) != MEDIA_HARDDRIVE_DP) {
-		STAILQ_FOREACH(ptr, &pd->pd_part, pd_link) {
-			if (efi_devpath_is_prefix(ptr->pd_devpath,
-			    hd->pd_devpath))
-				break;
-		}
-		/*
-		 * ptr == NULL means we have handles in unexpected order
-		 * and we would need to re-order the partitions later.
-		 */
+		ptr = STAILQ_LAST(&parent->pd_part, pdinfo, pd_link);
 		if (ptr != NULL)
-			pd = ptr;
-	}
-
-	/* Add the partition. */
-	if (DevicePathSubType(node) == MEDIA_HARDDRIVE_DP) {
-		hd->pd_unit = ((HARDDRIVE_DEVICE_PATH *)node)->PartitionNumber;
-	} else {
-		ptr = STAILQ_LAST(&pd->pd_part, pdinfo, pd_link);
-		if (ptr != NULL)
 			hd->pd_unit = ptr->pd_unit + 1;
 		else
 			hd->pd_unit = 0;
+	} else {
+		hd->pd_unit = ((HARDDRIVE_DEVICE_PATH *)node)->PartitionNumber;
 	}
-	hd->pd_parent = pd;
-	hd->pd_devsw = &efipart_hddev;
 
-	STAILQ_INSERT_TAIL(&pd->pd_part, hd, pd_link);
-	return (true);
-}
-
-static void
-efipart_hdinfo_add(pdinfo_t *hd, EFI_DEVICE_PATH *node)
-{
-	pdinfo_t *last;
-
-	if (efipart_hdinfo_add_node(hd, node))
-		return;
-
-	last = STAILQ_LAST(&hdinfo, pdinfo, pd_link);
-	if (last != NULL)
-		hd->pd_unit = last->pd_unit + 1;
-	else
-		hd->pd_unit = 0;
-
-	/* Add the disk. */
 	hd->pd_devsw = &efipart_hddev;
-	STAILQ_INSERT_TAIL(&hdinfo, hd, pd_link);
+	STAILQ_INSERT_TAIL(&parent->pd_part, hd, pd_link);
 }
 
 /*
@@ -640,96 +691,75 @@ efipart_hdinfo_add_filepath(pdinfo_t *hd, FILEPATH_DEV
 }
 
 static void
-efipart_updatehd(void)
+efipart_hdinfo_add(pdinfo_t *hd)
 {
-	EFI_DEVICE_PATH *devpath, *node;
-	EFI_STATUS status;
-	pdinfo_t *parent, *hd;
+	pdinfo_t *parent, *pd, *last;
+	EFI_DEVICE_PATH *node;
 
-restart:
-	STAILQ_FOREACH(hd, &pdinfo, pd_link) {
-		if ((node = efi_devpath_last_node(hd->pd_devpath)) == NULL)
-			continue;
+	if (hd == NULL)
+		return;
 
-		if (efipart_floppy(node) != NULL)
-			continue;
+	parent = hd->pd_parent;
+	/* Make sure we have parent added */
+	efipart_hdinfo_add(parent);
 
-		if (efipart_testcd(node, hd->pd_blkio))
-			continue;
-
-		if (DevicePathType(node) == HARDWARE_DEVICE_PATH &&
-		    (DevicePathSubType(node) == HW_PCI_DP ||
-		     DevicePathSubType(node) == HW_VENDOR_DP)) {
+	STAILQ_FOREACH(pd, &pdinfo, pd_link) {
+		if (efi_devpath_match(pd->pd_devpath, hd->pd_devpath)) {
 			STAILQ_REMOVE(&pdinfo, hd, pdinfo, pd_link);
-			efipart_hdinfo_add(hd, NULL);
-			goto restart;
+			break;
 		}
+	}
+	if (pd == NULL) {
+		/* This device is already added. */
+		return;
+	}
 
-		if (DevicePathType(node) == MEDIA_DEVICE_PATH &&
-		    DevicePathSubType(node) == MEDIA_FILEPATH_DP) {
-			STAILQ_REMOVE(&pdinfo, hd, pdinfo, pd_link);
-			efipart_hdinfo_add_filepath(hd,
-			    (FILEPATH_DEVICE_PATH *)node);
-			goto restart;
-		}
+	if ((node = efi_devpath_last_node(hd->pd_devpath)) == NULL)
+		return;
 
-		STAILQ_REMOVE(&pdinfo, hd, pdinfo, pd_link);
-		parent = efipart_find_parent(&pdinfo, hd->pd_devpath);
-		if (parent != NULL) {
-			STAILQ_REMOVE(&pdinfo, parent, pdinfo, pd_link);
-			efipart_hdinfo_add(parent, NULL);
-		} else {
-			parent = efipart_find_parent(&hdinfo, hd->pd_devpath);
-		}
+	if (DevicePathType(node) == MEDIA_DEVICE_PATH &&
+	    DevicePathSubType(node) == MEDIA_FILEPATH_DP) {
+		efipart_hdinfo_add_filepath(hd,
+		    (FILEPATH_DEVICE_PATH *)node);
+		return;
+	}
 
-		if (DevicePathType(node) == MEDIA_DEVICE_PATH &&
-		    DevicePathSubType(node) == MEDIA_HARDDRIVE_DP &&
-		    parent == NULL) {
-			parent = calloc(1, sizeof(*parent));
-			if (parent == NULL) {
-				printf("efipart_updatehd: out of memory\n");
-				/* this device is lost but try again. */
-				free(hd);
-				goto restart;
-			}
+	if (parent != NULL) {
+		efipart_hdinfo_add_node(hd, node);
+		return;
+	}
 
-			devpath = efi_devpath_trim(hd->pd_devpath);
-			if (devpath == NULL) {
-				printf("efipart_updatehd: out of memory\n");
-				/* this device is lost but try again. */
-				free(parent);
-				free(hd);
-				goto restart;
-			}
+	last = STAILQ_LAST(&hdinfo, pdinfo, pd_link);
+	if (last != NULL)
+		hd->pd_unit = last->pd_unit + 1;
+	else
+		hd->pd_unit = 0;
 
-			parent->pd_devpath = devpath;
-			status = BS->LocateDevicePath(&blkio_guid,
-			    &parent->pd_devpath, &parent->pd_handle);
-			free(devpath);
-			if (EFI_ERROR(status)) {
-				printf("efipart_updatehd: error %lu\n",
-				    EFI_ERROR_CODE(status));
-				free(parent);
-				free(hd);
-				goto restart;
-			}
+	/* Add the disk. */
+	hd->pd_devsw = &efipart_hddev;
+	STAILQ_INSERT_TAIL(&hdinfo, hd, pd_link);
+}
 
-			parent->pd_devpath =
-			    efi_lookup_devpath(&parent->pd_handle);
+static bool
+efipart_testhd(pdinfo_t *hd, pdinfo_t *data __unused)
+{
+	if (efipart_testfd(hd, NULL))
+		return (false);
 
-			efipart_hdinfo_add(parent, NULL);
-		}
+	if (efipart_testcd(hd, NULL))
+		return (false);
 
-		efipart_hdinfo_add(hd, node);
-		goto restart;
-	}
+	/* Anything else must be HD. */
+	return (true);
 }
 
 static int
 efipart_inithd(void)
 {
+	pdinfo_t *hd;
 
-	efipart_updatehd();
+	while ((hd = efipart_get_pd(&pdinfo, efipart_testhd, NULL)) != NULL)
+		efipart_hdinfo_add(hd);
 
 	bcache_add_dev(efiblk_pdinfo_count(&hdinfo));
 	return (0);


More information about the svn-src-head mailing list