Update U-boot to v2017.01 with the merge history

Bug: 34811400
Test: Booted a rpi3 with the new version
Change-Id: Ib7bf20ef73c8dd9153d12c157dcb1825f190827c
diff --git a/MODULE_LICENSE_GPL b/MODULE_LICENSE_GPL
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/MODULE_LICENSE_GPL
diff --git a/NOTICE b/NOTICE
new file mode 120000
index 0000000..fe4033d
--- /dev/null
+++ b/NOTICE
@@ -0,0 +1 @@
+Licenses/README
\ No newline at end of file
diff --git a/README b/README
index 7e0dd35..51cfd57 100644
--- a/README
+++ b/README
@@ -1001,7 +1001,7 @@
 		tree is available in the global data as gd->fdt_blob.
 
 		U-Boot needs to get its device tree from somewhere. This can
-		be done using one of the two options below:
+		be done using one of the three options below:
 
 		CONFIG_OF_EMBED
 		If this variable is defined, U-Boot will embed a device tree
@@ -1022,6 +1022,12 @@
 		still use the individual files if you need something more
 		exotic.
 
+		CONFIG_OF_BOARD
+		If this variable is defined, U-Boot will use the device tree
+		provided by the board at runtime instead of embedding one with
+		the image. Only boards defining board_fdt_blob_setup() support
+		this option (see include/fdtdec.h file).
+
 - Watchdog:
 		CONFIG_WATCHDOG
 		If this variable is defined, it enables watchdog
diff --git a/README.version b/README.version
new file mode 100644
index 0000000..b737c0c
--- /dev/null
+++ b/README.version
@@ -0,0 +1,9 @@
+URL: ftp://ftp.denx.de/pub/u-boot/u-boot-2017.01.tar.bz2
+Version: 2017.01
+License: GPLv2
+License File: Licenses/README
+BugComponent: 164615
+Owners: deymo, leecam
+
+Description:
+Das U-Boot is an open-source bootloader widely used in embedded devices.
diff --git a/board/raspberrypi/rpi/rpi.c b/board/raspberrypi/rpi/rpi.c
index 22e87a2..532ea99 100644
--- a/board/raspberrypi/rpi/rpi.c
+++ b/board/raspberrypi/rpi/rpi.c
@@ -510,6 +510,16 @@
 				  msg_clk->get_clock_rate.body.resp.rate_hz);
 }
 
+/*
+ *  * If the firmware passed a device tree use for U-Boot.
+ *   */
+void *board_fdt_blob_setup(void)
+{
+    if (fdt_magic(fw_dtb_pointer) != FDT_MAGIC)
+          return NULL;
+      return (void *)fw_dtb_pointer;
+}
+
 int ft_board_setup(void *blob, bd_t *bd)
 {
 	/*
diff --git a/cmd/Kconfig b/cmd/Kconfig
index 91bd3fb..f7dd159 100644
--- a/cmd/Kconfig
+++ b/cmd/Kconfig
@@ -385,6 +385,15 @@
 	help
 	  Load an S-Record file over serial line
 
+config CMD_LOAD_ANDROID
+	bool "load_android"
+	default n
+	depends on ANDROID_BOOT_IMAGE
+	help
+	  Load an Android Boot image from storage. The Android Boot images
+	  define the size and kernel address on the header, which are used by
+	  this command.
+
 config CMD_FLASH
 	bool "flinfo, erase, protect"
 	default y
diff --git a/cmd/Makefile b/cmd/Makefile
index 34bc544..8af239e 100644
--- a/cmd/Makefile
+++ b/cmd/Makefile
@@ -81,6 +81,7 @@
 obj-$(CONFIG_CMD_LED) += led.o
 obj-$(CONFIG_CMD_LICENSE) += license.o
 obj-y += load.o
+obj-$(CONFIG_CMD_LOAD_ANDROID) += load_android.o
 obj-$(CONFIG_LOGBUFFER) += log.o
 obj-$(CONFIG_ID_EEPROM) += mac.o
 obj-$(CONFIG_CMD_MD5SUM) += md5sum.o
diff --git a/cmd/fastboot.c b/cmd/fastboot.c
index 488822a..3e24fa0 100644
--- a/cmd/fastboot.c
+++ b/cmd/fastboot.c
@@ -11,18 +11,37 @@
 #include <command.h>
 #include <console.h>
 #include <g_dnl.h>
+#include <net.h>
 #include <usb.h>
 
 static int do_fastboot(cmd_tbl_t *cmdtp, int flag, int argc, char *const argv[])
 {
+#ifdef CONFIG_USB_FUNCTION_FASTBOOT
 	int controller_index;
 	char *usb_controller;
 	int ret;
+#endif
 
 	if (argc < 2)
 		return CMD_RET_USAGE;
 
-	usb_controller = argv[1];
+	if (!strcmp(argv[1], "udp")) {
+#ifndef CONFIG_UDP_FUNCTION_FASTBOOT
+		error("Fastboot UDP not enabled\n");
+		return -1;
+#else
+		return do_fastboot_udp(cmdtp, flag, argc, argv);
+#endif
+	}
+
+	if (strcmp(argv[1], "usb") || argc < 3)
+		return CMD_RET_USAGE;
+
+#ifndef CONFIG_USB_FUNCTION_FASTBOOT
+	error("Fastboot USB not enabled\n");
+	return -1;
+#else
+	usb_controller = argv[2];
 	controller_index = simple_strtoul(usb_controller, NULL, 0);
 
 	ret = board_usb_init(controller_index, USB_INIT_DEVICE);
@@ -59,11 +78,14 @@
 	board_usb_cleanup(controller_index, USB_INIT_DEVICE);
 
 	return ret;
+#endif
 }
 
 U_BOOT_CMD(
-	fastboot, 2, 1, do_fastboot,
-	"use USB Fastboot protocol",
-	"<USB_controller>\n"
-	"    - run as a fastboot usb device"
+	fastboot, 3, 1, do_fastboot,
+	"use USB or UDP Fastboot protocol",
+	"[usb,udp] <USB_controller>\n"
+	" - run as a fastboot usb or udp device\n"
+	"   usb: specify <USB_controller>\n"
+	"   udp: requires ip_addr set and ethernet initialized\n"
 );
diff --git a/cmd/fastboot/Kconfig b/cmd/fastboot/Kconfig
index 89b9e73..83a3eda 100644
--- a/cmd/fastboot/Kconfig
+++ b/cmd/fastboot/Kconfig
@@ -10,13 +10,21 @@
 	help
 	  This enables the USB part of the fastboot gadget.
 
+config UDP_FUNCTION_FASTBOOT
+	select NET
+	bool "Enable fastboot protocol over UDP"
+	help
+	  This enables the fastboot protocol over UDP.
+
 config CMD_FASTBOOT
 	bool "Enable FASTBOOT command"
+	depends on USB_FUNCTION_FASTBOOT || UDP_FUNCTION_FASTBOOT
 	help
 	  This enables the command "fastboot" which enables the Android
-	  fastboot mode for the platform's USB device. Fastboot is a USB
-	  protocol for downloading images, flashing and device control
-	  used on Android devices.
+	  fastboot mode for the platform. Fastboot is a protocol for
+	  downloading images, flashing and device control used on
+	  Android devices. Fastboot requires either network stack
+	  enabled or support for acting as a USB device.
 
 config ANDROID_BOOT_IMAGE
 	bool "Enable support for Android Boot Images"
@@ -24,7 +32,7 @@
 	  This enables support for booting images which use the Android
 	  image format header.
 
-if USB_FUNCTION_FASTBOOT
+if USB_FUNCTION_FASTBOOT || UDP_FUNCTION_FASTBOOT
 
 config FASTBOOT_BUF_ADDR
 	hex "Define FASTBOOT buffer address"
@@ -86,6 +94,6 @@
 	  specified on the "fastboot flash" command line matches the value
 	  defined here. The default target name for updating MBR is "mbr".
 
-endif # USB_FUNCTION_FASTBOOT
+endif # USB_FUNCTION_FASTBOOT || UDP_FUNCTION_FASTBOOT
 
 endif # FASTBOOT
diff --git a/cmd/load_android.c b/cmd/load_android.c
new file mode 100644
index 0000000..fb804c9
--- /dev/null
+++ b/cmd/load_android.c
@@ -0,0 +1,85 @@
+/*
+  * Copyright (C) 2008 The Android Open Source Project
+ *
+ * SPDX-License-Identifier: BSD-2-Clause
+ */
+
+#include <common.h>
+#include <command.h>
+#include <mapmem.h>
+
+static int do_load_android(cmd_tbl_t *cmdtp, int flag, int argc,
+			   char * const argv[])
+{
+	int boot_partition;
+	unsigned long load_address, blk_cnt, blk_read;
+	int ret = CMD_RET_SUCCESS;
+	char *addr_arg_endp, *addr_str;
+	void *buf;
+	struct blk_desc *dev_desc;
+	disk_partition_t part_info;
+
+	if (argc < 2)
+		return CMD_RET_USAGE;
+	if (argc > 4)
+		return CMD_RET_USAGE;
+
+	if (argc >= 4) {
+		load_address = simple_strtoul(argv[3], &addr_arg_endp, 16);
+		if (addr_arg_endp == argv[3] || *addr_arg_endp != '\0')
+			return CMD_RET_USAGE;
+	} else {
+		addr_str = getenv("loadaddr");
+		if (addr_str != NULL)
+			load_address = simple_strtoul(addr_str, NULL, 16);
+		else
+			load_address = CONFIG_SYS_LOAD_ADDR;
+	}
+
+	boot_partition = blk_get_device_part_str(argv[1],
+	                                         (argc >= 3) ? argv[2] : NULL,
+	                                         &dev_desc, &part_info, 1);
+	if (boot_partition < 0)
+		return CMD_RET_FAILURE;
+
+	/* We don't know the size of the Android image before reading the header
+	 * so we don't limit the size of the mapped memory. */
+	buf = map_sysmem(load_address, 0 /* size */);
+
+	/* Read the Android header first and then read the rest. */
+	if (blk_dread(dev_desc, part_info.start, 1, buf) != 1) {
+		ret = CMD_RET_FAILURE;
+	}
+
+	if (ret == CMD_RET_SUCCESS && android_image_check_header(buf) != 0) {
+		printf("\n** Invalid Android Image header on %s %d:%d **\n",
+		       argv[1], dev_desc->devnum, boot_partition);
+		ret = CMD_RET_FAILURE;
+	}
+	if (ret == CMD_RET_SUCCESS) {
+		blk_cnt = (android_image_get_end(buf) - (ulong)buf +
+		           part_info.blksz - 1) / part_info.blksz;
+		printf("\nLoading Android Image (%lu blocks) to 0x%lx... ",
+		       blk_cnt, load_address);
+		blk_read = blk_dread(dev_desc, part_info.start, blk_cnt, buf);
+	}
+
+	unmap_sysmem(buf);
+	if (ret != CMD_RET_SUCCESS)
+		return ret;
+
+	printf("%lu blocks read: %s\n",
+	       blk_read, (blk_read == blk_cnt) ? "OK" : "ERROR");
+	return (blk_read == blk_cnt) ? CMD_RET_SUCCESS : CMD_RET_FAILURE;
+}
+
+
+#if defined(CONFIG_CMD_LOAD_ANDROID)
+U_BOOT_CMD(
+	load_android, 4, 0, do_load_android,
+	"load Android Boot image from storage.",
+	"<interface> [<dev[:part]> [<addr>]]\n"
+	"    - Load a binary Android Boot image from the partition 'part' on\n"
+	"      device type 'interface' instance 'dev' to address 'addr'."
+);
+#endif	/* CONFIG_CMD_LOAD_ANDROID */
diff --git a/cmd/net.c b/cmd/net.c
index df8b6c9..4714f5a 100644
--- a/cmd/net.c
+++ b/cmd/net.c
@@ -70,6 +70,12 @@
 );
 #endif
 
+#ifdef CONFIG_UDP_FUNCTION_FASTBOOT
+int do_fastboot_udp(cmd_tbl_t *cmdtp, int flag, int argc, char *const argv[])
+{
+	return netboot_common(FASTBOOT, cmdtp, argc, argv);
+}
+#endif
 
 #ifdef CONFIG_CMD_RARP
 int do_rarpb(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[])
diff --git a/common/Makefile b/common/Makefile
index ecc23e6..3719597 100644
--- a/common/Makefile
+++ b/common/Makefile
@@ -165,6 +165,10 @@
 endif
 endif
 
+ifneq ($(or $(CONFIG_USB_FUNCTION_FASTBOOT),$(CONFIG_UDP_FUNCTION_FASTBOOT)),)
+obj-y += fb_common.o
+endif
+
 ifdef CONFIG_CMD_EEPROM_LAYOUT
 obj-y += eeprom/eeprom_field.o eeprom/eeprom_layout.o
 endif
diff --git a/common/fb_common.c b/common/fb_common.c
new file mode 100644
index 0000000..d6e4198
--- /dev/null
+++ b/common/fb_common.c
@@ -0,0 +1,41 @@
+/*
+* Copyright (C) 2016 The Android Open Source Project
+*
+* SPDX-License-Identifier: BSD-2-Clause
+*/
+
+#include <common.h>
+#include <fastboot.h>
+#ifdef CONFIG_UDP_FUNCTION_FASTBOOT
+#include <net/fastboot.h>
+#endif
+
+void fastboot_fail(const char *reason, char *response)
+{
+	const char *fail_str = "FAIL";
+	strncpy(response, fail_str, FASTBOOT_RESPONSE_LEN);
+	strncat(response, reason, FASTBOOT_RESPONSE_LEN - strlen(fail_str) - 1);
+}
+
+void fastboot_okay(const char *reason, char *response)
+{
+	const char *okay_str = "OKAY";
+	strncpy(response, okay_str, FASTBOOT_RESPONSE_LEN);
+	strncat(response, reason, FASTBOOT_RESPONSE_LEN - strlen(okay_str) - 1);
+}
+
+void timed_send_info(ulong *start, const char *msg)
+{
+#ifdef CONFIG_UDP_FUNCTION_FASTBOOT
+	/* Initialize timer */
+	if (*start == 0) {
+		*start = get_timer(0);
+	}
+	ulong time = get_timer(*start);
+	/* Send INFO packet to host every 30 seconds */
+	if (time >= 30000) {
+		*start = get_timer(0);
+		fastboot_send_info(msg);
+	}
+#endif
+}
diff --git a/common/fb_mmc.c b/common/fb_mmc.c
index 81a3bd0..d6b5585 100644
--- a/common/fb_mmc.c
+++ b/common/fb_mmc.c
@@ -27,6 +27,9 @@
 #define CONFIG_FASTBOOT_MBR_NAME "mbr"
 #endif
 
+#define FASTBOOT_MAX_BLK_WRITE 16384
+static ulong timer;
+
 struct fb_mmc_sparse {
 	struct blk_desc	*dev_desc;
 };
@@ -53,13 +56,37 @@
 	return ret;
 }
 
+static lbaint_t fb_mmc_blk_write(struct blk_desc *block_dev, lbaint_t start,
+		lbaint_t blkcnt, const void *buffer)
+{
+	lbaint_t blk = start;
+	lbaint_t blks_written;
+	lbaint_t cur_blkcnt;
+	lbaint_t blks = 0;
+	int i;
+	for (i = 0; i < blkcnt; i += FASTBOOT_MAX_BLK_WRITE) {
+		cur_blkcnt = min((int)blkcnt-i, FASTBOOT_MAX_BLK_WRITE);
+		if (buffer != NULL) {
+			timed_send_info(&timer, "writing");
+			blks_written = blk_dwrite(block_dev, blk, cur_blkcnt,
+					buffer+(i*block_dev->blksz));
+		} else {
+			timed_send_info(&timer, "erasing");
+			blks_written = blk_derase(block_dev, blk, cur_blkcnt);
+		}
+		blk += blks_written;
+		blks += blks_written;
+	}
+	return blks;
+}
+
 static lbaint_t fb_mmc_sparse_write(struct sparse_storage *info,
 		lbaint_t blk, lbaint_t blkcnt, const void *buffer)
 {
 	struct fb_mmc_sparse *sparse = info->priv;
 	struct blk_desc *dev_desc = sparse->dev_desc;
 
-	return blk_dwrite(dev_desc, blk, blkcnt, buffer);
+	return fb_mmc_blk_write(dev_desc, blk, blkcnt, buffer);
 }
 
 static lbaint_t fb_mmc_sparse_reserve(struct sparse_storage *info,
@@ -70,7 +97,7 @@
 
 static void write_raw_image(struct blk_desc *dev_desc, disk_partition_t *info,
 		const char *part_name, void *buffer,
-		unsigned int download_bytes)
+		unsigned int download_bytes, char *response)
 {
 	lbaint_t blkcnt;
 	lbaint_t blks;
@@ -81,26 +108,26 @@
 
 	if (blkcnt > info->size) {
 		error("too large for partition: '%s'\n", part_name);
-		fastboot_fail("too large for partition");
+		fastboot_fail("too large for partition", response);
 		return;
 	}
 
 	puts("Flashing Raw Image\n");
 
-	blks = blk_dwrite(dev_desc, info->start, blkcnt, buffer);
+	blks = fb_mmc_blk_write(dev_desc, info->start, blkcnt, buffer);
 	if (blks != blkcnt) {
 		error("failed writing to device %d\n", dev_desc->devnum);
-		fastboot_fail("failed writing to device");
+		fastboot_fail("failed writing to device", response);
 		return;
 	}
 
 	printf("........ wrote " LBAFU " bytes to '%s'\n", blkcnt * info->blksz,
 	       part_name);
-	fastboot_okay("");
+	fastboot_okay("", response);
 }
 
 void fb_mmc_flash_write(const char *cmd, void *download_buffer,
-			unsigned int download_bytes)
+			unsigned int download_bytes, char *response)
 {
 	struct blk_desc *dev_desc;
 	disk_partition_t info;
@@ -108,7 +135,7 @@
 	dev_desc = blk_get_dev("mmc", CONFIG_FASTBOOT_FLASH_MMC_DEV);
 	if (!dev_desc || dev_desc->type == DEV_TYPE_UNKNOWN) {
 		error("invalid mmc device\n");
-		fastboot_fail("invalid mmc device");
+		fastboot_fail("invalid mmc device", response);
 		return;
 	}
 
@@ -119,16 +146,17 @@
 		if (is_valid_gpt_buf(dev_desc, download_buffer)) {
 			printf("%s: invalid GPT - refusing to write to flash\n",
 			       __func__);
-			fastboot_fail("invalid GPT partition");
+			fastboot_fail("invalid GPT partition", response);
 			return;
 		}
 		if (write_mbr_and_gpt_partitions(dev_desc, download_buffer)) {
 			printf("%s: writing GPT partitions failed\n", __func__);
-			fastboot_fail("writing GPT partitions failed");
+			fastboot_fail("writing GPT partitions failed",
+				      response);
 			return;
 		}
 		printf("........ success\n");
-		fastboot_okay("");
+		fastboot_okay("", response);
 		return;
 	}
 #endif
@@ -139,23 +167,23 @@
 		if (is_valid_dos_buf(download_buffer)) {
 			printf("%s: invalid MBR - refusing to write to flash\n",
 			       __func__);
-			fastboot_fail("invalid MBR partition");
+			fastboot_fail("invalid MBR partition", response);
 			return;
 		}
 		if (write_mbr_partition(dev_desc, download_buffer)) {
 			printf("%s: writing MBR partition failed\n", __func__);
-			fastboot_fail("writing MBR partition failed");
+			fastboot_fail("writing MBR partition failed", response);
 			return;
 		}
 		printf("........ success\n");
-		fastboot_okay("");
+		fastboot_okay("", response);
 		return;
 	}
 #endif
 
 	if (part_get_info_by_name_or_alias(dev_desc, cmd, &info)) {
 		error("cannot find partition: '%s'\n", cmd);
-		fastboot_fail("cannot find partition");
+		fastboot_fail("cannot find partition", response);
 		return;
 	}
 
@@ -176,14 +204,14 @@
 
 		sparse.priv = &sparse_priv;
 		write_sparse_image(&sparse, cmd, download_buffer,
-				   download_bytes);
+				   download_bytes, response);
 	} else {
 		write_raw_image(dev_desc, &info, cmd, download_buffer,
-				download_bytes);
+				download_bytes, response);
 	}
 }
 
-void fb_mmc_erase(const char *cmd)
+void fb_mmc_erase(const char *cmd, char *response)
 {
 	int ret;
 	struct blk_desc *dev_desc;
@@ -193,21 +221,21 @@
 
 	if (mmc == NULL) {
 		error("invalid mmc device");
-		fastboot_fail("invalid mmc device");
+		fastboot_fail("invalid mmc device", response);
 		return;
 	}
 
 	dev_desc = blk_get_dev("mmc", CONFIG_FASTBOOT_FLASH_MMC_DEV);
 	if (!dev_desc || dev_desc->type == DEV_TYPE_UNKNOWN) {
 		error("invalid mmc device");
-		fastboot_fail("invalid mmc device");
+		fastboot_fail("invalid mmc device", response);
 		return;
 	}
 
 	ret = part_get_info_by_name_or_alias(dev_desc, cmd, &info);
 	if (ret) {
 		error("cannot find partition: '%s'", cmd);
-		fastboot_fail("cannot find partition");
+		fastboot_fail("cannot find partition", response);
 		return;
 	}
 
@@ -223,14 +251,14 @@
 	printf("Erasing blocks " LBAFU " to " LBAFU " due to alignment\n",
 	       blks_start, blks_start + blks_size);
 
-	blks = blk_derase(dev_desc, blks_start, blks_size);
+	blks = fb_mmc_blk_write(dev_desc, blks_start, blks_size, NULL);
 	if (blks != blks_size) {
 		error("failed erasing from device %d", dev_desc->devnum);
-		fastboot_fail("failed erasing from device");
+		fastboot_fail("failed erasing from device", response);
 		return;
 	}
 
 	printf("........ erased " LBAFU " bytes from '%s'\n",
 	       blks_size * info.blksz, cmd);
-	fastboot_okay("");
+	fastboot_okay("", response);
 }
diff --git a/common/fb_nand.c b/common/fb_nand.c
index c8c79e9..be11ca7 100644
--- a/common/fb_nand.c
+++ b/common/fb_nand.c
@@ -32,7 +32,8 @@
 
 static int fb_nand_lookup(const char *partname,
 			  struct mtd_info **mtd,
-			  struct part_info **part)
+			  struct part_info **part
+			  char *response)
 {
 	struct mtd_device *dev;
 	int ret;
@@ -41,21 +42,21 @@
 	ret = mtdparts_init();
 	if (ret) {
 		error("Cannot initialize MTD partitions\n");
-		fastboot_fail("cannot init mtdparts");
+		fastboot_fail("cannot init mtdparts", response);
 		return ret;
 	}
 
 	ret = find_dev_and_part(partname, &dev, &pnum, part);
 	if (ret) {
 		error("cannot find partition: '%s'", partname);
-		fastboot_fail("cannot find partition");
+		fastboot_fail("cannot find partition", response);
 		return ret;
 	}
 
 	if (dev->id->type != MTD_DEV_TYPE_NAND) {
 		error("partition '%s' is not stored on a NAND device",
 		      partname);
-		fastboot_fail("not a NAND device");
+		fastboot_fail("not a NAND device", response);
 		return -EINVAL;
 	}
 
@@ -146,16 +147,16 @@
 }
 
 void fb_nand_flash_write(const char *cmd, void *download_buffer,
-			 unsigned int download_bytes)
+			 unsigned int download_bytes, char *response)
 {
 	struct part_info *part;
 	struct mtd_info *mtd = NULL;
 	int ret;
 
-	ret = fb_nand_lookup(cmd, &mtd, &part);
+	ret = fb_nand_lookup(cmd, &mtd, &part, response);
 	if (ret) {
 		error("invalid NAND device");
-		fastboot_fail("invalid NAND device");
+		fastboot_fail("invalid NAND device", response);
 		return;
 	}
 
@@ -181,7 +182,7 @@
 
 		sparse.priv = &sparse_priv;
 		write_sparse_image(&sparse, cmd, download_buffer,
-				   download_bytes);
+				   download_bytes, response);
 	} else {
 		printf("Flashing raw image at offset 0x%llx\n",
 		       part->offset);
@@ -194,23 +195,23 @@
 	}
 
 	if (ret) {
-		fastboot_fail("error writing the image");
+		fastboot_fail("error writing the image", response);
 		return;
 	}
 
-	fastboot_okay("");
+	fastboot_okay("", response);
 }
 
-void fb_nand_erase(const char *cmd)
+void fb_nand_erase(const char *cmd, char *response)
 {
 	struct part_info *part;
 	struct mtd_info *mtd = NULL;
 	int ret;
 
-	ret = fb_nand_lookup(cmd, &mtd, &part);
+	ret = fb_nand_lookup(cmd, &mtd, &part, response);
 	if (ret) {
 		error("invalid NAND device");
-		fastboot_fail("invalid NAND device");
+		fastboot_fail("invalid NAND device", response);
 		return;
 	}
 
@@ -221,9 +222,9 @@
 	ret = _fb_nand_erase(mtd, part);
 	if (ret) {
 		error("failed erasing from device %s", mtd->name);
-		fastboot_fail("failed erasing from device");
+		fastboot_fail("failed erasing from device", response);
 		return;
 	}
 
-	fastboot_okay("");
+	fastboot_okay("", response);
 }
diff --git a/common/image-sparse.c b/common/image-sparse.c
index ddf5772..f1382bd 100644
--- a/common/image-sparse.c
+++ b/common/image-sparse.c
@@ -51,7 +51,7 @@
 
 void write_sparse_image(
 		struct sparse_storage *info, const char *part_name,
-		void *data, unsigned sz)
+		void *data, unsigned sz, char *response)
 {
 	lbaint_t blk;
 	lbaint_t blkcnt;
@@ -101,7 +101,7 @@
 	if (offset) {
 		printf("%s: Sparse image block size issue [%u]\n",
 		       __func__, sparse_header->blk_sz);
-		fastboot_fail("sparse image block size issue");
+		fastboot_fail("sparse image block size issue", response);
 		return;
 	}
 
@@ -137,7 +137,7 @@
 			if (chunk_header->total_sz !=
 			    (sparse_header->chunk_hdr_sz + chunk_data_sz)) {
 				fastboot_fail(
-					"Bogus chunk size for chunk type Raw");
+					"Bogus chunk size for chunk type Raw", response);
 				return;
 			}
 
@@ -146,7 +146,7 @@
 				    "%s: Request would exceed partition size!\n",
 				    __func__);
 				fastboot_fail(
-				    "Request would exceed partition size!");
+				    "Request would exceed partition size!", response);
 				return;
 			}
 
@@ -157,7 +157,7 @@
 				       __func__, "Write failed, block #",
 				       blk, blks);
 				fastboot_fail(
-					      "flash write failure");
+					      "flash write failure", response);
 				return;
 			}
 			blk += blks;
@@ -170,7 +170,7 @@
 			if (chunk_header->total_sz !=
 			    (sparse_header->chunk_hdr_sz + sizeof(uint32_t))) {
 				fastboot_fail(
-					"Bogus chunk size for chunk type FILL");
+					"Bogus chunk size for chunk type FILL", response);
 				return;
 			}
 
@@ -181,7 +181,7 @@
 						ARCH_DMA_MINALIGN));
 			if (!fill_buf) {
 				fastboot_fail(
-					"Malloc failed for: CHUNK_TYPE_FILL");
+					"Malloc failed for: CHUNK_TYPE_FILL", response);
 				return;
 			}
 
@@ -199,7 +199,7 @@
 				    "%s: Request would exceed partition size!\n",
 				    __func__);
 				fastboot_fail(
-				    "Request would exceed partition size!");
+				    "Request would exceed partition size!", response);
 				return;
 			}
 
@@ -215,7 +215,7 @@
 					       "Write failed, block #",
 					       blk, j);
 					fastboot_fail(
-						      "flash write failure");
+						      "flash write failure", response);
 					free(fill_buf);
 					return;
 				}
@@ -236,7 +236,7 @@
 			if (chunk_header->total_sz !=
 			    sparse_header->chunk_hdr_sz) {
 				fastboot_fail(
-					"Bogus chunk size for chunk type Dont Care");
+					"Bogus chunk size for chunk type Dont Care", response);
 				return;
 			}
 			total_blocks += chunk_header->chunk_sz;
@@ -246,7 +246,7 @@
 		default:
 			printf("%s: Unknown chunk type: %x\n", __func__,
 			       chunk_header->chunk_type);
-			fastboot_fail("Unknown chunk type");
+			fastboot_fail("Unknown chunk type", response);
 			return;
 		}
 	}
@@ -256,9 +256,9 @@
 	printf("........ wrote %u bytes to '%s'\n", bytes_written, part_name);
 
 	if (total_blocks != sparse_header->total_blks)
-		fastboot_fail("sparse image write failure");
+		fastboot_fail("sparse image write failure", response);
 	else
-		fastboot_okay("");
+		fastboot_okay("", response);
 
 	return;
 }
diff --git a/doc/README.fdt-control b/doc/README.fdt-control
index 2913fcb..fa40d72 100644
--- a/doc/README.fdt-control
+++ b/doc/README.fdt-control
@@ -130,6 +130,10 @@
 CONFIG_SPL_FRAMEWORK, then u-boot.img will be built to include the device
 tree binary.
 
+If CONFIG_OF_BOARD is defined, a board-specific routine will provide the
+device tree at runtime, for example if an earlier bootloader stage creates
+it and passes it to U-Boot.
+
 If CONFIG_OF_HOSTFILE is defined, then it will be read from a file on
 startup. This is only useful for sandbox. Use the -d flag to U-Boot to
 specify the file to read.
diff --git a/drivers/mmc/bcm2835_sdhci.c b/drivers/mmc/bcm2835_sdhci.c
index cb2bd40..9ae271c 100644
--- a/drivers/mmc/bcm2835_sdhci.c
+++ b/drivers/mmc/bcm2835_sdhci.c
@@ -44,6 +44,7 @@
 
 /* 400KHz is max freq for card ID etc. Use that as min */
 #define MIN_FREQ 400000
+#define SDHCI_BUFFER 0x20
 
 struct bcm2835_sdhci_host {
 	struct sdhci_host host;
@@ -69,8 +70,10 @@
 	 * (Which is just as well - otherwise we'd have to nobble the DMA engine
 	 * too)
 	 */
-	while (timer_get_us() - bcm_host->last_write < bcm_host->twoticks_delay)
-		;
+	if (reg != SDHCI_BUFFER) {
+		while (timer_get_us() - bcm_host->last_write < bcm_host->twoticks_delay)
+			;
+	}
 
 	writel(val, host->ioaddr + reg);
 	bcm_host->last_write = timer_get_us();
diff --git a/drivers/usb/gadget/f_fastboot.c b/drivers/usb/gadget/f_fastboot.c
index 2160b1c..522a3ce 100644
--- a/drivers/usb/gadget/f_fastboot.c
+++ b/drivers/usb/gadget/f_fastboot.c
@@ -150,21 +150,6 @@
 static void rx_handler_command(struct usb_ep *ep, struct usb_request *req);
 static int strcmp_l1(const char *s1, const char *s2);
 
-
-static char *fb_response_str;
-
-void fastboot_fail(const char *reason)
-{
-	strncpy(fb_response_str, "FAIL\0", 5);
-	strncat(fb_response_str, reason, FASTBOOT_RESPONSE_LEN - 4 - 1);
-}
-
-void fastboot_okay(const char *reason)
-{
-	strncpy(fb_response_str, "OKAY\0", 5);
-	strncat(fb_response_str, reason, FASTBOOT_RESPONSE_LEN - 4 - 1);
-}
-
 static void fastboot_complete(struct usb_ep *ep, struct usb_request *req)
 {
 	int status = req->status;
@@ -590,18 +575,14 @@
 		return;
 	}
 
-	/* initialize the response buffer */
-	fb_response_str = response;
-
-	fastboot_fail("no flash device defined");
+	fastboot_fail("no flash device defined", response);
 #ifdef CONFIG_FASTBOOT_FLASH_MMC_DEV
 	fb_mmc_flash_write(cmd, (void *)CONFIG_FASTBOOT_BUF_ADDR,
-			   download_bytes);
+				download_bytes, response);
 #endif
 #ifdef CONFIG_FASTBOOT_FLASH_NAND_DEV
-	fb_nand_flash_write(cmd,
-			    (void *)CONFIG_FASTBOOT_BUF_ADDR,
-			    download_bytes);
+	fb_nand_flash_write(cmd, (void *)CONFIG_FASTBOOT_BUF_ADDR,
+				download_bytes, response);
 #endif
 	fastboot_tx_write_str(response);
 }
@@ -642,15 +623,12 @@
 		return;
 	}
 
-	/* initialize the response buffer */
-	fb_response_str = response;
-
-	fastboot_fail("no flash device defined");
+	fastboot_fail("no flash device defined", response);
 #ifdef CONFIG_FASTBOOT_FLASH_MMC_DEV
-	fb_mmc_erase(cmd);
+	fb_mmc_erase(cmd, response);
 #endif
 #ifdef CONFIG_FASTBOOT_FLASH_NAND_DEV
-	fb_nand_erase(cmd);
+	fb_nand_erase(cmd, response);
 #endif
 	fastboot_tx_write_str(response);
 }
diff --git a/dts/Kconfig b/dts/Kconfig
index 4b7d8b1..7d33506 100644
--- a/dts/Kconfig
+++ b/dts/Kconfig
@@ -41,6 +41,14 @@
 	  and development only and is not recommended for production devices.
 	  Boards in the mainline U-Boot tree should not use it.
 
+config OF_BOARD
+	bool "Provided by the board at runtime"
+	depends on !SANDBOX
+	help
+	  If this option is enabled, the device tree will be provided by
+	  the board at runtime if the board supports it, instead of being
+	  bundled with the image.
+
 config OF_HOSTFILE
 	bool "Host filed DTB for DT control"
 	depends on SANDBOX
diff --git a/include/fastboot.h b/include/fastboot.h
index 616631e..bc7ff2b 100644
--- a/include/fastboot.h
+++ b/include/fastboot.h
@@ -14,9 +14,22 @@
 #define _FASTBOOT_H_
 
 /* The 64 defined bytes plus \0 */
-#define FASTBOOT_RESPONSE_LEN	(64 + 1)
+#define FASTBOOT_RESPONSE_LEN (64 + 1)
 
-void fastboot_fail(const char *reason);
-void fastboot_okay(const char *reason);
+void fastboot_fail(const char *reason, char *response);
+void fastboot_okay(const char *reason, char *response);
+
+/**
+ * Send an INFO packet during long commands based on timer. If
+ * CONFIG_UDP_FUNCTION_FASTBOOT is defined, an INFO packet is sent
+ * if the time is 30 seconds after start. Else, noop.
+ *
+ * TODO: Handle the situation where both UDP and USB fastboot are
+ *       enabled.
+ *
+ * @param start:  Time since last INFO packet was sent.
+ * @param msg:    String describing the reason for waiting
+ */
+void timed_send_info(ulong *start, const char *msg);
 
 #endif /* _FASTBOOT_H_ */
diff --git a/include/fb_mmc.h b/include/fb_mmc.h
index 12b99cb..402ba9b 100644
--- a/include/fb_mmc.h
+++ b/include/fb_mmc.h
@@ -5,5 +5,5 @@
  */
 
 void fb_mmc_flash_write(const char *cmd, void *download_buffer,
-			unsigned int download_bytes);
-void fb_mmc_erase(const char *cmd);
+			unsigned int download_bytes, char *response);
+void fb_mmc_erase(const char *cmd, char *response);
diff --git a/include/fb_nand.h b/include/fb_nand.h
index aaf7cf7..88bdf36 100644
--- a/include/fb_nand.h
+++ b/include/fb_nand.h
@@ -6,5 +6,5 @@
  */
 
 void fb_nand_flash_write(const char *cmd, void *download_buffer,
-			 unsigned int download_bytes);
-void fb_nand_erase(const char *cmd);
+			 unsigned int download_bytes, char *response);
+void fb_nand_erase(const char *cmd, char *response);
diff --git a/include/fdtdec.h b/include/fdtdec.h
index d074478..5c54f71 100644
--- a/include/fdtdec.h
+++ b/include/fdtdec.h
@@ -1015,4 +1015,10 @@
  */
 int fdtdec_setup(void);
 
+/**
+ * Board-specific FDT initialization. Returns the address to a device tree blob.
+ * Called when CONFIG_OF_BOARD is defined.
+ */
+void *board_fdt_blob_setup(void);
+
 #endif
diff --git a/include/image-sparse.h b/include/image-sparse.h
index b0cc500..967bb6a 100644
--- a/include/image-sparse.h
+++ b/include/image-sparse.h
@@ -37,4 +37,4 @@
 }
 
 void write_sparse_image(struct sparse_storage *info, const char *part_name,
-			void *data, unsigned sz);
+			void *data, unsigned sz, char *response);
diff --git a/include/net.h b/include/net.h
index 06320c6..bac3c83 100644
--- a/include/net.h
+++ b/include/net.h
@@ -527,7 +527,7 @@
 
 enum proto_t {
 	BOOTP, RARP, ARP, TFTPGET, DHCP, PING, DNS, NFS, CDP, NETCONS, SNTP,
-	TFTPSRV, TFTPPUT, LINKLOCAL
+	TFTPSRV, TFTPPUT, LINKLOCAL, FASTBOOT
 };
 
 extern char	net_boot_file_name[1024];/* Boot File name */
@@ -541,6 +541,10 @@
 extern char *net_dns_env_var;		/* the env var to put the ip into */
 #endif
 
+#if defined(CONFIG_UDP_FUNCTION_FASTBOOT)
+int do_fastboot_udp(cmd_tbl_t *cmdtp, int flag, int argc, char *const argv[]);
+#endif
+
 #if defined(CONFIG_CMD_PING)
 extern struct in_addr net_ping_ip;	/* the ip address to ping */
 #endif
diff --git a/include/net/fastboot.h b/include/net/fastboot.h
new file mode 100644
index 0000000..538af29
--- /dev/null
+++ b/include/net/fastboot.h
@@ -0,0 +1,28 @@
+/*
+* Copyright (C) 2016 The Android Open Source Project
+*
+* SPDX-License-Identifier: BSD-2-Clause
+*/
+
+#ifndef __NET_FASTBOOT_H__
+#define __NET_FASTBOOT_H__
+
+/**********************************************************************/
+/*
+ *	Global functions and variables.
+ */
+
+/**
+ * Wait for incoming fastboot comands.
+ */
+void fastboot_start_server(void);
+/**
+ * Send an INFO packet during long commands
+ *
+ * @param msg: String describing the reason for waiting
+ */
+void fastboot_send_info(const char*);
+
+/**********************************************************************/
+
+#endif /* __NET_FASTBOOT_H__ */
diff --git a/lib/fdtdec.c b/lib/fdtdec.c
index 81f47ef..2e1beb5 100644
--- a/lib/fdtdec.c
+++ b/lib/fdtdec.c
@@ -1247,6 +1247,9 @@
 	/* FDT is at end of image */
 	gd->fdt_blob = (ulong *)&_end;
 #  endif
+# elif defined(CONFIG_OF_BOARD)
+	/* Allow the board to override the fdt address. */
+	gd->fdt_blob = board_fdt_blob_setup();
 # elif defined(CONFIG_OF_HOSTFILE)
 	if (sandbox_read_fdt_from_file()) {
 		puts("Failed to read control FDT\n");
diff --git a/net/Makefile b/net/Makefile
index f03d608..e67b4a2 100644
--- a/net/Makefile
+++ b/net/Makefile
@@ -25,3 +25,4 @@
 obj-$(CONFIG_CMD_RARP) += rarp.o
 obj-$(CONFIG_CMD_SNTP) += sntp.o
 obj-$(CONFIG_CMD_NET)  += tftp.o
+obj-$(CONFIG_UDP_FUNCTION_FASTBOOT)  += fastboot.o
diff --git a/net/fastboot.c b/net/fastboot.c
new file mode 100644
index 0000000..aff7fd7
--- /dev/null
+++ b/net/fastboot.c
@@ -0,0 +1,528 @@
+/*
+* Copyright (C) 2016 The Android Open Source Project
+*
+* SPDX-License-Identifier: BSD-2-Clause
+*/
+
+#include <common.h>
+#include <fastboot.h>
+#include <fb_mmc.h>
+#include <net.h>
+#include <net/fastboot.h>
+#include <part.h>
+#include <stdlib.h>
+#include <version.h>
+
+/* Fastboot port # defined in spec */
+#define WELL_KNOWN_PORT 5554
+
+enum {
+	FASTBOOT_ERROR = 0,
+	FASTBOOT_QUERY = 1,
+	FASTBOOT_INIT = 2,
+	FASTBOOT_FASTBOOT = 3,
+};
+
+struct __attribute__((packed)) fastboot_header {
+	uchar id;
+	uchar flags;
+	unsigned short seq;
+};
+
+#define PACKET_SIZE 1024
+#define FASTBOOT_HEADER_SIZE sizeof(struct fastboot_header)
+#define DATA_SIZE (PACKET_SIZE - FASTBOOT_HEADER_SIZE)
+#define FASTBOOT_VERSION "0.4"
+
+/* Sequence number sent for every packet */
+static unsigned short fb_sequence_number = 1;
+static const unsigned short fb_packet_size = PACKET_SIZE;
+static const unsigned short fb_udp_version = 1;
+
+/* Keep track of last packet for resubmission */
+static uchar last_packet[PACKET_SIZE];
+static unsigned int last_packet_len = 0;
+
+/* Parsed from first fastboot command packet */
+static char *cmd_string = NULL;
+static char *cmd_parameter = NULL;
+
+/* Fastboot download parameters */
+static unsigned int bytes_received = 0;
+static unsigned int bytes_expected = 0;
+static unsigned int image_size = 0;
+
+static struct in_addr fastboot_remote_ip;
+/* The UDP port at their end */
+static int fastboot_remote_port;
+/* The UDP port at our end */
+static int fastboot_our_port;
+
+static void fb_getvar(char*);
+static void fb_download(char*, unsigned int, char*);
+static void fb_flash(char*);
+static void fb_erase(char*);
+static void fb_continue(char*);
+static void fb_reboot(char*);
+static void boot_downloaded_image(void);
+static void cleanup_command_data(void);
+static void write_fb_response(const char*, const char*, char*);
+
+void fastboot_send_info(const char *msg)
+{
+	uchar *packet;
+	uchar *packet_base;
+	int len = 0;
+	char response[FASTBOOT_RESPONSE_LEN] = {0};
+
+	struct fastboot_header fb_response_header =
+	{
+		.id = FASTBOOT_FASTBOOT,
+		.flags = 0,
+		.seq = htons(fb_sequence_number)
+	};
+	++fb_sequence_number;
+	packet = net_tx_packet + net_eth_hdr_size() + IP_UDP_HDR_SIZE;
+	packet_base = packet;
+
+	/* Write headers */
+	memcpy(packet, &fb_response_header, sizeof(fb_response_header));
+	packet += sizeof(fb_response_header);
+	/* Write response */
+	write_fb_response("INFO", msg, response);
+	memcpy(packet, response, strlen(response));
+	packet += strlen(response);
+
+	len = packet-packet_base;
+
+	/* Save packet for retransmitting */
+	last_packet_len = len;
+	memcpy(last_packet, packet_base, last_packet_len);
+
+	net_send_udp_packet(net_server_ethaddr, fastboot_remote_ip,
+			    fastboot_remote_port, fastboot_our_port, len);
+}
+
+/**
+ * Constructs and sends a packet in response to received fastboot packet
+ *
+ * @param fb_header            Header for response packet
+ * @param fastboot_data        Pointer to received fastboot data
+ * @param fastboot_data_len    Length of received fastboot data
+ * @param retransmit           Nonzero if sending last sent packet
+ */
+static void fastboot_send(struct fastboot_header fb_header, char *fastboot_data,
+		unsigned int fastboot_data_len, uchar retransmit)
+{
+	uchar *packet;
+	uchar *packet_base;
+	int len = 0;
+	const char *error_msg = "An error occurred.";
+	short tmp;
+	struct fastboot_header fb_response_header = fb_header;
+	char response[FASTBOOT_RESPONSE_LEN] = {0};
+	/*
+	 *	We will always be sending some sort of packet, so
+	 *	cobble together the packet headers now.
+	 */
+	packet = net_tx_packet + net_eth_hdr_size() + IP_UDP_HDR_SIZE;
+	packet_base = packet;
+
+	/* Resend last packet */
+	if (retransmit) {
+		memcpy(packet, last_packet, last_packet_len);
+		net_send_udp_packet(net_server_ethaddr, fastboot_remote_ip,
+				    fastboot_remote_port, fastboot_our_port, last_packet_len);
+		return;
+	}
+
+	fb_response_header.seq = htons(fb_response_header.seq);
+	memcpy(packet, &fb_response_header, sizeof(fb_response_header));
+	packet += sizeof(fb_response_header);
+
+	switch (fb_header.id) {
+	case FASTBOOT_QUERY:
+		tmp = htons(fb_sequence_number);
+		memcpy(packet, &tmp, sizeof(tmp));
+		packet += sizeof(tmp);
+		break;
+	case FASTBOOT_INIT:
+		tmp = htons(fb_udp_version);
+		memcpy(packet, &tmp, sizeof(tmp));
+		packet += sizeof(tmp);
+		tmp = htons(fb_packet_size);
+		memcpy(packet, &tmp, sizeof(tmp));
+		packet += sizeof(tmp);
+		break;
+	case FASTBOOT_ERROR:
+		memcpy(packet, error_msg, strlen(error_msg));
+		packet += strlen(error_msg);
+		break;
+	case FASTBOOT_FASTBOOT:
+		if (cmd_string == NULL) {
+			/* Parse command and send ack */
+			cmd_parameter = fastboot_data;
+			cmd_string = strsep(&cmd_parameter, ":");
+			cmd_string = strdup(cmd_string);
+			if (cmd_parameter) {
+				cmd_parameter = strdup(cmd_parameter);
+			}
+		} else if (!strcmp("getvar", cmd_string)) {
+			fb_getvar(response);
+		} else if (!strcmp("download", cmd_string)) {
+			fb_download(fastboot_data, fastboot_data_len, response);
+		} else if (!strcmp("flash", cmd_string)) {
+			fb_flash(response);
+		} else if (!strcmp("erase", cmd_string)) {
+			fb_erase(response);
+		} else if (!strcmp("boot", cmd_string)) {
+			write_fb_response("OKAY", "", response);
+		} else if (!strcmp("continue", cmd_string)) {
+			fb_continue(response);
+		} else if (!strncmp("reboot", cmd_string, 6)) {
+			fb_reboot(response);
+		} else if (!strcmp("set_active", cmd_string)) {
+			/* A/B not implemented, for now do nothing */
+			write_fb_response("OKAY", "", response);
+		} else {
+			error("command %s not implemented.\n", cmd_string);
+			write_fb_response("FAIL", "unrecognized command", response);
+		}
+		/* Sent some INFO packets, need to update sequence number in header */
+		if (fb_header.seq != fb_sequence_number) {
+			fb_response_header.seq = htons(fb_sequence_number);
+			memcpy(packet_base, &fb_response_header, sizeof(fb_response_header));
+		}
+		/* Write response to packet */
+		memcpy(packet, response, strlen(response));
+		packet += strlen(response);
+		break;
+	default:
+		error("ID %d not implemented.\n", fb_header.id);
+		return;
+	}
+
+	len = packet-packet_base;
+
+	/* Save packet for retransmitting */
+	last_packet_len = len;
+	memcpy(last_packet, packet_base, last_packet_len);
+
+	net_send_udp_packet(net_server_ethaddr, fastboot_remote_ip,
+			    fastboot_remote_port, fastboot_our_port, len);
+
+	/* Continue boot process after sending response */
+	if (!strncmp("OKAY", response, 4)) {
+		if (!strcmp("boot", cmd_string)) {
+			boot_downloaded_image();
+		} else if (!strcmp("continue", cmd_string)) {
+			run_command(getenv("bootcmd"), CMD_FLAG_ENV);
+		} else if (!strncmp("reboot", cmd_string, 6)) {
+			/* Matches reboot or reboot-bootloader */
+			do_reset(NULL, 0, 0, NULL);
+		}
+	}
+
+	/* OKAY and FAIL indicate command is complete */
+	if (!strncmp("OKAY", response, 4) ||
+			!strncmp("FAIL", response, 4)) {
+		cleanup_command_data();
+	}
+}
+
+/**
+ * Writes ascii string specified by cmd_parameter to response.
+ *
+ * @param repsonse    Pointer to fastboot response buffer
+ */
+static void fb_getvar(char *response)
+{
+
+	if (cmd_parameter == NULL) {
+		write_fb_response("FAIL", "missing var", response);
+	} else if (!strcmp("version", cmd_parameter)) {
+		write_fb_response("OKAY", FASTBOOT_VERSION, response);
+	} else if (!strcmp("bootloader-version", cmd_parameter) ||
+			!strcmp("version-bootloader", cmd_parameter)) {
+		write_fb_response("OKAY", U_BOOT_VERSION, response);
+	} else if (!strcmp("downloadsize", cmd_parameter) ||
+			!strcmp("max-download-size", cmd_parameter)) {
+		char buf_size_str[12];
+		sprintf(buf_size_str, "0x%08x", CONFIG_FASTBOOT_BUF_SIZE);
+		write_fb_response("OKAY", buf_size_str, response);
+	} else if (!strcmp("serialno", cmd_parameter)) {
+		const char *tmp = getenv("serial#");
+		if (tmp) {
+			write_fb_response("OKAY", tmp, response);
+		} else {
+			write_fb_response("FAIL", "Value not set", response);
+		}
+	} else if (!strcmp("version-baseband", cmd_parameter)) {
+		write_fb_response("OKAY", "N/A", response);
+	} else if (!strcmp("product", cmd_parameter)) {
+		const char *board = getenv("board");
+		if (board) {
+			write_fb_response("OKAY", board, response);
+		} else {
+			write_fb_response("FAIL", "Board not set", response);
+		}
+	} else if (!strcmp("current-slot", cmd_parameter)) {
+		/* A/B not implemented, for now always return _a */
+		write_fb_response("OKAY", "_a", response);
+	} else if (!strcmp("slot-suffixes", cmd_parameter)) {
+		write_fb_response("OKAY", "_a,_b", response);
+	} else if (!strncmp("has-slot", cmd_parameter, 8)) {
+		char *part_name = cmd_parameter;
+
+		cmd_parameter = strsep(&part_name, ":");
+		if (!strcmp(part_name, "boot") || !strcmp(part_name, "system")) {
+			write_fb_response("OKAY", "yes", response);
+		} else {
+			write_fb_response("OKAY", "no", response);
+		}
+	} else if (!strncmp("partition-type", cmd_parameter, 14)) {
+		disk_partition_t part_info;
+		struct blk_desc *dev_desc;
+		char *part_name = cmd_parameter;
+
+		cmd_parameter = strsep(&part_name, ":");
+		dev_desc = blk_get_dev("mmc", 0);
+		if (dev_desc == NULL) {
+			write_fb_response("FAIL", "block device not found", response);
+		} else if (part_get_info_by_name(dev_desc, part_name, &part_info) < 0) {
+			write_fb_response("FAIL", "partition not found", response);
+		} else {
+			write_fb_response("OKAY", (char*)part_info.type, response);
+		}
+	} else {
+		printf("WARNING: unknown variable: %s\n", cmd_parameter);
+		write_fb_response("FAIL", "Variable not implemented", response);
+	}
+}
+
+/**
+ * Copies image data from fastboot_data to CONFIG_FASTBOOT_BUF_ADDR.
+ * Writes to response.
+ *
+ * @param fastboot_data        Pointer to received fastboot data
+ * @param fastboot_data_len    Length of received fastboot data
+ * @param repsonse             Pointer to fastboot response buffer
+ */
+static void fb_download(char *fastboot_data, unsigned int fastboot_data_len,
+		char *response)
+{
+	char *tmp;
+
+	if (bytes_expected == 0) {
+		if (cmd_parameter == NULL) {
+			write_fb_response("FAIL", "Expected command parameter", response);
+			return;
+		}
+		bytes_expected = simple_strtoul(cmd_parameter, &tmp, 16);
+		if (bytes_expected == 0) {
+			write_fb_response("FAIL", "Expected nonzero image size", response);
+			return;
+		}
+	}
+	if (fastboot_data_len == 0 && bytes_received == 0) {
+		/* Nothing to download yet. Response is of the form:
+		 * [DATA|FAIL]$cmd_parameter
+		 *
+		 * where cmd_parameter is an 8 digit hexadecimal number
+		 */
+		if (bytes_expected > CONFIG_FASTBOOT_BUF_SIZE) {
+			write_fb_response("FAIL", cmd_parameter, response);
+		} else {
+			write_fb_response("DATA", cmd_parameter, response);
+		}
+	} else if (fastboot_data_len == 0 && (bytes_received >= bytes_expected)) {
+		/* Download complete. Respond with "OKAY" */
+		write_fb_response("OKAY", "", response);
+		image_size = bytes_received;
+		bytes_expected = bytes_received = 0;
+	} else {
+		if (fastboot_data_len == 0 ||
+				(bytes_received + fastboot_data_len) > bytes_expected) {
+			write_fb_response("FAIL", "Received invalid data length", response);
+			return;
+		}
+		/* Download data to CONFIG_FASTBOOT_BUF_ADDR */
+		memcpy((void*)CONFIG_FASTBOOT_BUF_ADDR + bytes_received, fastboot_data,
+				fastboot_data_len);
+		bytes_received += fastboot_data_len;
+	}
+}
+
+/**
+ * Writes the previously downloaded image to the partition indicated by
+ * cmd_parameter. Writes to response.
+ *
+ * @param repsonse    Pointer to fastboot response buffer
+ */
+static void fb_flash(char *response)
+{
+	fb_mmc_flash_write(cmd_parameter, (void*)CONFIG_FASTBOOT_BUF_ADDR,
+			image_size, response);
+}
+
+/**
+ * Erases the partition indicated by cmd_parameter (clear to 0x00s). Writes
+ * to response.
+ *
+ * @param repsonse    Pointer to fastboot response buffer
+ */
+static void fb_erase(char *response)
+{
+	fb_mmc_erase(cmd_parameter, response);
+}
+
+/**
+ * Continues normal boot process by running "bootcmd". Writes
+ * to response.
+ *
+ * @param repsonse    Pointer to fastboot response buffer
+ */
+static void fb_continue(char *response)
+{
+	char *bootcmd;
+	bootcmd = getenv("bootcmd");
+	if (bootcmd) {
+		write_fb_response("OKAY", "", response);
+	} else {
+		write_fb_response("FAIL", "bootcmd not set", response);
+	}
+}
+
+/**
+ * Sets reboot bootloader flag if requested. Writes to response.
+ *
+ * @param repsonse    Pointer to fastboot response buffer
+ */
+static void fb_reboot(char *response)
+{
+	write_fb_response("OKAY", "", response);
+	if (!strcmp("reboot-bootloader", cmd_string)) {
+		strcpy((char*)CONFIG_FASTBOOT_BUF_ADDR, "reboot-bootloader");
+	}
+}
+
+/**
+ * Boots into downloaded image.
+ */
+static void boot_downloaded_image(void)
+{
+	char kernel_addr[12];
+	char *fdt_addr = getenv("fdt_addr_r");
+	char *bootm_args[] = { "bootm", kernel_addr, "-", fdt_addr, NULL };
+
+	sprintf(kernel_addr, "0x%lx", (long)CONFIG_FASTBOOT_BUF_ADDR);
+
+	printf("\nBooting kernel at %s with fdt at %s...\n\n\n",
+			kernel_addr, fdt_addr);
+	do_bootm(NULL, 0, 4, bootm_args);
+
+	/* This only happens if image is faulty so we start over. */
+	do_reset(NULL, 0, 0, NULL);
+}
+
+/**
+ * Writes a response to response buffer of the form "$tag$reason".
+ *
+ * @param tag         The first part of the response
+ * @param reason      The second part of the response
+ * @param repsonse    Pointer to fastboot response buffer
+ */
+static void write_fb_response(const char* tag, const char *reason,
+		char *response)
+{
+	strncpy(response, tag, strlen(tag));
+	strncat(response, reason, FASTBOOT_RESPONSE_LEN - strlen(tag) - 1);
+}
+
+/**
+ * Frees any resources allocated during current fastboot command.
+ */
+static void cleanup_command_data(void)
+{
+	/* cmd_parameter and cmd_string potentially point to memory allocated by
+	 * strdup
+	 */
+	if (cmd_parameter) {
+		free(cmd_parameter);
+	}
+	if (cmd_string) {
+		free(cmd_string);
+	}
+	cmd_parameter = cmd_string = NULL;
+}
+
+/**
+ * Incoming UDP packet handler.
+ *
+ * @param packet  Pointer to incoming UDP packet
+ * @param dport   Destination UDP port
+ * @param sip     Source IP address
+ * @param sport   Source UDP port
+ * @param len     Packet length
+ */
+static void fastboot_handler(uchar *packet, unsigned dport, struct in_addr sip,
+		unsigned sport, unsigned len)
+{
+	struct fastboot_header fb_header;
+	char fastboot_data[DATA_SIZE] = {0};
+	unsigned int fastboot_data_len = 0;
+
+	if (dport != fastboot_our_port) {
+		return;
+	}
+
+	fastboot_remote_ip = sip;
+	fastboot_remote_port = sport;
+
+	if (len < FASTBOOT_HEADER_SIZE || len > PACKET_SIZE) {
+		return;
+	}
+	memcpy(&fb_header, packet, sizeof(fb_header));
+	fb_header.flags = 0;
+	fb_header.seq = ntohs(fb_header.seq);
+	packet += sizeof(fb_header);
+	len -= sizeof(fb_header);
+
+	switch (fb_header.id) {
+	case FASTBOOT_QUERY:
+		fastboot_send(fb_header, fastboot_data, 0, 0);
+		break;
+	case FASTBOOT_INIT:
+	case FASTBOOT_FASTBOOT:
+		fastboot_data_len = len;
+		if (len > 0) {
+			memcpy(fastboot_data, packet, len);
+		}
+		if (fb_header.seq == fb_sequence_number) {
+			fastboot_send(fb_header, fastboot_data, fastboot_data_len, 0);
+			fb_sequence_number++;
+		} else if (fb_header.seq == fb_sequence_number - 1) {
+			/* Retransmit last sent packet */
+			fastboot_send(fb_header, fastboot_data, fastboot_data_len, 1);
+		}
+		break;
+	default:
+		error("ID %d not implemented.\n", fb_header.id);
+		fb_header.id = FASTBOOT_ERROR;
+		fastboot_send(fb_header, fastboot_data, 0, 0);
+		break;
+	}
+}
+
+void fastboot_start_server(void)
+{
+	printf("Using %s device\n", eth_get_name());
+	printf("Listening for fastboot command on %pI4\n", &net_ip);
+
+	fastboot_our_port = WELL_KNOWN_PORT;
+
+	net_set_udp_handler(fastboot_handler);
+
+	/* zero out server ether in case the server ip has changed */
+	memset(net_server_ethaddr, 0, 6);
+}
diff --git a/net/net.c b/net/net.c
index 671d45d..0b70279 100644
--- a/net/net.c
+++ b/net/net.c
@@ -87,6 +87,9 @@
 #include <environment.h>
 #include <errno.h>
 #include <net.h>
+#if defined(CONFIG_UDP_FUNCTION_FASTBOOT)
+#include <net/fastboot.h>
+#endif
 #include <net/tftp.h>
 #if defined(CONFIG_STATUS_LED)
 #include <miiphy.h>
@@ -453,6 +456,11 @@
 			tftp_start_server();
 			break;
 #endif
+#ifdef CONFIG_UDP_FUNCTION_FASTBOOT
+		case FASTBOOT:
+			fastboot_start_server();
+			break;
+#endif
 #if defined(CONFIG_CMD_DHCP)
 		case DHCP:
 			bootp_reset();
@@ -1322,6 +1330,7 @@
 		/* Fall through */
 
 	case NETCONS:
+	case FASTBOOT:
 	case TFTPSRV:
 		if (net_ip.s_addr == 0) {
 			puts("*** ERROR: `ipaddr' not set\n");
diff --git a/update_uboot.sh b/update_uboot.sh
new file mode 100755
index 0000000..be5247b
--- /dev/null
+++ b/update_uboot.sh
@@ -0,0 +1,162 @@
+#!/bin/bash
+#
+# Copyright (C) 2016 The Android Open-Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+
+set -e -u
+
+base_dir=$(realpath $(dirname $0))
+cd "${base_dir}"
+
+UPSTREAM_GIT="http://git.denx.de/u-boot.git"
+UPSTREAM_FTP="ftp://ftp.denx.de/pub/u-boot/"
+UPSTREAM="denx_de"
+# The latest tag format is defined as follows:
+UPSTREAM_TAG_REGEX="v[0-9]{4}\\.[0-9]{2}(\\.[0-9]{2})?"
+README_FILE="README.version"
+
+REPLICATION_BRANCH="upstream-master"
+
+TEMP_FILES=()
+cleanup() {
+  trap - INT TERM ERR EXIT
+  if [[ ${#TEMP_FILES[@]} -ne 0 ]]; then
+    rm -f "${TEMP_FILES[@]}"
+  fi
+}
+trap cleanup INT TERM ERR EXIT
+
+
+# Update the contents of the README.version with the new version string passed
+# as the first parameter.
+update_readme() {
+  local version="$1"
+  local tmp_readme=$(mktemp update_readme.XXXXXX)
+  TEMP_FILES+=("${tmp_readme}")
+
+  cat >"${tmp_readme}" <<EOF
+URL: ${UPSTREAM_FTP}u-boot-${version}.tar.bz2
+Version: ${version}
+EOF
+  grep -v -E '^(URL|Version):' "${README_FILE}" >>"${tmp_readme}" 2>/dev/null \
+    || true
+  cp "${tmp_readme}" "${README_FILE}"
+}
+
+
+# Setup and fetch the upstream remote. While we have mirroring setup of the
+# remote, we need to fetch all the tags from upstream to identify the latest
+# release.
+setup_git() {
+  # Setup and fetch the remote.
+  if ! git remote show | grep "^${UPSTREAM}\$" >/dev/null; then
+    echo "Adding remote ${UPSTREAM} to ${UPSTREAM_GIT}"
+    git remote add -t master "${UPSTREAM}" "${UPSTREAM_GIT}"
+  fi
+
+  TRACKING_BRANCH=$(git rev-parse --abbrev-ref --symbolic-full-name @{u})
+  OUR_REMOTE="${TRACKING_BRANCH%/*}"
+
+  echo "Fetching latest upstream code..."
+  git fetch --quiet "${OUR_REMOTE}" "${REPLICATION_BRANCH}"
+  git fetch --quiet --tags "${UPSTREAM}" master
+}
+
+
+# Show a friendly
+show_merge_error() {
+  local tag="$1"
+  echo "Merge failed." >&2
+
+  local conflict_files=($(git diff --name-only --diff-filter=U))
+  echo >&2
+  echo "List merge conflict files:" >&2
+  local fn
+  for fn in "${conflict_files[@]}"; do
+    echo " * ${fn}" >&2
+  done
+
+  echo >&2
+  echo "List local commits not in upstream for those files:" >&2
+  git --no-pager log HEAD --not "${tag}" --oneline --no-merges -- \
+    "${conflict_files[@]}" >&2
+
+
+  echo >&2
+  echo "Run 'git merge --abort' to cancel the merge." >&2
+  exit 1
+}
+
+
+main() {
+  setup_git
+
+  local tags=($(git tag | grep -E "^${UPSTREAM_TAG_REGEX}\$" |
+                LC_ALL=C sort -r))
+
+  if [[ ${#tags[@]} -eq 0 ]]; then
+    echo "No versions detected, update tag regex."
+    exit 1
+  fi
+
+  local latest_tag="${tags[0]}"
+  local latest_version="${latest_tag#v}"
+  local current_version=$(
+    grep '^Version: ' "${README_FILE}" 2>/dev/null |
+    cut -f 2 -d ' ' || true)
+
+  echo "Current version: ${current_version}"
+  echo "Latest version: ${latest_version}"
+  echo
+
+  if [[ "${latest_version}" == "${current_version}" ]]; then
+    echo "Nothing to do. Exiting." >&2
+    exit 0
+  fi
+
+  # Sanity check that we have all the CLs replicated in upstream-master.
+  if [[ $(git log "${latest_tag}" --not "${OUR_REMOTE}/${REPLICATION_BRANCH}" \
+    --oneline -- | tee /dev/stderr | wc -c) -ne 0 ]]; then
+    echo
+    echo "ERROR: The previous CLs are not in the replication branch" \
+      "${OUR_REMOTE}/${REPLICATION_BRANCH}, please wait until it catches up" \
+      "or ping the build team." >&2
+    exit 1
+  fi
+
+  echo "Merging changes..."
+  git merge --no-stat --no-ff "${latest_tag}" \
+    -m "Update U-boot to ${latest_tag}
+
+Bug: [COMPLETE]
+Test: [COMPLETE]
+
+" || show_merge_error "${latest_tag}"
+  update_readme "${latest_version}"
+  git add "README.version"
+  git commit --amend --no-edit -- "README.version"
+
+  git --no-pager show
+  cat <<EOF
+
+  Run:
+    git commit --amend
+
+  and edit the message to add bug number and test it.
+
+EOF
+}
+
+main "$@"