gigaboot: Change how the ramdisk is handled.

This change pulls gigaboot buffer management into gigaboot.c, where it's easier
to access when the time comes to actually boot with them. It also consolidates
most of the code for managing the buffers.

Change-Id: I1c6dbcfa65a8723bcc60c1785340fa6f06c6aa54
diff --git a/src/arch/arm/fit.c b/src/arch/arm/fit.c
index 725e22c..8abc529 100644
--- a/src/arch/arm/fit.c
+++ b/src/arch/arm/fit.c
@@ -21,6 +21,7 @@
  */
 
 #include <stdint.h>
+#include <stdio.h>
 
 #include "arch/arm/boot.h"
 #include "base/cleanup.h"
@@ -29,8 +30,14 @@
 #include "boot/boot.h"
 #include "boot/fit.h"
 
-int boot(void *image, char *cmd_line, void *params, void *loader)
+int boot(void *image, char *cmd_line, void *params, void *loader,
+	 void *ram_disk, size_t ram_disk_size)
 {
+	if (ram_disk || ram_disk_size) {
+		printf("RAM disk support not implemented for FIT images.");
+		return 1;
+	}
+
 	DeviceTree *tree;
 	FitImageNode *kernel = fit_load(image, cmd_line, &tree);
 
diff --git a/src/arch/x86/zeropage.c b/src/arch/x86/zeropage.c
index 210a6b2..da3b589 100644
--- a/src/arch/x86/zeropage.c
+++ b/src/arch/x86/zeropage.c
@@ -40,7 +40,8 @@
 static const uint32_t KernelV2Magic = 0x53726448;
 static const uint16_t MinProtocol = 0x0202;
 
-static int prepare_zero_page(struct boot_params *boot_params)
+static int prepare_zero_page(struct boot_params *boot_params,
+			     void *ram_disk, size_t ram_disk_size)
 {
 	struct setup_header *hdr = &boot_params->hdr;
 
@@ -49,6 +50,9 @@
 		return 1;
 	}
 
+	hdr->ramdisk_image = (uintptr_t)ram_disk;
+	hdr->ramdisk_size = ram_disk_size;
+
 	E820MemRanges *e820 = get_e820_mem_ranges();
 	if (!e820)
 		return 1;
@@ -115,7 +119,8 @@
 }
 
 int boot_x86_linux(struct boot_params *boot_params, char *cmd_line,
-		   void *kernel, uintptr_t entry_offset)
+		   void *kernel, uintptr_t entry_offset,
+		   void *ram_disk, size_t ram_disk_size)
 {
 	// We'll move the boot_params structure and the command line to where
 	// Linux suggests and to where they'll be safe from being trampled by
@@ -124,14 +129,15 @@
 	assert((uint8_t *)CmdLineBuff - (uint8_t *)ParamsBuff >=
 		sizeof(*boot_params));
 
-	if (prepare_zero_page(boot_params))
+	if (prepare_zero_page(boot_params, ram_disk, ram_disk_size))
 		return 1;
 
 	place_and_start_kernel(boot_params, cmd_line, kernel, entry_offset);
 }
 
 int boot_x86_magenta(struct boot_params *boot_params, char *cmd_line,
-		     void *kernel, uintptr_t entry_offset)
+		     void *kernel, uintptr_t entry_offset,
+		     void *ram_disk, size_t ram_disk_size)
 {
 	// We'll move the boot_params structure and the command line to the
 	// same place Linux suggests and to where they'll be safe from being
@@ -140,7 +146,7 @@
 	assert((uint8_t *)CmdLineBuff - (uint8_t *)ParamsBuff >=
 		sizeof(*boot_params));
 
-	if (prepare_zero_page(boot_params))
+	if (prepare_zero_page(boot_params, ram_disk, ram_disk_size))
 		return 1;
 
 	/* Fill in the fields in the zero page magenta has co-opted. */
diff --git a/src/arch/x86/zeropage.h b/src/arch/x86/zeropage.h
index f669bf6..3353b42 100644
--- a/src/arch/x86/zeropage.h
+++ b/src/arch/x86/zeropage.h
@@ -26,9 +26,11 @@
 #include "arch/x86/boot/bootparam.h"
 
 int boot_x86_linux(struct boot_params *boot_base, char *cmd_line,
-		   void *kernel, uintptr_t entry_offset);
+		   void *kernel, uintptr_t entry_offset,
+		   void *ram_disk, size_t ram_disk_size);
 int boot_x86_magenta(struct boot_params *boot_base, char *cmd_line,
-		     void *kernel, uintptr_t entry_offset);
+		     void *kernel, uintptr_t entry_offset,
+		     void *ram_disk, size_t ram_disk_size);
 
 void boot_x86_zeropage_start_kernel(
 	struct boot_params *params, void *entry) __attribute__((noreturn));
diff --git a/src/arch/x86/zimage.c b/src/arch/x86/zimage.c
index 80a1a6c..c59e38e 100644
--- a/src/arch/x86/zimage.c
+++ b/src/arch/x86/zimage.c
@@ -27,7 +27,8 @@
 #include "arch/x86/zeropage.h"
 #include "boot/boot.h"
 
-int boot(void *kernel, char *cmd_line, void *params, void *loader)
+int boot(void *kernel, char *cmd_line, void *params, void *loader,
+	 void *ram_disk, size_t ram_disk_size)
 {
 	// If nobody's prepared the boot_params structure for us already,
 	// do that now.
@@ -56,10 +57,13 @@
 		memmove(kernel, (void *)pm_start, pm_size);
 	}
 
-	if (CONFIG_TARGET_OS_LINUX)
-		return boot_x86_linux(params, cmd_line, kernel, 0);
-	else if (CONFIG_TARGET_OS_MAGENTA)
-		return boot_x86_magenta(params, cmd_line, kernel, 0);
+	if (CONFIG_TARGET_OS_LINUX) {
+		return boot_x86_linux(params, cmd_line, kernel, 0,
+				      ram_disk, ram_disk_size);
+	} else if (CONFIG_TARGET_OS_MAGENTA) {
+		return boot_x86_magenta(params, cmd_line, kernel, 0,
+					ram_disk, ram_disk_size);
+	}
 
 	printf("Unsupported OS/format combination.\n");
 	return 1;
diff --git a/src/boot/boot.h b/src/boot/boot.h
index 76e1c20..a1703cc 100644
--- a/src/boot/boot.h
+++ b/src/boot/boot.h
@@ -23,7 +23,10 @@
 #ifndef __BOOT_BOOT_H__
 #define __BOOT_BOOT_H__
 
+#include <stddef.h>
+
 // To be implemented by each boot method.
-int boot(void *kernel, char *cmd_line, void *params, void *loader);
+int boot(void *kernel, char *cmd_line, void *params, void *loader,
+	 void *ram_disk, size_t ram_disk_size);
 
 #endif /* __BOOT_BOOT_H__ */
diff --git a/src/boot/dummy.c b/src/boot/dummy.c
index 487ac7a..818f671 100644
--- a/src/boot/dummy.c
+++ b/src/boot/dummy.c
@@ -22,7 +22,8 @@
 
 #include "boot/boot.h"
 
-int boot(void *kernel, void *cmd_line, void *params, void *loader)
+int boot(void *kernel, void *cmd_line, void *params, void *loader,
+	 void *ram_disk, size_t ram_disk_size)
 {
 	return 0;
 }
diff --git a/src/debug/netboot.c b/src/debug/netboot.c
index 8953919..a2b4619 100644
--- a/src/debug/netboot.c
+++ b/src/debug/netboot.c
@@ -30,26 +30,16 @@
  * the symbols from stubs.c. They must never be linked into production images!
  */
 
-GigabootBuffer *gigaboot_get_buffer(const char *name, size_t size)
+void *gigaboot_allocate_buffer(size_t *size)
 {
-	static GigabootBuffer kernel = {
-		.data = (void *)(uintptr_t)CONFIG_KERNEL_START,
-		.size = CONFIG_KERNEL_SIZE,
-	};
-
-	static char cmdline_buf[4096];
-	static GigabootBuffer cmdline = {
-		.data = cmdline_buf,
-		.size = sizeof(cmdline_buf),
-	};
-
-	if (!strcmp(name, gigaboot_buffer_kernel))
-		return &kernel;
-	if (!strcmp(name, gigaboot_buffer_cmdline))
-		return &cmdline;
 	return NULL;
 }
 
+int gigaboot_free_buffer(void *data, size_t size)
+{
+	return 1;
+}
+
 void dc_dev_netboot(void)
 {
 	if (CONFIG_DRIVER_CONSOLE_DISPLAY)
diff --git a/src/drivers/net/uefi.c b/src/drivers/net/uefi.c
index 29a449e..dc68000 100644
--- a/src/drivers/net/uefi.c
+++ b/src/drivers/net/uefi.c
@@ -92,8 +92,7 @@
 		EFI_SIMPLE_NETWORK_RECEIVE_PROMISCUOUS_MULTICAST;
 
 	if (mode->HwAddressSize != sizeof(dev->mac)) {
-		printf("Unexpected MAC size %d.\n",
-		       mode->HwAddressSize);
+		printf("Unexpected MAC size %d.\n", mode->HwAddressSize);
 		return 1;
 	}
 	memcpy(&dev->mac, &mode->CurrentAddress, sizeof(dev->mac));
diff --git a/src/module/netboot.c b/src/module/netboot.c
index 8599c86..c919a3f 100644
--- a/src/module/netboot.c
+++ b/src/module/netboot.c
@@ -39,26 +39,16 @@
 #include "net/netboot/params.h"
 #include "vboot/vbnv.h"
 
-GigabootBuffer *gigaboot_get_buffer(const char *name, size_t size)
+void *gigaboot_allocate_buffer(size_t *size)
 {
-	static GigabootBuffer kernel = {
-		.data = (void *)(uintptr_t)CONFIG_KERNEL_START,
-		.size = CONFIG_KERNEL_SIZE,
-	};
-
-	static char cmdline_buf[4096];
-	static GigabootBuffer cmdline = {
-		.data = cmdline_buf,
-		.size = sizeof(cmdline_buf),
-	};
-
-	if (!strcmp(name, gigaboot_buffer_kernel))
-		return &kernel;
-	if (!strcmp(name, gigaboot_buffer_cmdline))
-		return &cmdline;
 	return NULL;
 }
 
+int gigaboot_free_buffer(void *data, size_t size)
+{
+	return 1;
+}
+
 static void enable_graphics(void)
 {
 	if (CONFIG_DRIVER_CONSOLE_DISPLAY)
diff --git a/src/module/uefi/netboot.c b/src/module/uefi/netboot.c
index 52f6580..fb8a749 100644
--- a/src/module/uefi/netboot.c
+++ b/src/module/uefi/netboot.c
@@ -32,63 +32,47 @@
 #include "net/netboot/params.h"
 
 
-static GigabootBuffer *resize_gigaboot_buffer(GigabootBuffer *buf, size_t size)
+void *gigaboot_allocate_buffer(size_t *size)
 {
+	static const size_t PageSize = 4096;
+
 	EFI_SYSTEM_TABLE *st = uefi_system_table_ptr();
 	if (!st)
 		return NULL;
 	EFI_BOOT_SERVICES *bs = st->BootServices;
 
-	if (buf->size < size) {
-		EFI_PHYSICAL_ADDRESS data = (EFI_PHYSICAL_ADDRESS)(buf->data);
-		if (bs->FreePages(data, buf->size / 4096)) {
-			printf("Could not free previous gigaboot buffer.\n");
-			buf->size = 0;
-			return NULL;
-		}
-		buf->size = 0;
+	*size = (*size + PageSize - 1) & ~(PageSize - 1);
 
-		data = 0xffffffff;
-		if (bs->AllocatePages(AllocateMaxAddress, EfiLoaderData,
-				      size / 4096, &data)) {
-			printf("Failed to allocate gigaboot buffer.\n");
-			return NULL;
-		}
-		buf->data = (void *)data;
-		buf->size = size;
+	EFI_PHYSICAL_ADDRESS data = (EFI_PHYSICAL_ADDRESS)0xffffffff;
+	if (bs->AllocatePages(AllocateMaxAddress, EfiLoaderData,
+			      *size / PageSize, &data)) {
+		printf("Failed to allocate gigaboot buffer of size %d.\n",
+			(int)(*size / PageSize));
+		halt();
+		return NULL;
 	}
-	return buf;
+
+	return (void *)(uintptr_t)data;
 }
 
-GigabootBuffer *gigaboot_get_buffer(const char *name, size_t size)
+int gigaboot_free_buffer(void *data, size_t size)
 {
-	static GigabootBuffer kernel = {
-		.data = (void *)(uintptr_t)CONFIG_KERNEL_START,
-		.size = CONFIG_KERNEL_SIZE,
-	};
+	if (!data)
+		return 0;
 
-	static char cmdline_buf[4096];
-	static GigabootBuffer cmdline = {
-		.data = cmdline_buf,
-		.size = sizeof(cmdline_buf),
-	};
+	static const size_t PageSize = 4096;
 
-	static GigabootBuffer ramdisk;
+	EFI_SYSTEM_TABLE *st = uefi_system_table_ptr();
+	if (!st)
+		return 1;
+	EFI_BOOT_SERVICES *bs = st->BootServices;
 
-	if (!strcmp(name, gigaboot_buffer_kernel))
-		return &kernel;
-	if (!strcmp(name, gigaboot_buffer_cmdline))
-		return &cmdline;
-	if (!strcmp(name, gigaboot_buffer_ramdisk)) {
-		if (ramdisk.size != 0 && size == 0)
-			return &ramdisk;
-
-		size_t buf_size =
-			size > 0 ? (size + 4095) & ~4095 : 512 * 1024 * 1024;
-
-		return resize_gigaboot_buffer(&ramdisk, buf_size);
+	EFI_PHYSICAL_ADDRESS addr = (EFI_PHYSICAL_ADDRESS)(uintptr_t)data;
+	if (bs->FreePages(addr, size / PageSize)) {
+		printf("Could not free previous gigaboot buffer.\n");
+		return 1;
 	}
-	return NULL;
+	return 0;
 }
 
 void module_main(void)
diff --git a/src/net/gigaboot/gigaboot.c b/src/net/gigaboot/gigaboot.c
index af04246..22f4161 100644
--- a/src/net/gigaboot/gigaboot.c
+++ b/src/net/gigaboot/gigaboot.c
@@ -63,10 +63,6 @@
 	GigabootCommandBoot = 4,
 };
 
-const char gigaboot_buffer_kernel[] = "kernel.bin";
-const char gigaboot_buffer_cmdline[] = "cmdline";
-const char gigaboot_buffer_ramdisk[] = "ramdisk.bin";
-
 typedef struct {
 	uint32_t magic;
 	uint32_t cookie;
@@ -79,6 +75,27 @@
 	uint8_t data[0];
 } GigabootMessage;
 
+
+typedef struct {
+	void *data;
+	size_t size; // Max size of the buffer.
+	size_t offset; // Write pointer.
+} GigabootBuffer;
+
+static GigabootBuffer gigaboot_kernel = {
+	.data = (void *)(uintptr_t)CONFIG_KERNEL_START,
+	.size = CONFIG_KERNEL_SIZE,
+};
+
+static char gigaboot_cmdline_buf[4096];
+static GigabootBuffer gigaboot_cmdline = {
+	.data = gigaboot_cmdline_buf,
+	.size = sizeof(gigaboot_cmdline_buf),
+};
+
+static GigabootBuffer gigaboot_ramdisk;
+
+
 typedef struct {
 	NetConOps *con;
 
@@ -97,6 +114,36 @@
  * Receive messages and act on commands.
  */
 
+static GigabootBuffer *gigaboot_prepare_buffer(const char *name, size_t size)
+{
+	if (!strcmp(name, "kernel.bin"))
+		return &gigaboot_kernel;
+	if (!strcmp(name, "cmdline"))
+		return &gigaboot_cmdline;
+	if (!strcmp(name, "ramdisk.bin")) {
+		if (gigaboot_ramdisk.size > size)
+			return &gigaboot_ramdisk;
+
+		if (gigaboot_free_buffer(gigaboot_ramdisk.data,
+					 gigaboot_ramdisk.size)) {
+			return NULL;
+		}
+		gigaboot_ramdisk.data = NULL;
+		gigaboot_ramdisk.size = 0;
+
+		if (!size)
+			size = 512 * 1024 * 1024;
+		void *data = gigaboot_allocate_buffer(&size);
+		if (!data)
+			return NULL;
+		gigaboot_ramdisk.data = data;
+		gigaboot_ramdisk.size = size;
+
+		return &gigaboot_ramdisk;
+	}
+	return NULL;
+}
+
 static void gigaboot_recv_message(GigabootState *state, GigabootMessage *msg,
 				  size_t size)
 {
@@ -130,8 +177,8 @@
 			if (msg->data[i] < ' ' || msg->data[i] > 127)
 				msg->data[i] = '.';
 		}
-		state->buffer = gigaboot_get_buffer((const char *)msg->data,
-						    msg->hdr.arg);
+		state->buffer = gigaboot_prepare_buffer(
+			(const char *)msg->data, msg->hdr.arg);
 		if (state->buffer) {
 			state->buffer->offset = 0;
 			state->ack.arg = msg->hdr.arg;
@@ -164,18 +211,15 @@
 	{
 		printf("netboot: Boot Kernel...\n");
 
-		GigabootBuffer *kernel =
-			gigaboot_get_buffer(gigaboot_buffer_kernel, 0);
-		GigabootBuffer *cmdline =
-			gigaboot_get_buffer(gigaboot_buffer_cmdline, 0);
-		if (!kernel || !cmdline) {
+		if (!gigaboot_kernel.offset) {
 			state->ack.command = GigabootErrorBadParam;
 			break;
 		}
 
 		// Send an ack here since we're (probably) not coming back.
 		netcon_send(state->con, &state->ack, sizeof(state->ack));
-		boot(kernel->data, cmdline->data, NULL, NULL);
+		boot(gigaboot_kernel.data, gigaboot_cmdline.data, NULL,
+		     NULL, gigaboot_ramdisk.data, gigaboot_ramdisk.offset);
 		return;
 	}
 
@@ -230,6 +274,10 @@
 	state.con = con;
 	state.ack.magic = GigabootMagic;
 
+	gigaboot_kernel.offset = 0;
+	gigaboot_cmdline.offset = 0;
+	gigaboot_ramdisk.offset = 0;
+
 	gigaboot_advertise(&state);
 
 	uint64_t start = time_us(0);
diff --git a/src/net/gigaboot/gigaboot.h b/src/net/gigaboot/gigaboot.h
index 3f35f03..e8a530e 100644
--- a/src/net/gigaboot/gigaboot.h
+++ b/src/net/gigaboot/gigaboot.h
@@ -28,17 +28,8 @@
 // Call to trigger the gigaboot protocol.
 void gigaboot(void);
 
-typedef struct {
-	void *data;
-	size_t size; // Max size of the buffer.
-	size_t offset; // Write pointer.
-} GigabootBuffer;
-
-extern const char gigaboot_buffer_kernel[];
-extern const char gigaboot_buffer_cmdline[];
-extern const char gigaboot_buffer_ramdisk[];
-
 // Implement to give gigaboot a place to put the data it downloads.
-GigabootBuffer *gigaboot_get_buffer(const char *name, size_t size);
+void *gigaboot_allocate_buffer(size_t *size);
+int gigaboot_free_buffer(void *data, size_t size);
 
 #endif /* __NET_GIGABOOT_GIGABOOT_H__ */
diff --git a/src/net/netboot/netboot.c b/src/net/netboot/netboot.c
index abc2d17..3726a5f 100644
--- a/src/net/netboot/netboot.c
+++ b/src/net/netboot/netboot.c
@@ -148,7 +148,7 @@
 	printf("The command line is: %s\n", cmd_line);
 
 	// Boot.
-	boot(payload, cmd_line, NULL, NULL);
+	boot(payload, cmd_line, NULL, NULL, NULL, 0);
 }
 
 void netboot(char *args, size_t args_size)
diff --git a/src/vboot/stages.c b/src/vboot/stages.c
index 8b940f3..9e3fa7b 100644
--- a/src/vboot/stages.c
+++ b/src/vboot/stages.c
@@ -244,7 +244,7 @@
 	if (crossystem_setup())
 		return 1;
 
-	boot(kernel, cmd_line_buf, params, loader);
+	boot(kernel, cmd_line_buf, params, loader, NULL, 0);
 
 	return 1;
 }