[Concept,11/14] efi: Add device-path support for EFI media devices

Message ID 20250820144621.1073679-12-sjg@u-boot.org
State New
Headers
Series efi: app: Support booting an OS |

Commit Message

Simon Glass Aug. 20, 2025, 2:46 p.m. UTC
  From: Simon Glass <sjg@chromium.org>

Add proper device-path handling for UCLASS_EFI_MEDIA devices in both
dp_size() and dp_fill() functions. This enables EFI applications to use
firmware device-paths for media devices accessed through the EFI block
I/O protocol.

Add some debugging while we are here.

Co-developed-by: Claude <noreply@anthropic.com>
Signed-off-by: Simon Glass <sjg@chromium.org>
---

 lib/efi/device_path.c | 116 +++++++++++++++++++++++++++++++++++++++++-
 1 file changed, 115 insertions(+), 1 deletion(-)
  

Patch

diff --git a/lib/efi/device_path.c b/lib/efi/device_path.c
index 0b2eb7561b1..449b9d4239b 100644
--- a/lib/efi/device_path.c
+++ b/lib/efi/device_path.c
@@ -375,6 +375,10 @@  __maybe_unused static unsigned int dp_size(struct udevice *dev)
 				size =
 				      sizeof(struct efi_device_path_controller);
 			break;
+		case UCLASS_EFI_MEDIA:
+			/* EFI app */
+			size = sizeof(struct efi_device_path_udevice);
+			break;
 		default:
 			/* UCLASS_BLKMAP, UCLASS_HOST, UCLASS_VIRTIO */
 			size = sizeof(struct efi_device_path_udevice);
@@ -389,6 +393,9 @@  __maybe_unused static unsigned int dp_size(struct udevice *dev)
 	case UCLASS_USB_HUB:
 		size = sizeof(struct efi_device_path_usb);
 		break;
+	case UCLASS_EFI_MEDIA:
+		size = sizeof(struct efi_device_path_udevice);
+		break;
 	default:
 		size = sizeof(struct efi_device_path_udevice);
 		break;
@@ -519,6 +526,22 @@  __maybe_unused static void *dp_fill(void *buf, struct udevice *dev)
 			return &dp[1];
 			}
 			break;
+		case UCLASS_EFI_MEDIA:
+		if (IS_ENABLED(CONFIG_EFI_APP)) {
+			struct efi_device_path_udevice *dp = buf;
+			struct blk_desc *desc = dev_get_uclass_plat(dev);
+
+			dp->dp.type = DEVICE_PATH_TYPE_HARDWARE_DEVICE;
+			dp->dp.sub_type = DEVICE_PATH_SUB_TYPE_VENDOR;
+			dp->dp.length = sizeof(*dp);
+			memcpy(&dp->guid, &efi_u_boot_guid, sizeof(efi_guid_t));
+			dp->uclass_id = (UCLASS_BLK & 0xffff) |
+					(desc->uclass_id << 16);
+			dp->dev_number = desc->devnum;
+
+			return &dp[1];
+		}
+			break;
 		default: {
 			/* UCLASS_BLKMAP, UCLASS_HOST, UCLASS_VIRTIO */
 			struct efi_device_path_udevice *dp = buf;
@@ -1013,6 +1036,86 @@  out:
 	return EFI_SUCCESS;
 }
 
+/**
+ * efi_dp_from_efi_app() - create device path for EFI app
+ *
+ * Create a device path for EFI applications using firmware device paths
+ * from EFI media devices
+ *
+ * @devnr:	device number string (format: "dev:part")
+ * @descp:	pointer to store block device descriptor
+ * @partp:	pointer to store partition number
+ * @dp:		pointer to store created device path
+ * Return:	U-Boot error code (0 on success, negative on error)
+ */
+static int efi_dp_from_efi_app(const char *devnr,
+			       struct blk_desc **descp, int *partp,
+			       struct efi_device_path **dpp)
+{
+	struct efi_media_plat *plat;
+	struct efi_device_path *dp;
+	struct udevice *media_dev;
+	struct blk_desc *desc;
+	int part, dev_num;
+	char *ep;
+	int ret;
+
+	log_debug("using EFI app firmware device path for devnr='%s'\n", devnr);
+
+	/* parse device number from devnr (format: "devnum:part") */
+	dev_num = hextoul(devnr, &ep);
+	if (*ep != ':') {
+		log_err("invalid EFI device format: '%s'\n", devnr);
+		return log_msg_ret("eda", -EINVAL);
+	}
+
+	/* find the EFI media device */
+	ret = uclass_get_device(UCLASS_EFI_MEDIA, dev_num, &media_dev);
+	if (ret) {
+		log_err("cannot find EFI media device %d\n", dev_num);
+		return log_msg_ret("eda", -ENODEV);
+	}
+	plat = dev_get_plat(media_dev);
+
+	log_debug("found EFI media device %d with firmware device path: %pD\n",
+		  dev_num, plat->device_path);
+
+	/* use the firmware device path and append partition */
+	part = simple_strtoul(ep + 1, NULL, 16);
+	if (part > 0) {
+		struct efi_device_path *part_dp;
+		struct disk_partition pinfo;
+
+		/* Get partition info */
+		part = blk_get_device_part_str("efi", devnr, &desc, &pinfo, 1);
+		if (part < 0 || !desc) {
+			log_err("cannot get partition info for '%s'\n", devnr);
+			return log_msg_ret("edb", part < 0 ? part : -ENODEV);
+		}
+
+		/* Create partition node */
+		part_dp = efi_dp_part_node(desc, part);
+		if (!part_dp)
+			return log_msg_ret("edn", -ENOMEM);
+
+		/* Combine firmware device path with partition */
+		dp = efi_dp_append_node(plat->device_path, part_dp);
+		efi_free_pool(part_dp);
+	} else {
+		/* Use whole device */
+		dp = efi_dp_dup(plat->device_path);
+	}
+	if (!dp)
+		return log_msg_ret("ede", -ENOMEM);
+
+	log_debug("created final device path: %pD\n", dp);
+	*descp = desc;
+	*partp = part;
+	*dpp = dp;
+
+	return 0;
+}
+
 /**
  * efi_dp_from_name() - convert U-Boot device and file path to device path
  *
@@ -1050,11 +1153,22 @@  efi_status_t efi_dp_from_name(const char *dev, const char *devnr,
 		efi_net_dp_from_dev(&dp, eth_get_dev(), false);
 	} else if (!strcmp(dev, "Uart")) {
 		dp = efi_dp_from_uart();
+	} else if (IS_ENABLED(CONFIG_EFI_APP) && !strcmp(dev, "efi")) {
+		int ret;
+
+		ret = efi_dp_from_efi_app(devnr, &desc, &part, &dp);
+		if (ret)
+			return EFI_INVALID_PARAMETER;
 	} else {
+		log_debug("calling blk_get_device_part_str dev='%s', devnr='%s'\n",
+			  dev, devnr);
 		part = blk_get_device_part_str(dev, devnr, &desc, &fs_partition,
 					       1);
-		if (part < 0 || !desc)
+		if (part < 0 || !desc) {
+			log_err("Failed to find fs: dev='%s', devnr='%s', part=%d, desc=%p\n",
+				dev, devnr, part, desc);
 			return EFI_INVALID_PARAMETER;
+		}
 
 		dp = efi_dp_from_part(desc, part);
 	}