8139 loader: efi multiboot2 update
Reviewed by: Robert Mustacchi <rm@joyent.com>
Approved by: Richard Lowe <richlowe@richlowe.net>
diff --git a/usr/src/boot/sys/boot/efi/loader/main.c b/usr/src/boot/sys/boot/efi/loader/main.c
index 269f7df..472ce86 100644
--- a/usr/src/boot/sys/boot/efi/loader/main.c
+++ b/usr/src/boot/sys/boot/efi/loader/main.c
@@ -65,6 +65,7 @@
 
 extern void acpi_detect(void);
 void efi_serial_init(void);
+extern void efi_getsmap(void);
 #ifdef EFI_ZFS_BOOT
 static void efi_zfs_probe(void);
 #endif
@@ -209,6 +210,8 @@
 	archsw.arch_copyin = efi_copyin;
 	archsw.arch_copyout = efi_copyout;
 	archsw.arch_readin = efi_readin;
+	archsw.arch_loadaddr = efi_loadaddr;
+	archsw.arch_free_loadaddr = efi_free_loadaddr;
 #ifdef EFI_ZFS_BOOT
 	/* Note this needs to be set before ZFS init. */
 	archsw.arch_zfs_probe = efi_zfs_probe;
@@ -226,6 +229,7 @@
 	 * printf() etc. once this is done.
 	 */
 	cons_probe();
+	efi_getsmap();
 
 	/*
 	 * Initialise the block cache. Set the upper limit.
@@ -329,11 +333,6 @@
 		setenv("console", "ttya" , 1);
 	}
 
-	if (efi_copy_init()) {
-		printf("failed to allocate staging area\n");
-		return (EFI_BUFFER_TOO_SMALL);
-	}
-
 	/*
 	 * March through the device switch probing for things.
 	 */
@@ -796,6 +795,104 @@
 COMMAND_SET(fdt, "fdt", "flattened device tree handling", command_fdt);
 #endif
 
+/*
+ * Chain load another efi loader.
+ */
+static int
+command_chain(int argc, char *argv[])
+{
+	EFI_GUID LoadedImageGUID = LOADED_IMAGE_PROTOCOL;
+	EFI_HANDLE loaderhandle;
+	EFI_LOADED_IMAGE *loaded_image;
+	EFI_STATUS status;
+	struct stat st;
+	struct devdesc *dev;
+	char *name, *path;
+	void *buf;
+	int fd;
+
+	if (argc < 2) {
+		command_errmsg = "wrong number of arguments";
+		return (CMD_ERROR);
+	}
+
+	name = argv[1];
+
+	if ((fd = open(name, O_RDONLY)) < 0) {
+		command_errmsg = "no such file";
+		return (CMD_ERROR);
+	}
+
+	if (fstat(fd, &st) < -1) {
+		command_errmsg = "stat failed";
+		close(fd);
+		return (CMD_ERROR);
+	}
+
+	status = BS->AllocatePool(EfiLoaderCode, (UINTN)st.st_size, &buf);
+	if (status != EFI_SUCCESS) {
+		command_errmsg = "failed to allocate buffer";
+		close(fd);
+		return (CMD_ERROR);
+	}
+	if (read(fd, buf, st.st_size) != st.st_size) {
+		command_errmsg = "error while reading the file";
+		(void)BS->FreePool(buf);
+		close(fd);
+		return (CMD_ERROR);
+	}
+	close(fd);
+	status = BS->LoadImage(FALSE, IH, NULL, buf, st.st_size, &loaderhandle);
+	(void)BS->FreePool(buf);
+	if (status != EFI_SUCCESS) {
+		command_errmsg = "LoadImage failed";
+		return (CMD_ERROR);
+	}
+	status = BS->HandleProtocol(loaderhandle, &LoadedImageGUID,
+	    (void **)&loaded_image);
+
+	if (argc > 2) {
+		int i, len = 0;
+		CHAR16 *argp;
+
+		for (i = 2; i < argc; i++)
+			len += strlen(argv[i]) + 1;
+
+		len *= sizeof (*argp);
+		loaded_image->LoadOptions = argp = malloc (len);
+		if (loaded_image->LoadOptions == NULL) {
+			(void) BS->UnloadImage(loaded_image);
+			return (CMD_ERROR);
+		}
+		loaded_image->LoadOptionsSize = len;
+		for (i = 2; i < argc; i++) {
+			char *ptr = argv[i];
+			while (*ptr)
+				*(argp++) = *(ptr++);
+			*(argp++) = ' ';
+		}
+		*(--argv) = 0;
+	}
+
+	if (efi_getdev((void **)&dev, name, (const char **)&path) == 0)
+		loaded_image->DeviceHandle =
+		    efi_find_handle(dev->d_dev, dev->d_unit);
+
+	dev_cleanup();
+	status = BS->StartImage(loaderhandle, NULL, NULL);
+	if (status != EFI_SUCCESS) {
+		command_errmsg = "StartImage failed";
+		free(loaded_image->LoadOptions);
+		loaded_image->LoadOptions = NULL;
+		status = BS->UnloadImage(loaded_image);
+		return (CMD_ERROR);
+	}
+
+	return (CMD_ERROR);	/* not reached */
+}
+
+COMMAND_SET(chain, "chain", "chain load file", command_chain);
+
 #ifdef EFI_ZFS_BOOT
 static void
 efi_zfs_probe(void)