[efi] Expose eMMC boot partitions to GBL

Register the "bootloader" raw partition on eMMC hardware partition 1
(boot0) using GBL_VENDOR_MEDIA_DEVICE_PATH_GUID, and mirror block writes
to hardware partition 2 (boot1) starting at block offset 1.

This allows GBL fastboot to query `partition-size:bootloader` and flash
or stream-flash `bootloader` directly via `EFI_BLOCK_IO_PROTOCOL` when
running `fx flash` or `ffx target flash` on VIM3.

TAG=agy
CONV=d6901459-7b8c-4e0a-8447-092869375046
Bug: 496295142
Bug: 565499464
Test: Verified fx flash and ffx target flash on VIM3

Change-Id: I34f454143a467bbd83911effbd53a508522c1ff1
Reviewed-on: https://turquoise-internal-review.googlesource.com/c/third_party/u-boot/+/1678534
Reviewed-by: David Pursell <dpursell@google.com>
Commit-Queue: Sergii Parubochyi <sergiip@google.com>
GitOrigin-RevId: 615c25aa87d6ef89ecf5a60b1af5436d70778bfb
diff --git a/lib/efi_loader/efi_aml_sd_emmc.c b/lib/efi_loader/efi_aml_sd_emmc.c
index 413416f..a1c9147 100644
--- a/lib/efi_loader/efi_aml_sd_emmc.c
+++ b/lib/efi_loader/efi_aml_sd_emmc.c
@@ -11,6 +11,26 @@
 
 #define VENDOR_PATH_DATA_LEN 16
 
+// GBL vendor-defined media device path GUID used to expose an EFI block device
+// as a raw partition to GBL instead of a GPT disk.
+// See:
+// https://android.googlesource.com/platform/bootable/libbootloader/+/refs/heads/main/gbl/docs/partitions.md
+#define GBL_VENDOR_MEDIA_DEVICE_PATH_GUID \
+	EFI_GUID(0xa09773e3, 0xf027, 0x4f33, 0xad, 0xb3, 0xbd, 0x8d, 0xcf, \
+		 0x4b, 0x38, 0x54)
+
+// eMMC hardware partition indices used by `mmc_select_hwpart()`.
+// See `amlmmc_write_bootloader()` in `common/cmd_aml_mmc.c`.
+#define MMC_HWPART_USER  0
+#define MMC_HWPART_BOOT0 1
+#define MMC_HWPART_BOOT1 2
+
+// On Amlogic GXL and newer SoCs (including G12B / VIM3), block 0 of the eMMC
+// boot partitions (`boot0` and `boot1`) is reserved for `storage_emmc_boot_info`
+// metadata, and the bootloader image starts at block 1 (`GXL_START_BLK`).
+// See `amlmmc_write_bootloader()` in `common/cmd_aml_mmc.c`.
+#define GXL_START_BLK 1
+
 // Represents the EFI device path for a hardware partition on a Amlogic SD
 // eMMC.
 struct efi_aml_sd_disk_hwpart_device_path {
@@ -27,6 +47,10 @@
 	bool is_write;
 	efi_block_io2_token_t *token;
 	bool io_started;
+	// For writes to the virtual "bootloader" device, tracks whether the
+	// first write pass to `boot0` (MMC_HWPART_BOOT0) has finished and we
+	// are now mirroring the write to `boot1` (MMC_HWPART_BOOT1).
+	bool write_boot1;
 };
 
 // Represents a hardware partition on an Amlogic SD eMMC device.
@@ -42,12 +66,19 @@
 };
 
 // Represents an Amlogic SD eMMC device.
+//
+// Exposes 2 EFI block devices to GBL:
+//   - `hwparts[0]` ("hwpart-0"): eMMC `user` hardware partition (GPT disk).
+//   - `hwparts[1]` ("bootloader"): virtual GBL raw partition that reads from
+//     `boot0` (MMC_HWPART_BOOT0) and mirrors writes to both `boot0`
+//     (MMC_HWPART_BOOT0) and `boot1` (MMC_HWPART_BOOT1) starting at
+//     `GXL_START_BLK`.
 struct efi_aml_sd_disk {
 	struct mmc *mmc;
 	int dev;
 	efi_block_io2_token_t *io2_token;
 	struct efi_event *timer_event;
-	struct efi_aml_sd_disk_hwpart hwparts[3];
+	struct efi_aml_sd_disk_hwpart hwparts[2];
 	int curr_hwpart; // current hardware partition to process.
 	efi_status_t status;
 };
@@ -109,12 +140,23 @@
 			return;
 		}
 		// Initiates the read/write if it has not started.
-		mmc_select_hwpart(disk->dev, disk->curr_hwpart);
+		//
+		// `curr_hwpart == 0` accesses the `user` partition (hwpart 0).
+		// `curr_hwpart == 1` is the virtual "bootloader" partition:
+		// reads and the first write pass target `boot0` (hwpart 1), and
+		// the second write pass (`write_boot1`) mirrors to `boot1`
+		// (hwpart 2). Both boot partitions skip block 0 (`GXL_START_BLK`).
+		int hwpart = (disk->curr_hwpart == 0) ? MMC_HWPART_USER :
+			     (request->write_boot1 ? MMC_HWPART_BOOT1 :
+						     MMC_HWPART_BOOT0);
+		uint64_t blk_off = request->blk_off +
+				   (disk->curr_hwpart == 0 ? 0 : GXL_START_BLK);
+		mmc_select_hwpart(disk->dev, hwpart);
 		if (request->is_write ?
-			    aml_sd_write_async(disk->mmc, request->blk_off,
+			    aml_sd_write_async(disk->mmc, blk_off,
 					       request->blk_cnt,
 					       request->buffer) :
-			    aml_sd_read_async(disk->mmc, request->blk_off,
+			    aml_sd_read_async(disk->mmc, blk_off,
 					      request->blk_cnt,
 					      request->buffer)) {
 			handle_driver_error(disk, EFI_DEVICE_ERROR);
@@ -126,6 +168,19 @@
 		if (aml_sd_poll_io(disk->mmc)) {
 			handle_driver_error(disk, EFI_DEVICE_ERROR);
 		} else if (!aml_sd_is_busy(disk->mmc)) {
+			// When writing to the virtual "bootloader" device
+			// (`curr_hwpart == 1`), after the write to `boot0`
+			// finishes, repeat the write to `boot1` so both eMMC
+			// boot partitions stay in sync (matching
+			// `amlmmc_write_bootloader` in `common/cmd_aml_mmc.c`).
+			if (disk->curr_hwpart == 1 && request->is_write &&
+			    !request->write_boot1) {
+				request->write_boot1 = true;
+				request->io_started = false;
+				return;
+			}
+			if (disk->curr_hwpart != MMC_HWPART_USER)
+				mmc_select_hwpart(disk->dev, MMC_HWPART_USER);
 			efi_io2_finish(request, EFI_SUCCESS);
 			// Sets to the next hardware partition to poll.
 			// This ensures fairness and prevents one busy block device from
@@ -335,7 +390,16 @@
 	disk->mmc = mmc;
 	disk->dev = CONFIG_FASTBOOT_FLASH_MMC_DEV;
 
-	// Creates a block device for boot0/boot1/user hardware partitions.
+	// Creates two EFI block devices:
+	//   - hwpart 0 ("hwpart-0"): the eMMC `user` hardware partition, which
+	//     contains the GPT. Left without `GBL_VENDOR_MEDIA_DEVICE_PATH_GUID`
+	//     so GBL treats it as a GPT disk.
+	//   - hwpart 1 ("bootloader"): a virtual raw partition tagged with
+	//     `GBL_VENDOR_MEDIA_DEVICE_PATH_GUID` so GBL exposes the entire
+	//     device as the "bootloader" fastboot partition. Reads access
+	//     `boot0` (MMC_HWPART_BOOT0) and writes mirror to both `boot0`
+	//     (MMC_HWPART_BOOT0) and `boot1` (MMC_HWPART_BOOT1) in
+	//     `efi_disk_poll()`.
 	for (size_t hwpart = 0; hwpart < ARRAY_SIZE(disk->hwparts); hwpart++) {
 		struct efi_aml_sd_disk_hwpart *disk_hwpart =
 			&disk->hwparts[hwpart];
@@ -349,8 +413,15 @@
 			DEVICE_PATH_SUB_TYPE_VENDOR_PATH;
 		disk_hwpart->dp.path.dp.length =
 			sizeof(disk_hwpart->dp.path) + VENDOR_PATH_DATA_LEN;
-		snprintf(disk_hwpart->dp.vendor_defined_data,
-			 VENDOR_PATH_DATA_LEN, "hwpart-%ld", hwpart);
+		if (hwpart == 0) {
+			snprintf(disk_hwpart->dp.vendor_defined_data,
+				 VENDOR_PATH_DATA_LEN, "hwpart-%ld", hwpart);
+		} else {
+			disk_hwpart->dp.path.guid =
+				(efi_guid_t)GBL_VENDOR_MEDIA_DEVICE_PATH_GUID;
+			snprintf(disk_hwpart->dp.vendor_defined_data,
+				 VENDOR_PATH_DATA_LEN, "bootloader");
+		}
 		disk_hwpart->dp.end.type = DEVICE_PATH_TYPE_END;
 		disk_hwpart->dp.end.sub_type = DEVICE_PATH_SUB_TYPE_END;
 		// Populates `EFI_BLOCK_IO_MEDIA`
@@ -364,7 +435,9 @@
 		disk_hwpart->media.io_align = mmc->write_bl_len;
 		uint64_t total_blocks =
 			(hwpart == 0 ? mmc->capacity_user : mmc->capacity_boot);
-		total_blocks /= mmc->write_bl_len;
+		// Reserve block 0 (`GXL_START_BLK`) on the boot partitions.
+		total_blocks = total_blocks / mmc->write_bl_len -
+			       (hwpart == 0 ? 0 : GXL_START_BLK);
 		disk_hwpart->media.last_block = max(1ULL, total_blocks) - 1;
 		// Populates `EFI_BLOCK_IO2_PROTOCOL`.
 		disk_hwpart->ops_io2.media = &disk_hwpart->media;