Merge branch 'master' of git://git.denx.de/u-boot-ubi
diff --git a/arch/arm/dts/exynos5.dtsi b/arch/arm/dts/exynos5.dtsi
index 2db1be8..6102978 100644
--- a/arch/arm/dts/exynos5.dtsi
+++ b/arch/arm/dts/exynos5.dtsi
@@ -10,6 +10,8 @@
 / {
 	compatible = "samsung,exynos5";
 
+	interrupt-parent = <&gic>;
+
 	combiner: interrupt-controller@10440000 {
 		compatible = "samsung,exynos4210-combiner";
 		#interrupt-cells = <2>;
@@ -44,33 +46,6 @@
 		#size-cells = <0>;
 	};
 
-	combiner: interrupt-controller@10440000 {
-		compatible = "samsung,exynos4210-combiner";
-		#interrupt-cells = <2>;
-		interrupt-controller;
-		samsung,combiner-nr = <32>;
-		reg = <0x10440000 0x1000>;
-		interrupts =	<0 0 0>, <0 1 0>, <0 2 0>, <0 3 0>,
-				<0 4 0>, <0 5 0>, <0 6 0>, <0 7 0>,
-				<0 8 0>, <0 9 0>, <0 10 0>, <0 11 0>,
-				<0 12 0>, <0 13 0>, <0 14 0>, <0 15 0>,
-				<0 16 0>, <0 17 0>, <0 18 0>, <0 19 0>,
-				<0 20 0>, <0 21 0>, <0 22 0>, <0 23 0>,
-				<0 24 0>, <0 25 0>, <0 26 0>, <0 27 0>,
-				<0 28 0>, <0 29 0>, <0 30 0>, <0 31 0>;
-	};
-
-	gic: interrupt-controller@10481000 {
-		compatible = "arm,cortex-a15-gic", "arm,cortex-a9-gic";
-		#interrupt-cells = <3>;
-		interrupt-controller;
-		reg =	<0x10481000 0x1000>,
-			<0x10482000 0x1000>,
-			<0x10484000 0x2000>,
-			<0x10486000 0x2000>;
-		interrupts = <1 9 0xf04>;
-	};
-
 	i2c_0: i2c@12C60000 {
 		compatible = "samsung,s3c2440-i2c";
 		reg = <0x12C60000 0x100>;
diff --git a/arch/arm/mach-orion5x/lowlevel_init.S b/arch/arm/mach-orion5x/lowlevel_init.S
index f5c382b..aa3fcf7 100644
--- a/arch/arm/mach-orion5x/lowlevel_init.S
+++ b/arch/arm/mach-orion5x/lowlevel_init.S
@@ -71,67 +71,67 @@
 
 #ifdef CONFIG_SPL_BUILD
 
-	/* Use 'r4 as the base for internal register accesses */
-	ldr	r4, =ORION5X_REGS_PHY_BASE
+	/* Use 'r2 as the base for internal register accesses */
+	ldr	r2, =ORION5X_REGS_PHY_BASE
 
 	/* move internal registers from the default 0xD0000000
 	 * to their intended location, defined by SoC */
 	ldr	r3, =0xD0000000
 	add	r3, r3, #0x20000
-	str	r4, [r3, #0x80]
+	str	r2, [r3, #0x80]
 
 	/* Use R3 as the base for DRAM registers */
-	add	r3, r4, #0x01000
+	add	r3, r2, #0x01000
 
 	/*DDR SDRAM Initialization Control */
-	ldr	r6, =0x00000001
-	str	r6, [r3, #0x480]
+	ldr	r0, =0x00000001
+	str	r0, [r3, #0x480]
 
 	/* Use R3 as the base for PCI registers */
-	add	r3, r4, #0x31000
+	add	r3, r2, #0x31000
 
 	/* Disable arbiter */
-	ldr	r6, =0x00000030
-	str	r6, [r3, #0xd00]
+	ldr	r0, =0x00000030
+	str	r0, [r3, #0xd00]
 
 	/* Use R3 as the base for DRAM registers */
-	add	r3, r4, #0x01000
+	add	r3, r2, #0x01000
 
 	/* set all dram windows to 0 */
-	mov	r6, #0
-	str	r6, [r3, #0x504]
-	str	r6, [r3, #0x50C]
-	str	r6, [r3, #0x514]
-	str	r6, [r3, #0x51C]
+	mov	r0, #0
+	str	r0, [r3, #0x504]
+	str	r0, [r3, #0x50C]
+	str	r0, [r3, #0x514]
+	str	r0, [r3, #0x51C]
 
 	/* 1) Configure SDRAM  */
-	ldr	r6, =SDRAM_CONFIG
-	str	r6, [r3, #0x400]
+	ldr	r0, =SDRAM_CONFIG
+	str	r0, [r3, #0x400]
 
 	/* 2) Set SDRAM Control reg */
-	ldr	r6, =SDRAM_CONTROL
-	str	r6, [r3, #0x404]
+	ldr	r0, =SDRAM_CONTROL
+	str	r0, [r3, #0x404]
 
 	/* 3) Write SDRAM address control register */
-	ldr	r6, =SDRAM_ADDR_CTRL
-	str	r6, [r3, #0x410]
+	ldr	r0, =SDRAM_ADDR_CTRL
+	str	r0, [r3, #0x410]
 
 	/* 4) Write SDRAM bank 0 size register */
-	ldr	r6, =SDRAM_BANK0_SIZE
-	str	r6, [r3, #0x504]
+	ldr	r0, =SDRAM_BANK0_SIZE
+	str	r0, [r3, #0x504]
 	/* keep other banks disabled */
 
 	/* 5) Write SDRAM open pages control register */
-	ldr	r6, =SDRAM_OPEN_PAGE_EN
-	str	r6, [r3, #0x414]
+	ldr	r0, =SDRAM_OPEN_PAGE_EN
+	str	r0, [r3, #0x414]
 
 	/* 6) Write SDRAM timing Low register */
-	ldr	r6, =SDRAM_TIME_CTRL_LOW
-	str	r6, [r3, #0x408]
+	ldr	r0, =SDRAM_TIME_CTRL_LOW
+	str	r0, [r3, #0x408]
 
 	/* 7) Write SDRAM timing High register */
-	ldr	r6, =SDRAM_TIME_CTRL_HI
-	str	r6, [r3, #0x40C]
+	ldr	r0, =SDRAM_TIME_CTRL_HI
+	str	r0, [r3, #0x40C]
 
 	/* 8) Write SDRAM mode register */
 	/* The CPU must not attempt to change the SDRAM Mode register setting */
@@ -142,73 +142,73 @@
 	/* and then sets SDRAM Mode register to its new value.		      */
 
 	/* 8.1 write 'nop' to SDRAM operation */
-	ldr	r6, =SDRAM_OP_NOP
-	str	r6, [r3, #0x418]
+	ldr	r0, =SDRAM_OP_NOP
+	str	r0, [r3, #0x418]
 
 	/* 8.2 poll SDRAM operation until back in 'normal' mode.  */
 1:
-	ldr	r6, [r3, #0x418]
-	cmp	r6, #0
+	ldr	r0, [r3, #0x418]
+	cmp	r0, #0
 	bne	1b
 
 	/* 8.3 Now its safe to write new value to SDRAM Mode register	      */
-	ldr	r6, =SDRAM_MODE
-	str	r6, [r3, #0x41C]
+	ldr	r0, =SDRAM_MODE
+	str	r0, [r3, #0x41C]
 
 	/* 8.4 Set new mode */
-	ldr	r6, =SDRAM_OP_SETMODE
-	str	r6, [r3, #0x418]
+	ldr	r0, =SDRAM_OP_SETMODE
+	str	r0, [r3, #0x418]
 
 	/* 8.5 poll SDRAM operation until back in 'normal' mode.  */
 2:
-	ldr	r6, [r3, #0x418]
-	cmp	r6, #0
+	ldr	r0, [r3, #0x418]
+	cmp	r0, #0
 	bne	2b
 
 	/* DDR SDRAM Address/Control Pads Calibration */
-	ldr	r6, [r3, #0x4C0]
+	ldr	r0, [r3, #0x4C0]
 
 	/* Set Bit [31] to make the register writable			*/
-	orr	r6, r6, #SDRAM_PAD_CTRL_WR_EN
-	str	r6, [r3, #0x4C0]
+	orr	r0, r0, #SDRAM_PAD_CTRL_WR_EN
+	str	r0, [r3, #0x4C0]
 
-	bic	r6, r6, #SDRAM_PAD_CTRL_WR_EN
-	bic	r6, r6, #SDRAM_PAD_CTRL_TUNE_EN
-	bic	r6, r6, #SDRAM_PAD_CTRL_DRVN_MASK
-	bic	r6, r6, #SDRAM_PAD_CTRL_DRVP_MASK
+	bic	r0, r0, #SDRAM_PAD_CTRL_WR_EN
+	bic	r0, r0, #SDRAM_PAD_CTRL_TUNE_EN
+	bic	r0, r0, #SDRAM_PAD_CTRL_DRVN_MASK
+	bic	r0, r0, #SDRAM_PAD_CTRL_DRVP_MASK
 
 	/* Get the final N locked value of driving strength [22:17]	*/
-	mov	r1, r6
+	mov	r1, r0
 	mov	r1, r1, LSL #9
 	mov	r1, r1, LSR #26	 /* r1[5:0]<DrvN>  = r3[22:17]<LockN>	*/
 	orr	r1, r1, r1, LSL #6 /* r1[11:6]<DrvP> = r1[5:0]<DrvN>	*/
 
 	/* Write to both <DrvN> bits [5:0] and <DrvP> bits [11:6]	*/
-	orr	r6, r6, r1
-	str	r6, [r3, #0x4C0]
+	orr	r0, r0, r1
+	str	r0, [r3, #0x4C0]
 
 	/* DDR SDRAM Data Pads Calibration				*/
-	ldr	r6, [r3, #0x4C4]
+	ldr	r0, [r3, #0x4C4]
 
 	/* Set Bit [31] to make the register writable			*/
-	orr	r6, r6, #SDRAM_PAD_CTRL_WR_EN
-	str	r6, [r3, #0x4C4]
+	orr	r0, r0, #SDRAM_PAD_CTRL_WR_EN
+	str	r0, [r3, #0x4C4]
 
-	bic	r6, r6, #SDRAM_PAD_CTRL_WR_EN
-	bic	r6, r6, #SDRAM_PAD_CTRL_TUNE_EN
-	bic	r6, r6, #SDRAM_PAD_CTRL_DRVN_MASK
-	bic	r6, r6, #SDRAM_PAD_CTRL_DRVP_MASK
+	bic	r0, r0, #SDRAM_PAD_CTRL_WR_EN
+	bic	r0, r0, #SDRAM_PAD_CTRL_TUNE_EN
+	bic	r0, r0, #SDRAM_PAD_CTRL_DRVN_MASK
+	bic	r0, r0, #SDRAM_PAD_CTRL_DRVP_MASK
 
 	/* Get the final N locked value of driving strength [22:17]	*/
-	mov	r1, r6
+	mov	r1, r0
 	mov	r1, r1, LSL #9
 	mov	r1, r1, LSR #26
 	orr	r1, r1, r1, LSL #6 /* r1[5:0] = r3[22:17]<LockN>	*/
 
 	/* Write to both <DrvN> bits [5:0] and <DrvP> bits [11:6]	*/
-	orr	r6, r6, r1
+	orr	r0, r0, r1
 
-	str	r6, [r3, #0x4C4]
+	str	r0, [r3, #0x4C4]
 
 	/* Implement Guideline (GL# MEM-3) Drive Strength Value		*/
 	/* Relevant for: 88F5181-A1/B0/B1 and 88F5281-A0/B0		*/
@@ -216,37 +216,37 @@
 	ldr	r1, =DDR1_PAD_STRENGTH_DEFAULT
 
 	/* Enable writes to DDR SDRAM Addr/Ctrl Pads Calibration register */
-	ldr	r6, [r3, #0x4C0]
-	orr	r6, r6, #SDRAM_PAD_CTRL_WR_EN
-	str	r6, [r3, #0x4C0]
+	ldr	r0, [r3, #0x4C0]
+	orr	r0, r0, #SDRAM_PAD_CTRL_WR_EN
+	str	r0, [r3, #0x4C0]
 
 	/* Correct strength and disable writes again */
-	bic	r6, r6, #SDRAM_PAD_CTRL_WR_EN
-	bic	r6, r6, #SDRAM_PAD_CTRL_DRV_STR_MASK
-	orr	r6, r6, r1
-	str	r6, [r3, #0x4C0]
+	bic	r0, r0, #SDRAM_PAD_CTRL_WR_EN
+	bic	r0, r0, #SDRAM_PAD_CTRL_DRV_STR_MASK
+	orr	r0, r0, r1
+	str	r0, [r3, #0x4C0]
 
 	/* Enable writes to DDR SDRAM Data Pads Calibration register */
-	ldr	r6, [r3, #0x4C4]
-	orr	r6, r6, #SDRAM_PAD_CTRL_WR_EN
-	str	r6, [r3, #0x4C4]
+	ldr	r0, [r3, #0x4C4]
+	orr	r0, r0, #SDRAM_PAD_CTRL_WR_EN
+	str	r0, [r3, #0x4C4]
 
 	/* Correct strength and disable writes again */
-	bic	r6, r6, #SDRAM_PAD_CTRL_DRV_STR_MASK
-	bic	r6, r6, #SDRAM_PAD_CTRL_WR_EN
-	orr	r6, r6, r1
-	str	r6, [r3, #0x4C4]
+	bic	r0, r0, #SDRAM_PAD_CTRL_DRV_STR_MASK
+	bic	r0, r0, #SDRAM_PAD_CTRL_WR_EN
+	orr	r0, r0, r1
+	str	r0, [r3, #0x4C4]
 
 	/* Implement Guideline (GL# MEM-4) DQS Reference Delay Tuning	*/
 	/* Relevant for: 88F5181-A1/B0/B1 and 88F5281-A0/B0		*/
 
 	/* Get the "sample on reset" register for the DDR frequancy	*/
 	ldr	r3, =0x10000
-	ldr	r6, [r3, #0x010]
+	ldr	r0, [r3, #0x010]
 	ldr	r1, =MSAR_ARMDDRCLCK_MASK
-	and	r1, r6, r1
+	and	r1, r0, r1
 
-	ldr	r6, =FTDLL_DDR1_166MHZ
+	ldr	r0, =FTDLL_DDR1_166MHZ
 	cmp	r1, #MSAR_ARMDDRCLCK_333_167
 	beq	3f
 	cmp	r1, #MSAR_ARMDDRCLCK_500_167
@@ -254,7 +254,7 @@
 	cmp	r1, #MSAR_ARMDDRCLCK_667_167
 	beq	3f
 
-	ldr	r6, =FTDLL_DDR1_200MHZ
+	ldr	r0, =FTDLL_DDR1_200MHZ
 	cmp	r1, #MSAR_ARMDDRCLCK_400_200_1
 	beq	3f
 	cmp	r1, #MSAR_ARMDDRCLCK_400_200
@@ -264,21 +264,21 @@
 	cmp	r1, #MSAR_ARMDDRCLCK_800_200
 	beq	3f
 
-	ldr	r6, =0
+	ldr	r0, =0
 
 3:
 	/* Use R3 as the base for DRAM registers */
-	add	r3, r4, #0x01000
+	add	r3, r2, #0x01000
 
 	ldr	r2, [r3, #0x484]
-	orr	r2, r2, r6
+	orr	r2, r2, r0
 	str	r2, [r3, #0x484]
 
 	/* enable for 2 GB DDR; detection should find out real amount */
-	sub	r6, r6, r6
-	str	r6, [r3, #0x500]
-	ldr	r6, =0x7fff0001
-	str	r6, [r3, #0x504]
+	sub	r0, r0, r0
+	str	r0, [r3, #0x500]
+	ldr	r0, =0x7fff0001
+	str	r0, [r3, #0x504]
 
 #endif /* CONFIG_SPL_BUILD */
 
diff --git a/arch/arm/mach-stm32/Kconfig b/arch/arm/mach-stm32/Kconfig
index 1353284..759f545 100644
--- a/arch/arm/mach-stm32/Kconfig
+++ b/arch/arm/mach-stm32/Kconfig
@@ -48,6 +48,7 @@
 	imply SPL_OS_BOOT
 	select SPL_PINCTRL
 	select SPL_RAM
+	select SPL_RESET_SUPPORT
 	select SPL_SERIAL_SUPPORT
 	select SPL_SYS_MALLOC_SIMPLE
 	select SPL_TIMER
diff --git a/board/CZ.NIC/turris_omnia/turris_omnia.c b/board/CZ.NIC/turris_omnia/turris_omnia.c
index da663cf..160d30c 100644
--- a/board/CZ.NIC/turris_omnia/turris_omnia.c
+++ b/board/CZ.NIC/turris_omnia/turris_omnia.c
@@ -321,7 +321,11 @@
 	writel(OMNIA_GPP_OUT_ENA_LOW, MVEBU_GPIO0_BASE + 0x04);
 	writel(OMNIA_GPP_OUT_ENA_MID, MVEBU_GPIO1_BASE + 0x04);
 
-	/* Disable I2C debug mode blocking 0x64 I2C address */
+	/*
+	 * Disable I2C debug mode blocking 0x64 I2C address.
+	 * Note: that would be redundant once Turris Omnia migrates to DM_I2C,
+	 * because the mvtwsi driver includes equivalent code.
+	 */
 	i2c_debug_reg = readl(MVEBU_TWSI_BASE + MVTWSI_ARMADA_DEBUG_REG);
 	i2c_debug_reg &= ~(1<<18);
 	writel(i2c_debug_reg, MVEBU_TWSI_BASE + MVTWSI_ARMADA_DEBUG_REG);
diff --git a/board/lego/ev3/README b/board/lego/ev3/README
index 1a50ca9..da62a64 100644
--- a/board/lego/ev3/README
+++ b/board/lego/ev3/README
@@ -9,14 +9,34 @@
 =======
 
 The EV3 contains a bootloader in EEPROM that loads u-boot.bin from address 0x0
-of the spi flash memory. Using the default configuration, u-boot will check to
-see if there is a boot.scr file on the first FAT partition of the mmc. If there
-is, it will run the script and boot the kernel from the uImage file also in
-the FAT partition. Otherwise, it will load a kernel and rootfs from the flash.
-The kernel must be stored at address 0x50000 on the flash and have a maximum
-size of 3MiB. The rootfs must be a squasfs image and stored at 0x350000 in the
-flash and have a maximum size of 9.3MiB. The flash starting at 0xCB0000 is
-reserved for user data.
+of the SPI flash memory (with a size of 256KiB!). Because the EEPROM is read-
+only and it takes care of low level configuration (PLL and DDR), we don't use
+U-Boot to produce an SPL image.
+
+Using the default configuration, U-Boot had a boot scrips that works as follows:
+
+* Check to see if microSD card is present
+* If it is, try to load boot.scr from the first FAT partition
+* If loading boot.scr was successful, run it
+* Otherwise, try loading uEnv.txt
+* If loading uEnv.txt was successful, import it
+* If there is a uenvcmd variable (from uEnv.txt), run it
+* Try to load uImage from the first FAT partition
+* If it was successful, try to load da850-lego-ev3.dtb
+* If loading uImage was successful, boot it (DT is optional)
+* If none of the above was successful, try booting from flash
+
+Suggested Flash Memory Layout
+=============================
+
+The following is based on the default U-Boot configuration:
+
+| Image (file)       | Start Addr. | Max. Size         |
++--------------------+-------------+-------------------+
+| u-boot.bin         |         0x0 |  0x40000 (256KiB) |
+| da850-lego-ev3.dtb |     0x40000 |  0x10000 (64KiB)  |
+| uImage             |     0x50000 | 0x400000 (4MiB)   |
+| rootfs (squashfs)  |    0x450000 | 0xa00000 (10MiB)  |
 
 Writing image to flash
 ======================
diff --git a/board/lego/ev3/legoev3.c b/board/lego/ev3/legoev3.c
index 5e70363..423c2fa 100644
--- a/board/lego/ev3/legoev3.c
+++ b/board/lego/ev3/legoev3.c
@@ -14,8 +14,6 @@
 
 #include <common.h>
 #include <i2c.h>
-#include <net.h>
-#include <netdev.h>
 #include <spi.h>
 #include <spi_flash.h>
 #include <asm/arch/hardware.h>
@@ -132,6 +130,11 @@
 
 int board_early_init_f(void)
 {
+	/* enable the console UART */
+	writel((DAVINCI_UART_PWREMU_MGMT_FREE | DAVINCI_UART_PWREMU_MGMT_URRST |
+		DAVINCI_UART_PWREMU_MGMT_UTRST),
+	       &davinci_uart1_ctrl_regs->pwremu_mgmt);
+
 	/*
 	 * Power on required peripherals
 	 * ARM does not have access by default to PSC0 and PSC1
@@ -157,7 +160,7 @@
 
 	/* setup the SUSPSRC for ARM to control emulation suspend */
 	writel(readl(&davinci_syscfg_regs->suspsrc) &
-	       ~(DAVINCI_SYSCFG_SUSPSRC_EMAC | DAVINCI_SYSCFG_SUSPSRC_I2C |
+	       ~(DAVINCI_SYSCFG_SUSPSRC_I2C |
 		 DAVINCI_SYSCFG_SUSPSRC_SPI0 | DAVINCI_SYSCFG_SUSPSRC_TIMER0 |
 		 DAVINCI_SYSCFG_SUSPSRC_UART1),
 	       &davinci_syscfg_regs->suspsrc);
@@ -166,10 +169,5 @@
 	if (davinci_configure_pin_mux_items(pinmuxes, ARRAY_SIZE(pinmuxes)))
 		return 1;
 
-	/* enable the console UART */
-	writel((DAVINCI_UART_PWREMU_MGMT_FREE | DAVINCI_UART_PWREMU_MGMT_URRST |
-		DAVINCI_UART_PWREMU_MGMT_UTRST),
-	       &davinci_uart1_ctrl_regs->pwremu_mgmt);
-
 	return 0;
 }
diff --git a/board/samsung/common/exynos5-dt.c b/board/samsung/common/exynos5-dt.c
index 9f6f654..8c3a9ea 100644
--- a/board/samsung/common/exynos5-dt.c
+++ b/board/samsung/common/exynos5-dt.c
@@ -164,7 +164,7 @@
 		samsung_get_base_usb3_phy();
 
 	if (!phy) {
-		pr_err("usb3 phy not supported");
+		pr_err("usb3 phy not supported\n");
 		return -ENODEV;
 	}
 
diff --git a/board/samsung/common/misc.c b/board/samsung/common/misc.c
index c9df7e6..05243fc 100644
--- a/board/samsung/common/misc.c
+++ b/board/samsung/common/misc.c
@@ -456,7 +456,7 @@
 
 	addr = panel_info.logo_addr;
 	if (!addr) {
-		pr_err("There is no logo data.");
+		pr_err("There is no logo data.\n");
 		return;
 	}
 
diff --git a/board/samsung/odroid/odroid.c b/board/samsung/odroid/odroid.c
index 1c2bd01..552333f 100644
--- a/board/samsung/odroid/odroid.c
+++ b/board/samsung/odroid/odroid.c
@@ -428,7 +428,7 @@
 	};
 
 	if (regulator_list_autoset(mmc_regulators, NULL, true))
-		pr_err("Unable to init all mmc regulators");
+		pr_err("Unable to init all mmc regulators\n");
 
 	return 0;
 }
@@ -441,7 +441,7 @@
 
 	ret = regulator_get_by_platname("VDD_UOTG_3.0V", &dev);
 	if (ret) {
-		pr_err("Regulator get error: %d", ret);
+		pr_err("Regulator get error: %d\n", ret);
 		return ret;
 	}
 
@@ -486,25 +486,25 @@
 
 	ret = regulator_get_by_platname("VCC_P3V3_2.85V", &dev);
 	if (ret) {
-		pr_err("Regulator get error: %d", ret);
+		pr_err("Regulator get error: %d\n", ret);
 		return ret;
 	}
 
 	ret = regulator_set_enable(dev, true);
 	if (ret) {
-		pr_err("Regulator %s enable setting error: %d", dev->name, ret);
+		pr_err("Regulator %s enable setting error: %d\n", dev->name, ret);
 		return ret;
 	}
 
 	ret = regulator_set_value(dev, 750000);
 	if (ret) {
-		pr_err("Regulator %s value setting error: %d", dev->name, ret);
+		pr_err("Regulator %s value setting error: %d\n", dev->name, ret);
 		return ret;
 	}
 
 	ret = regulator_set_value(dev, 3300000);
 	if (ret) {
-		pr_err("Regulator %s value setting error: %d", dev->name, ret);
+		pr_err("Regulator %s value setting error: %d\n", dev->name, ret);
 		return ret;
 	}
 #endif
diff --git a/cmd/fastboot.c b/cmd/fastboot.c
index 557257a..e6ae057 100644
--- a/cmd/fastboot.c
+++ b/cmd/fastboot.c
@@ -48,7 +48,7 @@
 
 	ret = board_usb_init(controller_index, USB_INIT_DEVICE);
 	if (ret) {
-		pr_err("USB init failed: %d", ret);
+		pr_err("USB init failed: %d\n", ret);
 		return CMD_RET_FAILURE;
 	}
 
diff --git a/cmd/iotrace.c b/cmd/iotrace.c
index e496787..601b8c8 100644
--- a/cmd/iotrace.c
+++ b/cmd/iotrace.c
@@ -15,6 +15,9 @@
 	iotrace_get_buffer(&start, &size, &offset, &count);
 	printf("Start:  %08lx\n", start);
 	printf("Size:   %08lx\n", size);
+	iotrace_get_region(&start, &size);
+	printf("Region: %08lx\n", start);
+	printf("Size:   %08lx\n", size);
 	printf("Offset: %08lx\n", offset);
 	printf("Output: %08lx\n", start + offset);
 	printf("Count:  %08lx\n", count);
@@ -37,6 +40,22 @@
 	return 0;
 }
 
+static int do_set_region(int argc, char * const argv[])
+{
+	ulong addr = 0, size = 0;
+
+	if (argc == 2) {
+		addr = simple_strtoul(*argv++, NULL, 16);
+		size = simple_strtoul(*argv++, NULL, 16);
+	} else if (argc != 0) {
+		return CMD_RET_USAGE;
+	}
+
+	iotrace_set_region(addr, size);
+
+	return 0;
+}
+
 int do_iotrace(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[])
 {
 	const char *cmd = argc < 2 ? NULL : argv[1];
@@ -46,6 +65,8 @@
 	switch (*cmd) {
 	case 'b':
 		return do_set_buffer(argc - 2, argv + 2);
+	case 'l':
+		return do_set_region(argc - 2, argv + 2);
 	case 'p':
 		iotrace_set_enabled(0);
 		break;
@@ -67,6 +88,7 @@
 	"iotrace utility commands",
 	"stats                        - display iotrace stats\n"
 	"iotrace buffer <address> <size>      - set iotrace buffer\n"
+	"iotrace limit <address> <size>       - set iotrace region limit\n"
 	"iotrace pause                        - pause tracing\n"
 	"iotrace resume                       - resume tracing"
 );
diff --git a/cmd/pxe.c b/cmd/pxe.c
index 7649d92..5609545 100644
--- a/cmd/pxe.c
+++ b/cmd/pxe.c
@@ -1453,8 +1453,8 @@
 	/*
 	 * Create a menu and add items for all the labels.
 	 */
-	m = menu_create(cfg->title, cfg->timeout, cfg->prompt, label_print,
-			NULL, NULL);
+	m = menu_create(cfg->title, DIV_ROUND_UP(cfg->timeout, 10),
+			cfg->prompt, label_print, NULL, NULL);
 
 	if (!m)
 		return NULL;
diff --git a/cmd/regulator.c b/cmd/regulator.c
index 89461b6..ed8d778 100644
--- a/cmd/regulator.c
+++ b/cmd/regulator.c
@@ -70,7 +70,7 @@
 
 	*uc_pdata = dev_get_uclass_platdata(*devp);
 	if (!*uc_pdata) {
-		pr_err("Regulator: %s - missing platform data!", currdev->name);
+		pr_err("Regulator: %s - missing platform data!\n", currdev->name);
 		return CMD_RET_FAILURE;
 	}
 
diff --git a/cmd/thordown.c b/cmd/thordown.c
index e297de2..2615ada 100644
--- a/cmd/thordown.c
+++ b/cmd/thordown.c
@@ -32,7 +32,7 @@
 	int controller_index = simple_strtoul(usb_controller, NULL, 0);
 	ret = board_usb_init(controller_index, USB_INIT_DEVICE);
 	if (ret) {
-		pr_err("USB init failed: %d", ret);
+		pr_err("USB init failed: %d\n", ret);
 		ret = CMD_RET_FAILURE;
 		goto exit;
 	}
@@ -41,14 +41,14 @@
 
 	ret = thor_init();
 	if (ret) {
-		pr_err("THOR DOWNLOAD failed: %d", ret);
+		pr_err("THOR DOWNLOAD failed: %d\n", ret);
 		ret = CMD_RET_FAILURE;
 		goto exit;
 	}
 
 	ret = thor_handle();
 	if (ret) {
-		pr_err("THOR failed: %d", ret);
+		pr_err("THOR failed: %d\n", ret);
 		ret = CMD_RET_FAILURE;
 		goto exit;
 	}
diff --git a/cmd/usb_mass_storage.c b/cmd/usb_mass_storage.c
index 89b9ddf..0d55114 100644
--- a/cmd/usb_mass_storage.c
+++ b/cmd/usb_mass_storage.c
@@ -161,21 +161,21 @@
 	controller_index = (unsigned int)(simple_strtoul(
 				usb_controller,	NULL, 0));
 	if (board_usb_init(controller_index, USB_INIT_DEVICE)) {
-		pr_err("Couldn't init USB controller.");
+		pr_err("Couldn't init USB controller.\n");
 		rc = CMD_RET_FAILURE;
 		goto cleanup_ums_init;
 	}
 
 	rc = fsg_init(ums, ums_count);
 	if (rc) {
-		pr_err("fsg_init failed");
+		pr_err("fsg_init failed\n");
 		rc = CMD_RET_FAILURE;
 		goto cleanup_board;
 	}
 
 	rc = g_dnl_register("usb_dnl_ums");
 	if (rc) {
-		pr_err("g_dnl_register failed");
+		pr_err("g_dnl_register failed\n");
 		rc = CMD_RET_FAILURE;
 		goto cleanup_board;
 	}
diff --git a/common/board_f.c b/common/board_f.c
index fa667c7..e943347 100644
--- a/common/board_f.c
+++ b/common/board_f.c
@@ -394,19 +394,21 @@
 
 static int reserve_uboot(void)
 {
-	/*
-	 * reserve memory for U-Boot code, data & bss
-	 * round down to next 4 kB limit
-	 */
-	gd->relocaddr -= gd->mon_len;
-	gd->relocaddr &= ~(4096 - 1);
-#if defined(CONFIG_E500) || defined(CONFIG_MIPS)
-	/* round down to next 64 kB limit so that IVPR stays aligned */
-	gd->relocaddr &= ~(65536 - 1);
-#endif
+	if (!(gd->flags & GD_FLG_SKIP_RELOC)) {
+		/*
+		 * reserve memory for U-Boot code, data & bss
+		 * round down to next 4 kB limit
+		 */
+		gd->relocaddr -= gd->mon_len;
+		gd->relocaddr &= ~(4096 - 1);
+	#if defined(CONFIG_E500) || defined(CONFIG_MIPS)
+		/* round down to next 64 kB limit so that IVPR stays aligned */
+		gd->relocaddr &= ~(65536 - 1);
+	#endif
 
-	debug("Reserving %ldk for U-Boot at: %08lx\n", gd->mon_len >> 10,
-	      gd->relocaddr);
+		debug("Reserving %ldk for U-Boot at: %08lx\n",
+		      gd->mon_len >> 10, gd->relocaddr);
+	}
 
 	gd->start_addr_sp = gd->relocaddr;
 
diff --git a/common/iotrace.c b/common/iotrace.c
index b16b0d6..2f03a60 100644
--- a/common/iotrace.c
+++ b/common/iotrace.c
@@ -27,11 +27,13 @@
  * struct iotrace_record - Holds a single I/O trace record
  *
  * @flags: I/O access type
+ * @timestamp: Timestamp of access
  * @addr: Address of access
  * @value: Value written or read
  */
 struct iotrace_record {
 	enum iotrace_flags flags;
+	u64 timestamp;
 	phys_addr_t addr;
 	iovalue_t value;
 };
@@ -42,6 +44,8 @@
  * @start:	Start address of iotrace buffer
  * @size:	Size of iotrace buffer in bytes
  * @offset:	Current write offset into iotrace buffer
+ * @region_start: Address of IO region to trace
+ * @region_size: Size of region to trace. if 0 will trace all address space
  * @crc32:	Current value of CRC chceksum of trace records
  * @enabled:	true if enabled, false if disabled
  */
@@ -49,6 +53,8 @@
 	ulong start;
 	ulong size;
 	ulong offset;
+	ulong region_start;
+	ulong region_size;
 	u32 crc32;
 	bool enabled;
 } iotrace;
@@ -66,13 +72,18 @@
 	if (!(gd->flags & GD_FLG_RELOC) || !iotrace.enabled)
 		return;
 
+	if (iotrace.region_size)
+		if ((ulong)ptr < iotrace.region_start ||
+		    (ulong)ptr > iotrace.region_start + iotrace.region_size)
+			return;
+
 	/* Store it if there is room */
 	if (iotrace.offset + sizeof(*rec) < iotrace.size) {
 		rec = (struct iotrace_record *)map_sysmem(
 					iotrace.start + iotrace.offset,
 					sizeof(value));
 	}
-
+	rec->timestamp = timer_get_us();
 	rec->flags = flags;
 	rec->addr = map_to_sysmem(ptr);
 	rec->value = value;
@@ -142,6 +153,24 @@
 	return iotrace.crc32;
 }
 
+void iotrace_set_region(ulong start, ulong size)
+{
+	iotrace.region_start = start;
+	iotrace.region_size = size;
+}
+
+void iotrace_reset_region(void)
+{
+	iotrace.region_start = 0;
+	iotrace.region_size = 0;
+}
+
+void iotrace_get_region(ulong *start, ulong *size)
+{
+	*start = iotrace.region_start;
+	*size = iotrace.region_size;
+}
+
 void iotrace_set_enabled(int enable)
 {
 	iotrace.enabled = enable;
diff --git a/common/menu.c b/common/menu.c
index bf2b471..0f0a29a 100644
--- a/common/menu.c
+++ b/common/menu.c
@@ -194,8 +194,7 @@
 
 		if (!m->item_choice) {
 			readret = cli_readline_into_buffer("Enter choice: ",
-							   cbuf,
-							   m->timeout / 10);
+							   cbuf, m->timeout);
 
 			if (readret >= 0) {
 				choice_item = menu_item_by_key(m, cbuf);
diff --git a/configs/legoev3_defconfig b/configs/legoev3_defconfig
index c5f54a9..8ee2d22 100644
--- a/configs/legoev3_defconfig
+++ b/configs/legoev3_defconfig
@@ -18,9 +18,7 @@
 CONFIG_CMD_SF=y
 CONFIG_CMD_SPI=y
 # CONFIG_CMD_SETEXPR is not set
-CONFIG_CMD_DHCP=y
-CONFIG_CMD_MII=y
-CONFIG_CMD_PING=y
+# CONFIG_CMD_NET is not set
 CONFIG_CMD_EXT4=y
 CONFIG_CMD_FAT=y
 CONFIG_CMD_DIAG=y
diff --git a/disk/part_efi.c b/disk/part_efi.c
index 72e3997..5c1039f 100644
--- a/disk/part_efi.c
+++ b/disk/part_efi.c
@@ -350,8 +350,6 @@
 {
 	/* Setup the Protective MBR */
 	ALLOC_CACHE_ALIGN_BUFFER_PAD(legacy_mbr, p_mbr, 1, dev_desc->blksz);
-	memset(p_mbr, 0, sizeof(*p_mbr));
-
 	if (p_mbr == NULL) {
 		printf("%s: calloc failed!\n", __func__);
 		return -1;
@@ -363,6 +361,10 @@
 		return -1;
 	}
 
+	/* Clear all data in MBR except of backed up boot code */
+	memset((char *)p_mbr + MSDOS_MBR_BOOT_CODE_SIZE, 0, sizeof(*p_mbr) -
+			MSDOS_MBR_BOOT_CODE_SIZE);
+
 	/* Append signature */
 	p_mbr->signature = MSDOS_MBR_SIGNATURE;
 	p_mbr->partition_record[0].sys_ind = EFI_PMBR_OSTYPE_EFI_GPT;
diff --git a/drivers/block/Kconfig b/drivers/block/Kconfig
index 15fd1bc..0792373 100644
--- a/drivers/block/Kconfig
+++ b/drivers/block/Kconfig
@@ -29,7 +29,8 @@
 
 config BLOCK_CACHE
 	bool "Use block device cache"
-	default n
+	depends on BLK
+	default y
 	help
 	  This option enables a disk-block cache for all block devices.
 	  This is most useful when accessing filesystems under U-Boot since
diff --git a/drivers/i2c/mvtwsi.c b/drivers/i2c/mvtwsi.c
index f9822e5..74ac0a4 100644
--- a/drivers/i2c/mvtwsi.c
+++ b/drivers/i2c/mvtwsi.c
@@ -11,6 +11,7 @@
 #include <i2c.h>
 #include <linux/errno.h>
 #include <asm/io.h>
+#include <linux/bitops.h>
 #include <linux/compat.h>
 #ifdef CONFIG_DM_I2C
 #include <dm.h>
@@ -57,6 +58,7 @@
 	u32 status;
 	u32 baudrate;
 	u32 soft_reset;
+	u32 debug; /* Dummy field for build compatibility with mvebu */
 };
 
 #else
@@ -70,8 +72,10 @@
 		u32 baudrate;	/* When writing */
 	};
 	u32 xtnd_slave_addr;
-	u32 reserved[2];
+	u32 reserved0[2];
 	u32 soft_reset;
+	u32 reserved1[27];
+	u32 debug;
 };
 
 #endif
@@ -795,6 +799,23 @@
 	return 0;
 }
 
+static void twsi_disable_i2c_slave(struct mvtwsi_registers *twsi)
+{
+	clrbits_le32(&twsi->debug, BIT(18));
+}
+
+static int mvtwsi_i2c_bind(struct udevice *bus)
+{
+	struct mvtwsi_registers *twsi = devfdt_get_addr_ptr(bus);
+
+	/* Disable the hidden slave in i2c0 of these platforms */
+	if ((IS_ENABLED(CONFIG_ARMADA_38X) || IS_ENABLED(CONFIG_KIRKWOOD))
+			&& bus->req_seq == 0)
+		twsi_disable_i2c_slave(twsi);
+
+	return 0;
+}
+
 static int mvtwsi_i2c_probe(struct udevice *bus)
 {
 	struct mvtwsi_i2c_dev *dev = dev_get_priv(bus);
@@ -850,6 +871,7 @@
 	.name = "i2c_mvtwsi",
 	.id = UCLASS_I2C,
 	.of_match = mvtwsi_i2c_ids,
+	.bind = mvtwsi_i2c_bind,
 	.probe = mvtwsi_i2c_probe,
 	.ofdata_to_platdata = mvtwsi_i2c_ofdata_to_platdata,
 	.priv_auto_alloc_size = sizeof(struct mvtwsi_i2c_dev),
diff --git a/drivers/misc/stm32_rcc.c b/drivers/misc/stm32_rcc.c
index dee82c0..980b3a5 100644
--- a/drivers/misc/stm32_rcc.c
+++ b/drivers/misc/stm32_rcc.c
@@ -53,13 +53,9 @@
 	if (ret)
 		return ret;
 
-#ifdef CONFIG_SPL_BUILD
-	return 0;
-#else
 	return device_bind_driver_to_node(dev, "stm32_rcc_reset",
 					  "stm32_rcc_reset",
 					  dev_ofnode(dev), &child);
-#endif
 }
 
 static const struct misc_ops stm32_rcc_ops = {
diff --git a/drivers/mtd/spi/spi_flash.c b/drivers/mtd/spi/spi_flash.c
index 0ed2317..c159124 100644
--- a/drivers/mtd/spi/spi_flash.c
+++ b/drivers/mtd/spi/spi_flash.c
@@ -128,6 +128,7 @@
 	if (flash->bank_curr == 0)
 		return 0;
 	cmd = flash->bank_write_cmd;
+	flash->bank_curr = 0;
 
 	return spi_flash_write_common(flash, &cmd, 1, &bank_sel, 1);
 }
diff --git a/drivers/mtd/spi/spi_flash_ids.c b/drivers/mtd/spi/spi_flash_ids.c
index 5d146e3..c45d2e8 100644
--- a/drivers/mtd/spi/spi_flash_ids.c
+++ b/drivers/mtd/spi/spi_flash_ids.c
@@ -63,6 +63,7 @@
 #endif
 #ifdef CONFIG_SPI_FLASH_GIGADEVICE	/* GIGADEVICE */
 	{"gd25q64b",	   INFO(0xc84017, 0x0, 64 * 1024,   128, SECT_4K) },
+	{"gd25q32b",       INFO(0xc84016, 0x0, 64 * 1024,    64, SECT_4K) },
 	{"gd25lq32",	   INFO(0xc86016, 0x0, 64 * 1024,    64, SECT_4K) },
 #endif
 #ifdef CONFIG_SPI_FLASH_ISSI		/* ISSI */
diff --git a/drivers/rtc/Kconfig b/drivers/rtc/Kconfig
index 277dc3d..a3f8c8a 100644
--- a/drivers/rtc/Kconfig
+++ b/drivers/rtc/Kconfig
@@ -48,6 +48,13 @@
 	help
 	  Support for Epson RX8010SJ Real Time Clock devices.
 
+config RTC_MV
+	bool "Enable Marvell RTC driver"
+	depends on DM_RTC
+	help
+	  Enable Marvell RTC driver. This driver supports the rtc that is present
+	  on some Marvell SoCs.
+
 config RTC_S35392A
 	bool "Enable S35392A driver"
 	select BITREVERSE
diff --git a/drivers/rtc/mvrtc.c b/drivers/rtc/mvrtc.c
index f2a2266..94a0653 100644
--- a/drivers/rtc/mvrtc.c
+++ b/drivers/rtc/mvrtc.c
@@ -10,6 +10,7 @@
 
 #include <common.h>
 #include <command.h>
+#include <dm.h>
 #include <rtc.h>
 #include <asm/io.h>
 #include "mvrtc.h"
@@ -17,19 +18,16 @@
 /* This RTC does not support century, so we assume 20 */
 #define CENTURY 20
 
-int rtc_get(struct rtc_time *t)
+static int __mv_rtc_get(struct mvrtc_registers *regs, struct rtc_time *t)
 {
 	u32 time;
 	u32 date;
-	struct mvrtc_registers *mvrtc_regs;
-
-	mvrtc_regs = (struct mvrtc_registers *)KW_RTC_BASE;
 
 	/* read the time register */
-	time = readl(&mvrtc_regs->time);
+	time = readl(&regs->time);
 
 	/* read the date register */
-	date = readl(&mvrtc_regs->date);
+	date = readl(&regs->date);
 
 	/* test for 12 hour clock (can't tell if it's am/pm) */
 	if (time & MVRTC_HRFMT_MSK) {
@@ -57,13 +55,20 @@
 	return 0;
 }
 
-int rtc_set(struct rtc_time *t)
+#ifndef CONFIG_DM_RTC
+int rtc_get(struct rtc_time *t)
+{
+	struct mvrtc_registers *regs;
+
+	regs = (struct mvrtc_registers *)KW_RTC_BASE;
+	return __mv_rtc_get(regs, t);
+}
+#endif /* !CONFIG_DM_RTC */
+
+static int __mv_rtc_set(struct mvrtc_registers *regs, const struct rtc_time *t)
 {
 	u32 time = 0; /* sets hour format bit to zero, 24hr format. */
 	u32 date = 0;
-	struct mvrtc_registers *mvrtc_regs;
-
-	mvrtc_regs = (struct mvrtc_registers *)KW_RTC_BASE;
 
 	/* check that this code isn't 80+ years old ;-) */
 	if ((t->tm_year / 100) != CENTURY)
@@ -81,28 +86,100 @@
 	date |= (bin2bcd(t->tm_year % 100) & MVRTC_YEAR_MSK) << MVRTC_YEAR_SFT;
 
 	/* write the time register */
-	writel(time, &mvrtc_regs->time);
+	writel(time, &regs->time);
 
 	/* write the date register */
-	writel(date, &mvrtc_regs->date);
+	writel(date, &regs->date);
 
 	return 0;
 }
 
-void rtc_reset(void)
+#ifndef CONFIG_DM_RTC
+int rtc_set(struct rtc_time *t)
+{
+	struct mvrtc_registers *regs;
+
+	regs = (struct mvrtc_registers *)KW_RTC_BASE;
+	return __mv_rtc_set(regs, t);
+}
+#endif /* !CONFIG_DM_RTC */
+
+static void __mv_rtc_reset(struct mvrtc_registers *regs)
 {
 	u32 time;
 	u32 sec;
-	struct mvrtc_registers *mvrtc_regs;
-
-	mvrtc_regs = (struct mvrtc_registers *)KW_RTC_BASE;
 
 	/* no init routine for this RTC needed, just check that it's working */
-	time = readl(&mvrtc_regs->time);
+	time = readl(&regs->time);
 	sec  = bcd2bin((time >> MVRTC_SEC_SFT) & MVRTC_SEC_MSK);
 	udelay(1000000);
-	time = readl(&mvrtc_regs->time);
+	time = readl(&regs->time);
 
 	if (sec == bcd2bin((time >> MVRTC_SEC_SFT) & MVRTC_SEC_MSK))
 		printf("Error: RTC did not increment.\n");
 }
+
+#ifndef CONFIG_DM_RTC
+void rtc_reset(void)
+{
+	struct mvrtc_registers *regs;
+
+	regs = (struct mvrtc_registers *)KW_RTC_BASE;
+	__mv_rtc_reset(regs);
+}
+#endif /* !CONFIG_DM_RTC */
+
+#ifdef CONFIG_DM_RTC
+static int mv_rtc_get(struct udevice *dev, struct rtc_time *tm)
+{
+	struct mvrtc_pdata *pdata = dev_get_platdata(dev);
+	struct mvrtc_registers *regs = (struct mvrtc_registers *)pdata->iobase;
+
+	return __mv_rtc_get(regs, tm);
+}
+
+static int mv_rtc_set(struct udevice *dev, const struct rtc_time *tm)
+{
+	struct mvrtc_pdata *pdata = dev_get_platdata(dev);
+	struct mvrtc_registers *regs = (struct mvrtc_registers *)pdata->iobase;
+
+	return __mv_rtc_set(regs, tm);
+}
+
+static int mv_rtc_reset(struct udevice *dev)
+{
+	struct mvrtc_pdata *pdata = dev_get_platdata(dev);
+	struct mvrtc_registers *regs = (struct mvrtc_registers *)pdata->iobase;
+
+	__mv_rtc_reset(regs);
+	return 0;
+}
+
+static const struct rtc_ops mv_rtc_ops = {
+	.get = mv_rtc_get,
+	.set = mv_rtc_set,
+	.reset = mv_rtc_reset,
+};
+
+static const struct udevice_id mv_rtc_ids[] = {
+	{ .compatible = "marvell,kirkwood-rtc" },
+	{ .compatible = "marvell,orion-rtc" },
+	{ }
+};
+
+static int mv_rtc_ofdata_to_platdata(struct udevice *dev)
+{
+	struct mvrtc_pdata *pdata = dev_get_platdata(dev);
+
+	pdata->iobase = devfdt_get_addr(dev);
+	return 0;
+}
+
+U_BOOT_DRIVER(rtc_mv) = {
+	.name	= "rtc-mv",
+	.id	= UCLASS_RTC,
+	.ofdata_to_platdata = mv_rtc_ofdata_to_platdata,
+	.of_match = mv_rtc_ids,
+	.ops	= &mv_rtc_ops,
+};
+#endif /* CONFIG_DM_RTC */
diff --git a/drivers/rtc/mvrtc.h b/drivers/rtc/mvrtc.h
index dc470a9..87ff432 100644
--- a/drivers/rtc/mvrtc.h
+++ b/drivers/rtc/mvrtc.h
@@ -20,6 +20,11 @@
 	u32 date;
 };
 
+/* Platform data */
+struct mvrtc_pdata {
+	phys_addr_t iobase;
+};
+
 /* time register */
 #define MVRTC_SEC_SFT		0
 #define MVRTC_SEC_MSK		0x7f
diff --git a/drivers/spi/cadence_qspi.c b/drivers/spi/cadence_qspi.c
index b88837c..91742ba 100644
--- a/drivers/spi/cadence_qspi.c
+++ b/drivers/spi/cadence_qspi.c
@@ -283,18 +283,9 @@
 	const void *blob = gd->fdt_blob;
 	int node = dev_of_offset(bus);
 	int subnode;
-	u32 data[4];
-	int ret;
 
-	/* 2 base addresses are needed, lets get them from the DT */
-	ret = fdtdec_get_int_array(blob, node, "reg", data, ARRAY_SIZE(data));
-	if (ret) {
-		printf("Error: Can't get base addresses (ret=%d)!\n", ret);
-		return -ENODEV;
-	}
-
-	plat->regbase = (void *)data[0];
-	plat->ahbbase = (void *)data[2];
+	plat->regbase = (void *)devfdt_get_addr_index(bus, 0);
+	plat->ahbbase = (void *)devfdt_get_addr_index(bus, 1);
 	plat->is_decoded_cs = fdtdec_get_bool(blob, node, "cdns,is-decoded-cs");
 	plat->fifo_depth = fdtdec_get_uint(blob, node, "cdns,fifo-depth", 128);
 	plat->fifo_width = fdtdec_get_uint(blob, node, "cdns,fifo-width", 4);
diff --git a/drivers/usb/dwc3/linux-compat.h b/drivers/usb/dwc3/linux-compat.h
index 35850f9..8279376 100644
--- a/drivers/usb/dwc3/linux-compat.h
+++ b/drivers/usb/dwc3/linux-compat.h
@@ -11,7 +11,6 @@
 #ifndef __DWC3_LINUX_COMPAT__
 #define __DWC3_LINUX_COMPAT__
 
-#define WARN(val, format, arg...)	debug(format, ##arg)
 #define dev_WARN(dev, format, arg...)	debug(format, ##arg)
 
 static inline size_t strlcat(char *dest, const char *src, size_t n)
diff --git a/drivers/usb/musb-new/linux-compat.h b/drivers/usb/musb-new/linux-compat.h
index 7bb53d2..f366ae5 100644
--- a/drivers/usb/musb-new/linux-compat.h
+++ b/drivers/usb/musb-new/linux-compat.h
@@ -5,12 +5,6 @@
 #include <linux/list.h>
 #include <linux/compat.h>
 
-#define WARN(condition, fmt, args...) ({	\
-	int ret_warn = !!condition;		\
-	if (ret_warn)				\
-		printf(fmt, ##args);		\
-	ret_warn; })
-
 #define device_init_wakeup(dev, a) do {} while (0)
 
 #define platform_data device_data
diff --git a/include/configs/legoev3.h b/include/configs/legoev3.h
index 542b8f0..7a0511f 100644
--- a/include/configs/legoev3.h
+++ b/include/configs/legoev3.h
@@ -39,61 +39,6 @@
 
 #define CONFIG_NR_DRAM_BANKS	1 /* we have 1 bank of DRAM */
 
-#define CONFIG_SYS_DA850_SYSCFG_SUSPSRC (	\
-	DAVINCI_SYSCFG_SUSPSRC_TIMER0 |		\
-	DAVINCI_SYSCFG_SUSPSRC_SPI0 |		\
-	DAVINCI_SYSCFG_SUSPSRC_UART1 |		\
-	DAVINCI_SYSCFG_SUSPSRC_EMAC |		\
-	DAVINCI_SYSCFG_SUSPSRC_I2C)
-
-/*
- * PLL configuration
- */
-
-#define CONFIG_SYS_DA850_PLL0_PLLM     24
-#define CONFIG_SYS_DA850_PLL1_PLLM     21
-
-/*
- * DDR2 memory configuration
- */
-#define CONFIG_SYS_DA850_DDR2_DDRPHYCR (DV_DDR_PHY_PWRDNEN | \
-					DV_DDR_PHY_EXT_STRBEN | \
-					(0x4 << DV_DDR_PHY_RD_LATENCY_SHIFT))
-
-#define CONFIG_SYS_DA850_DDR2_SDBCR (		\
-	(1 << DV_DDR_SDCR_MSDRAMEN_SHIFT) |	\
-	(1 << DV_DDR_SDCR_DDREN_SHIFT) |	\
-	(1 << DV_DDR_SDCR_SDRAMEN_SHIFT) |	\
-	(1 << DV_DDR_SDCR_BUS_WIDTH_SHIFT) |	\
-	(0x3 << DV_DDR_SDCR_CL_SHIFT) |		\
-	(0x2 << DV_DDR_SDCR_IBANK_SHIFT) |	\
-	(0x2 << DV_DDR_SDCR_PAGESIZE_SHIFT))
-
-/* SDBCR2 is only used if IBANK_POS bit in SDBCR is set */
-#define CONFIG_SYS_DA850_DDR2_SDBCR2 0
-
-#define CONFIG_SYS_DA850_DDR2_SDTIMR (		\
-	(14 << DV_DDR_SDTMR1_RFC_SHIFT) |	\
-	(2 << DV_DDR_SDTMR1_RP_SHIFT) |		\
-	(2 << DV_DDR_SDTMR1_RCD_SHIFT) |	\
-	(1 << DV_DDR_SDTMR1_WR_SHIFT) |		\
-	(5 << DV_DDR_SDTMR1_RAS_SHIFT) |	\
-	(8 << DV_DDR_SDTMR1_RC_SHIFT) |		\
-	(1 << DV_DDR_SDTMR1_RRD_SHIFT) |	\
-	(0 << DV_DDR_SDTMR1_WTR_SHIFT))
-
-#define CONFIG_SYS_DA850_DDR2_SDTIMR2 (		\
-	(7 << DV_DDR_SDTMR2_RASMAX_SHIFT) |	\
-	(0 << DV_DDR_SDTMR2_XP_SHIFT) |		\
-	(0 << DV_DDR_SDTMR2_ODT_SHIFT) |	\
-	(17 << DV_DDR_SDTMR2_XSNR_SHIFT) |	\
-	(199 << DV_DDR_SDTMR2_XSRD_SHIFT) |	\
-	(0 << DV_DDR_SDTMR2_RTP_SHIFT) |	\
-	(0 << DV_DDR_SDTMR2_CKE_SHIFT))
-
-#define CONFIG_SYS_DA850_DDR2_SDRCR    0x00000494
-#define CONFIG_SYS_DA850_DDR2_PBBPR    0x30
-
 /*
  * Serial Driver info
  */
@@ -139,35 +84,60 @@
 		"if run loadbootscr; then " \
 			"run bootscript; " \
 		"else " \
+			"if run loadbootenv; then " \
+				"echo Loaded env from ${bootenvfile};" \
+				"run importbootenv;" \
+			"fi;" \
+			"if test -n $uenvcmd; then " \
+				"echo Running uenvcmd...;" \
+				"run uenvcmd;" \
+			"fi;" \
 			"if run loadimage; then " \
 				"run mmcargs; " \
+				"if run loadfdt; then " \
+					"echo Using ${fdtfile}...;" \
+					"run fdtfixup; " \
+					"run fdtboot; "\
+				"fi; " \
 				"run mmcboot; " \
-			"else " \
-				"run flashargs; " \
-				"run flashboot; " \
 			"fi; " \
 		"fi; " \
-	"else " \
-		"run flashargs; " \
-		"run flashboot; " \
-	"fi"
+	"fi; "\
+	"run flashargs; " \
+	"run flashboot"
 #define CONFIG_EXTRA_ENV_SETTINGS \
-	"hostname=EV3\0" \
+	"bootenvfile=uEnv.txt\0" \
+	"fdtfile=da850-lego-ev3.dtb\0" \
 	"memsize=64M\0" \
 	"filesyssize=10M\0" \
 	"verify=n\0" \
 	"console=ttyS1,115200n8\0" \
 	"bootscraddr=0xC0600000\0" \
+	"fdtaddr=0xC0600000\0" \
 	"loadaddr=0xC0007FC0\0" \
 	"filesysaddr=0xC1180000\0" \
 	"fwupdateboot=mw 0xFFFF1FFC 0x5555AAAA; reset\0" \
-	"mmcargs=setenv bootargs mem=${memsize} console=${console} root=/dev/mmcblk0p2 rw rootwait lpj=747520\0" \
+	"importbootenv=echo Importing environment...; " \
+		"env import -t ${loadaddr} ${filesize}\0" \
+	"loadbootenv=fatload mmc 0 ${loadaddr} ${bootenvfile}\0" \
+	"mmcargs=setenv bootargs console=${console} root=/dev/mmcblk0p2 rw " \
+		"rootwait ${optargs}\0" \
 	"mmcboot=bootm ${loadaddr}\0" \
-	"flashargs=setenv bootargs mem=${memsize} initrd=${filesysaddr},${filesyssize} root=/dev/ram0 rw rootfstype=squashfs console=${console} lpj=747520\0" \
-	"flashboot=sf probe 0; sf read ${loadaddr} 0x50000 0x300000; sf read ${filesysaddr} 0x350000 0x960000; bootm ${loadaddr}\0" \
+	"flashargs=setenv bootargs initrd=${filesysaddr},${filesyssize} " \
+		"root=/dev/ram0 rw rootfstype=squashfs console=${console} " \
+		"${optargs}\0" \
+	"flashboot=sf probe 0; " \
+		"sf read ${fdtaddr} 0x40000 0x10000; " \
+		"sf read ${loadaddr} 0x50000 0x400000; " \
+		"sf read ${filesysaddr} 0x450000 0xA00000; " \
+		"run fdtfixup; " \
+		"run fdtboot\0" \
 	"loadimage=fatload mmc 0 ${loadaddr} uImage\0" \
+	"loadfdt=fatload mmc 0 ${fdtaddr} ${fdtfile}\0" \
+	"fdtfixup=fdt addr ${fdtaddr}; fdt resize; fdt chosen\0" \
+	"fdtboot=bootm ${loadaddr} - ${fdtaddr}\0" \
 	"loadbootscr=fatload mmc 0 ${bootscraddr} boot.scr\0" \
-	"bootscript=source ${bootscraddr}\0" \
+	"bootscript=source ${bootscraddr}\0"
 
 #ifdef CONFIG_CMD_BDI
 #define CONFIG_CLOCKS
diff --git a/include/configs/stih410-b2260.h b/include/configs/stih410-b2260.h
index b4e4619..d8bf3b6 100644
--- a/include/configs/stih410-b2260.h
+++ b/include/configs/stih410-b2260.h
@@ -31,6 +31,7 @@
 			"fdtfile=stih410-b2260.dtb\0"		\
 			"fdt_addr_r=0x47000000\0"		\
 			"scriptaddr=0x50000000\0"		\
+			"pxefile_addr_r=0x50100000\0"		\
 			"fdt_high=0xffffffffffffffff\0"		\
 			"initrd_high=0xffffffffffffffff\0"	\
 			"ramdisk_addr_r=0x48000000\0"		\
diff --git a/include/iotrace.h b/include/iotrace.h
index 9fe5733..1efb117 100644
--- a/include/iotrace.h
+++ b/include/iotrace.h
@@ -59,6 +59,30 @@
 u32 iotrace_get_checksum(void);
 
 /**
+ * iotrace_set_region() - Set whether iotrace is limited to a specific
+ * io region.
+ *
+ * Defines the address and size of the limited region.
+ *
+ * @start: address of the beginning of the region
+ * @size: size of the region in bytes.
+ */
+void iotrace_set_region(ulong start, ulong size);
+
+/**
+ * iotrace_reset_region() - Reset the region limit
+ */
+void iotrace_reset_region(void);
+
+/**
+ * iotrace_get_region() - Get region information
+ *
+ * @start: Returns start address of region
+ * @size: Returns size of region in bytes
+ */
+void iotrace_get_region(ulong *start, ulong *size);
+
+/**
  * iotrace_set_enabled() - Set whether iotracing is enabled or not
  *
  * This controls whether the checksum is updated and a trace record added
diff --git a/include/linux/bug.h b/include/linux/bug.h
index f07bb71..29f8416 100644
--- a/include/linux/bug.h
+++ b/include/linux/bug.h
@@ -20,6 +20,13 @@
 	unlikely(__ret_warn_on);					\
 })
 
+#define WARN(condition, format...) ({                   \
+	int __ret_warn_on = !!(condition);              \
+	if (unlikely(__ret_warn_on))                    \
+		printf(format);                  \
+	unlikely(__ret_warn_on);                    \
+})
+
 #define WARN_ON_ONCE(condition)	({				\
 	static bool __warned;					\
 	int __ret_warn_once = !!(condition);			\
@@ -31,4 +38,15 @@
 	unlikely(__ret_warn_once);				\
 })
 
+#define WARN_ONCE(condition, format...) ({          \
+	static bool __warned;     \
+	int __ret_warn_once = !!(condition);            \
+								\
+	if (unlikely(__ret_warn_once && !__warned)) {       \
+		__warned = true;                \
+		WARN(1, format);                \
+	}                           \
+	unlikely(__ret_warn_once);              \
+})
+
 #endif	/* _LINUX_BUG_H */
diff --git a/include/part_efi.h b/include/part_efi.h
index 6065c57..8525770 100644
--- a/include/part_efi.h
+++ b/include/part_efi.h
@@ -20,6 +20,7 @@
 #include <efi.h>
 
 #define MSDOS_MBR_SIGNATURE 0xAA55
+#define MSDOS_MBR_BOOT_CODE_SIZE 440
 #define EFI_PMBR_OSTYPE_EFI 0xEF
 #define EFI_PMBR_OSTYPE_EFI_GPT 0xEE
 
@@ -111,7 +112,7 @@
 } __packed gpt_entry;
 
 typedef struct _legacy_mbr {
-	u8 boot_code[440];
+	u8 boot_code[MSDOS_MBR_BOOT_CODE_SIZE];
 	__le32 unique_mbr_signature;
 	__le16 unknown;
 	struct partition partition_record[4];
diff --git a/scripts/get_default_envs.sh b/scripts/get_default_envs.sh
index 2872653..da86a9d 100755
--- a/scripts/get_default_envs.sh
+++ b/scripts/get_default_envs.sh
@@ -23,7 +23,7 @@
 fi
 
 env_obj_file_path=$(find ${path} -path "*/env/*" -not -path "*/spl/*" \
-			 -name "${ENV_OBJ_FILE}")
+			 -not -path "*/tools/*" -name "${ENV_OBJ_FILE}")
 [ -z "${env_obj_file_path}" ] && \
     { echoerr "File '${ENV_OBJ_FILE}' not found!"; exit 1; }
 
diff --git a/test/run b/test/run
index b2d24e3..eb1563d 100755
--- a/test/run
+++ b/test/run
@@ -11,8 +11,7 @@
 run_test ./test/py/test.py --bd sandbox --build
 
 # Run tests which require sandbox_spl
-run_test ./test/py/test.py --bd sandbox_spl --build -k \
-		test/py/tests/test_ofplatdata.py
+run_test ./test/py/test.py --bd sandbox_spl --build -k test_ofplatdata.py
 
 # Run tests for the flat DT version of sandbox
 ./test/py/test.py --bd sandbox_flattree --build
diff --git a/tools/binman/README b/tools/binman/README
index b200981..22f21bc 100644
--- a/tools/binman/README
+++ b/tools/binman/README
@@ -302,9 +302,9 @@
 align-end:
 	This sets the alignment of the end of an entry. Some entries require
 	that they end on an alignment boundary, regardless of where they
-	start. If 'align-end' is not provided, no alignment is performed.
-
-	Note: This is not yet implemented in binman.
+	start. This does not move the start of the entry, so the contents of
+	the entry will still start at the beginning. But there may be padding
+	at the end. If 'align-end' is not provided, no alignment is performed.
 
 filename:
 	For 'blob' types this provides the filename containing the binary to
@@ -316,6 +316,13 @@
 	possible to use any name, and then add (for example) 'type = "u-boot"'
 	to specify the type.
 
+pos-unset:
+	Indicates that the position of this entry should not be set by placing
+	it immediately after the entry before. Instead, is set by another
+	entry which knows where this entry should go. When this boolean
+	property is present, binman will give an error if another entry does
+	not set the position (with the GetPositions() method).
+
 
 The attributes supported for images are described below. Several are similar
 to those for entries.
@@ -387,6 +394,57 @@
 Examples of the above options can be found in the tests. See the
 tools/binman/test directory.
 
+It is possible to have the same binary appear multiple times in the image,
+either by using a unit number suffix (u-boot@0, u-boot@1) or by using a
+different name for each and specifying the type with the 'type' attribute.
+
+
+Sections and hiearchical images
+-------------------------------
+
+Sometimes it is convenient to split an image into several pieces, each of which
+contains its own set of binaries. An example is a flash device where part of
+the image is read-only and part is read-write. We can set up sections for each
+of these, and place binaries in them independently. The image is still produced
+as a single output file.
+
+This feature provides a way of creating hierarchical images. For example here
+is an example image with two copies of U-Boot. One is read-only (ro), intended
+to be written only in the factory. Another is read-write (rw), so that it can be
+upgraded in the field. The sizes are fixed so that the ro/rw boundary is known
+and can be programmed:
+
+	binman {
+		section@0 {
+			read-only;
+			name-prefix = "ro-";
+			size = <0x100000>;
+			u-boot {
+			};
+		};
+		section@1 {
+			name-prefix = "rw-";
+			size = <0x100000>;
+			u-boot {
+			};
+		};
+	};
+
+This image could be placed into a SPI flash chip, with the protection boundary
+set at 1MB.
+
+A few special properties are provided for sections:
+
+read-only:
+	Indicates that this section is read-only. This has no impact on binman's
+	operation, but his property can be read at run time.
+
+name-prefix:
+	This string is prepended to all the names of the binaries in the
+	section. In the example above, the 'u-boot' binaries which actually be
+	renamed to 'ro-u-boot' and 'rw-u-boot'. This can be useful to
+	distinguish binaries with otherwise identical names.
+
 
 Special properties
 ------------------
@@ -498,6 +556,25 @@
 to fill in such symbols in U-Boot proper, as well.
 
 
+Map files
+---------
+
+The -m option causes binman to output a .map file for each image that it
+generates. This shows the position and size of each entry. For example:
+
+    Position      Size  Name
+    00000000  00000010  section@0
+     00000000  00000004  u-boot
+    00000010  00000010  section@1
+     00000000  00000004  u-boot
+
+This shows a hierarchical image with two sections, each with a single entry. The
+positions of the sections are absolute hex byte offsets within the image. The
+positions of the entries are relative to their respective sections. The size of
+each entry is also shown, in bytes (hex). The indentation shows the entries
+nested inside their sections.
+
+
 Code coverage
 -------------
 
@@ -547,7 +624,7 @@
 a reasonably simple and sound design but has expanded greatly over the
 years. In particular its handling of x86 images is convoluted.
 
-Quite a few lessons have been learned which are hopefully be applied here.
+Quite a few lessons have been learned which are hopefully applied here.
 
 
 Design notes
@@ -578,17 +655,13 @@
   'Access to binman entry positions at run time' above
 - Use of-platdata to make the information available to code that is unable
   to use device tree (such as a very small SPL image)
-- Write an image map to a text file
 - Allow easy building of images by specifying just the board name
 - Produce a full Python binding for libfdt (for upstream)
 - Add an option to decode an image into the constituent binaries
-- Suppoort hierarchical images (packing of binaries into another binary
-  which is then placed in the image)
 - Support building an image for a board (-b) more completely, with a
   configurable build directory
 - Consider making binman work with buildman, although if it is used in the
   Makefile, this will be automatic
-- Implement align-end
 
 --
 Simon Glass <sjg@chromium.org>
diff --git a/tools/binman/binman.py b/tools/binman/binman.py
index fa2f551..31b0453 100755
--- a/tools/binman/binman.py
+++ b/tools/binman/binman.py
@@ -23,15 +23,18 @@
 # Bring in the libfdt module
 sys.path.insert(0, 'scripts/dtc/pylibfdt')
 
-# Also allow entry-type modules to be brought in from the etype directory.
-sys.path.insert(0, os.path.join(our_path, 'etype'))
-
 import cmdline
 import command
 import control
 
-def RunTests(debug):
-    """Run the functional tests and any embedded doctests"""
+def RunTests(debug, args):
+    """Run the functional tests and any embedded doctests
+
+    Args:
+        debug: True to enable debugging, which shows a full stack trace on error
+        args: List of positional args provided to binman. This can hold a test
+            name to execute (as in 'binman -t testSections', for example)
+    """
     import elf_test
     import entry_test
     import fdt_test
@@ -53,9 +56,16 @@
     # 'entry' module.
     suite = unittest.TestLoader().loadTestsFromTestCase(entry_test.TestEntry)
     suite.run(result)
+    test_name = args and args[0] or None
     for module in (ftest.TestFunctional, fdt_test.TestFdt, elf_test.TestElf,
                    image_test.TestImage):
-        suite = unittest.TestLoader().loadTestsFromTestCase(module)
+        if test_name:
+            try:
+                suite = unittest.TestLoader().loadTestsFromName(args[0], module)
+            except AttributeError:
+                continue
+        else:
+            suite = unittest.TestLoader().loadTestsFromTestCase(module)
         suite.run(result)
 
     print result
@@ -114,7 +124,7 @@
         sys.tracebacklimit = 0
 
     if options.test:
-        ret_code = RunTests(options.debug)
+        ret_code = RunTests(options.debug, args[1:])
 
     elif options.test_coverage:
         RunTestCoverage()
diff --git a/tools/binman/bsection.py b/tools/binman/bsection.py
new file mode 100644
index 0000000..3f30f6e
--- /dev/null
+++ b/tools/binman/bsection.py
@@ -0,0 +1,318 @@
+# SPDX-License-Identifier: GPL-2.0+
+# Copyright (c) 2018 Google, Inc
+# Written by Simon Glass <sjg@chromium.org>
+#
+# Base class for sections (collections of entries)
+#
+
+from __future__ import print_function
+
+from collections import OrderedDict
+import sys
+
+import fdt_util
+import re
+import tools
+
+class Section(object):
+    """A section which contains multiple entries
+
+    A section represents a collection of entries. There must be one or more
+    sections in an image. Sections are used to group entries together.
+
+    Attributes:
+        _node: Node object that contains the section definition in device tree
+        _size: Section size in bytes, or None if not known yet
+        _align_size: Section size alignment, or None
+        _pad_before: Number of bytes before the first entry starts. This
+            effectively changes the place where entry position 0 starts
+        _pad_after: Number of bytes after the last entry ends. The last
+            entry will finish on or before this boundary
+        _pad_byte: Byte to use to pad the section where there is no entry
+        _sort: True if entries should be sorted by position, False if they
+            must be in-order in the device tree description
+        _skip_at_start: Number of bytes before the first entry starts. These
+            effectively adjust the starting position of entries. For example,
+            if _pad_before is 16, then the first entry would start at 16.
+            An entry with pos = 20 would in fact be written at position 4
+            in the image file.
+        _end_4gb: Indicates that the section ends at the 4GB boundary. This is
+            used for x86 images, which want to use positions such that a
+             memory address (like 0xff800000) is the first entry position.
+             This causes _skip_at_start to be set to the starting memory
+             address.
+        _name_prefix: Prefix to add to the name of all entries within this
+            section
+        _entries: OrderedDict() of entries
+    """
+    def __init__(self, name, node, test=False):
+        global entry
+        global Entry
+        import entry
+        from entry import Entry
+
+        self._node = node
+        self._size = None
+        self._align_size = None
+        self._pad_before = 0
+        self._pad_after = 0
+        self._pad_byte = 0
+        self._sort = False
+        self._skip_at_start = 0
+        self._end_4gb = False
+        self._name_prefix = ''
+        self._entries = OrderedDict()
+        if not test:
+            self._ReadNode()
+            self._ReadEntries()
+
+    def _ReadNode(self):
+        """Read properties from the section node"""
+        self._size = fdt_util.GetInt(self._node, 'size')
+        self._align_size = fdt_util.GetInt(self._node, 'align-size')
+        if tools.NotPowerOfTwo(self._align_size):
+            self._Raise("Alignment size %s must be a power of two" %
+                        self._align_size)
+        self._pad_before = fdt_util.GetInt(self._node, 'pad-before', 0)
+        self._pad_after = fdt_util.GetInt(self._node, 'pad-after', 0)
+        self._pad_byte = fdt_util.GetInt(self._node, 'pad-byte', 0)
+        self._sort = fdt_util.GetBool(self._node, 'sort-by-pos')
+        self._end_4gb = fdt_util.GetBool(self._node, 'end-at-4gb')
+        if self._end_4gb and not self._size:
+            self._Raise("Section size must be provided when using end-at-4gb")
+        if self._end_4gb:
+            self._skip_at_start = 0x100000000 - self._size
+        self._name_prefix = fdt_util.GetString(self._node, 'name-prefix')
+
+    def _ReadEntries(self):
+        for node in self._node.subnodes:
+            entry = Entry.Create(self, node)
+            entry.SetPrefix(self._name_prefix)
+            self._entries[node.name] = entry
+
+    def CheckSize(self):
+        """Check that the section contents does not exceed its size, etc."""
+        contents_size = 0
+        for entry in self._entries.values():
+            contents_size = max(contents_size, entry.pos + entry.size)
+
+        contents_size -= self._skip_at_start
+
+        size = self._size
+        if not size:
+            size = self._pad_before + contents_size + self._pad_after
+            size = tools.Align(size, self._align_size)
+
+        if self._size and contents_size > self._size:
+            self._Raise("contents size %#x (%d) exceeds section size %#x (%d)" %
+                       (contents_size, contents_size, self._size, self._size))
+        if not self._size:
+            self._size = size
+        if self._size != tools.Align(self._size, self._align_size):
+            self._Raise("Size %#x (%d) does not match align-size %#x (%d)" %
+                  (self._size, self._size, self._align_size, self._align_size))
+        return size
+
+    def _Raise(self, msg):
+        """Raises an error for this section
+
+        Args:
+            msg: Error message to use in the raise string
+        Raises:
+            ValueError()
+        """
+        raise ValueError("Section '%s': %s" % (self._node.path, msg))
+
+    def GetPath(self):
+        """Get the path of an image (in the FDT)
+
+        Returns:
+            Full path of the node for this image
+        """
+        return self._node.path
+
+    def FindEntryType(self, etype):
+        """Find an entry type in the section
+
+        Args:
+            etype: Entry type to find
+        Returns:
+            entry matching that type, or None if not found
+        """
+        for entry in self._entries.values():
+            if entry.etype == etype:
+                return entry
+        return None
+
+    def GetEntryContents(self):
+        """Call ObtainContents() for each entry
+
+        This calls each entry's ObtainContents() a few times until they all
+        return True. We stop calling an entry's function once it returns
+        True. This allows the contents of one entry to depend on another.
+
+        After 3 rounds we give up since it's likely an error.
+        """
+        todo = self._entries.values()
+        for passnum in range(3):
+            next_todo = []
+            for entry in todo:
+                if not entry.ObtainContents():
+                    next_todo.append(entry)
+            todo = next_todo
+            if not todo:
+                break
+
+    def _SetEntryPosSize(self, name, pos, size):
+        """Set the position and size of an entry
+
+        Args:
+            name: Entry name to update
+            pos: New position
+            size: New size
+        """
+        entry = self._entries.get(name)
+        if not entry:
+            self._Raise("Unable to set pos/size for unknown entry '%s'" % name)
+        entry.SetPositionSize(self._skip_at_start + pos, size)
+
+    def GetEntryPositions(self):
+        """Handle entries that want to set the position/size of other entries
+
+        This calls each entry's GetPositions() method. If it returns a list
+        of entries to update, it updates them.
+        """
+        for entry in self._entries.values():
+            pos_dict = entry.GetPositions()
+            for name, info in pos_dict.iteritems():
+                self._SetEntryPosSize(name, *info)
+
+    def PackEntries(self):
+        """Pack all entries into the section"""
+        pos = self._skip_at_start
+        for entry in self._entries.values():
+            pos = entry.Pack(pos)
+
+    def _SortEntries(self):
+        """Sort entries by position"""
+        entries = sorted(self._entries.values(), key=lambda entry: entry.pos)
+        self._entries.clear()
+        for entry in entries:
+            self._entries[entry._node.name] = entry
+
+    def CheckEntries(self):
+        """Check that entries do not overlap or extend outside the section"""
+        if self._sort:
+            self._SortEntries()
+        pos = 0
+        prev_name = 'None'
+        for entry in self._entries.values():
+            entry.CheckPosition()
+            if (entry.pos < self._skip_at_start or
+                entry.pos >= self._skip_at_start + self._size):
+                entry.Raise("Position %#x (%d) is outside the section starting "
+                            "at %#x (%d)" %
+                            (entry.pos, entry.pos, self._skip_at_start,
+                             self._skip_at_start))
+            if entry.pos < pos:
+                entry.Raise("Position %#x (%d) overlaps with previous entry '%s' "
+                            "ending at %#x (%d)" %
+                            (entry.pos, entry.pos, prev_name, pos, pos))
+            pos = entry.pos + entry.size
+            prev_name = entry.GetPath()
+
+    def ProcessEntryContents(self):
+        """Call the ProcessContents() method for each entry
+
+        This is intended to adjust the contents as needed by the entry type.
+        """
+        for entry in self._entries.values():
+            entry.ProcessContents()
+
+    def WriteSymbols(self):
+        """Write symbol values into binary files for access at run time"""
+        for entry in self._entries.values():
+            entry.WriteSymbols(self)
+
+    def BuildSection(self, fd, base_pos):
+        """Write the section to a file"""
+        fd.seek(base_pos)
+        fd.write(self.GetData())
+
+    def GetData(self):
+        """Write the section to a file"""
+        section_data = chr(self._pad_byte) * self._size
+
+        for entry in self._entries.values():
+            data = entry.GetData()
+            base = self._pad_before + entry.pos - self._skip_at_start
+            section_data = (section_data[:base] + data +
+                            section_data[base + len(data):])
+        return section_data
+
+    def LookupSymbol(self, sym_name, optional, msg):
+        """Look up a symbol in an ELF file
+
+        Looks up a symbol in an ELF file. Only entry types which come from an
+        ELF image can be used by this function.
+
+        At present the only entry property supported is pos.
+
+        Args:
+            sym_name: Symbol name in the ELF file to look up in the format
+                _binman_<entry>_prop_<property> where <entry> is the name of
+                the entry and <property> is the property to find (e.g.
+                _binman_u_boot_prop_pos). As a special case, you can append
+                _any to <entry> to have it search for any matching entry. E.g.
+                _binman_u_boot_any_prop_pos will match entries called u-boot,
+                u-boot-img and u-boot-nodtb)
+            optional: True if the symbol is optional. If False this function
+                will raise if the symbol is not found
+            msg: Message to display if an error occurs
+
+        Returns:
+            Value that should be assigned to that symbol, or None if it was
+                optional and not found
+
+        Raises:
+            ValueError if the symbol is invalid or not found, or references a
+                property which is not supported
+        """
+        m = re.match(r'^_binman_(\w+)_prop_(\w+)$', sym_name)
+        if not m:
+            raise ValueError("%s: Symbol '%s' has invalid format" %
+                             (msg, sym_name))
+        entry_name, prop_name = m.groups()
+        entry_name = entry_name.replace('_', '-')
+        entry = self._entries.get(entry_name)
+        if not entry:
+            if entry_name.endswith('-any'):
+                root = entry_name[:-4]
+                for name in self._entries:
+                    if name.startswith(root):
+                        rest = name[len(root):]
+                        if rest in ['', '-img', '-nodtb']:
+                            entry = self._entries[name]
+        if not entry:
+            err = ("%s: Entry '%s' not found in list (%s)" %
+                   (msg, entry_name, ','.join(self._entries.keys())))
+            if optional:
+                print('Warning: %s' % err, file=sys.stderr)
+                return None
+            raise ValueError(err)
+        if prop_name == 'pos':
+            return entry.pos
+        else:
+            raise ValueError("%s: No such property '%s'" % (msg, prop_name))
+
+    def GetEntries(self):
+        return self._entries
+
+    def WriteMap(self, fd, indent):
+        """Write a map of the section to a .map file
+
+        Args:
+            fd: File to write the map to
+        """
+        for entry in self._entries.values():
+            entry.WriteMap(fd, indent)
diff --git a/tools/binman/cmdline.py b/tools/binman/cmdline.py
index e9e0434..bf63919 100644
--- a/tools/binman/cmdline.py
+++ b/tools/binman/cmdline.py
@@ -30,6 +30,8 @@
             help='Add a path to a directory to use for input files')
     parser.add_option('-H', '--full-help', action='store_true',
         default=False, help='Display the README file')
+    parser.add_option('-m', '--map', action='store_true',
+        default=False, help='Output a map file for each image')
     parser.add_option('-O', '--outdir', type='string',
         action='store', help='Path to directory to use for intermediate and '
         'output files')
diff --git a/tools/binman/control.py b/tools/binman/control.py
index bc8ed8e..9243472 100644
--- a/tools/binman/control.py
+++ b/tools/binman/control.py
@@ -112,6 +112,8 @@
                 image.ProcessEntryContents()
                 image.WriteSymbols()
                 image.BuildImage()
+                if options.map:
+                    image.WriteMap()
         finally:
             tools.FinaliseOutputDir()
     finally:
diff --git a/tools/binman/elf.py b/tools/binman/elf.py
index 50085a3..0ae3b61 100644
--- a/tools/binman/elf.py
+++ b/tools/binman/elf.py
@@ -75,7 +75,7 @@
         return None
     return sym.address
 
-def LookupAndWriteSymbols(elf_fname, entry, image):
+def LookupAndWriteSymbols(elf_fname, entry, section):
     """Replace all symbols in an entry with their correct values
 
     The entry contents is updated so that values for referenced symbols will be
@@ -87,7 +87,7 @@
         elf_fname: Filename of ELF image containing the symbol information for
             entry
         entry: Entry to process
-        image: Image which can be used to lookup symbol values
+        section: Section which can be used to lookup symbol values
     """
     fname = tools.GetInputFilename(elf_fname)
     syms = GetSymbols(fname, ['image', 'binman'])
@@ -98,8 +98,8 @@
         return
     for name, sym in syms.iteritems():
         if name.startswith('_binman'):
-            msg = ("Image '%s': Symbol '%s'\n   in entry '%s'" %
-                   (image.GetPath(), name, entry.GetPath()))
+            msg = ("Section '%s': Symbol '%s'\n   in entry '%s'" %
+                   (section.GetPath(), name, entry.GetPath()))
             offset = sym.address - base.address
             if offset < 0 or offset + sym.size > entry.contents_size:
                 raise ValueError('%s has offset %x (size %x) but the contents '
@@ -114,7 +114,7 @@
                                  (msg, sym.size))
 
             # Look up the symbol in our entry tables.
-            value = image.LookupSymbol(name, sym.weak, msg)
+            value = section.LookupSymbol(name, sym.weak, msg)
             if value is not None:
                 value += base.address
             else:
diff --git a/tools/binman/elf_test.py b/tools/binman/elf_test.py
index 4abde12..fb6e451 100644
--- a/tools/binman/elf_test.py
+++ b/tools/binman/elf_test.py
@@ -40,12 +40,12 @@
     def GetPath(self):
         return 'entry_path'
 
-class FakeImage:
+class FakeSection:
     def __init__(self, sym_value=1):
         self.sym_value = sym_value
 
     def GetPath(self):
-        return 'image_path'
+        return 'section_path'
 
     def LookupSymbol(self, name, weak, msg):
         return self.sym_value
@@ -67,51 +67,51 @@
 
     def testMissingFile(self):
         entry = FakeEntry(10)
-        image = FakeImage()
+        section = FakeSection()
         with self.assertRaises(ValueError) as e:
-            syms = elf.LookupAndWriteSymbols('missing-file', entry, image)
+            syms = elf.LookupAndWriteSymbols('missing-file', entry, section)
         self.assertIn("Filename 'missing-file' not found in input path",
                       str(e.exception))
 
     def testOutsideFile(self):
         entry = FakeEntry(10)
-        image = FakeImage()
+        section = FakeSection()
         elf_fname = os.path.join(binman_dir, 'test', 'u_boot_binman_syms')
         with self.assertRaises(ValueError) as e:
-            syms = elf.LookupAndWriteSymbols(elf_fname, entry, image)
+            syms = elf.LookupAndWriteSymbols(elf_fname, entry, section)
         self.assertIn('entry_path has offset 4 (size 8) but the contents size '
                       'is a', str(e.exception))
 
     def testMissingImageStart(self):
         entry = FakeEntry(10)
-        image = FakeImage()
+        section = FakeSection()
         elf_fname = os.path.join(binman_dir, 'test', 'u_boot_binman_syms_bad')
-        self.assertEqual(elf.LookupAndWriteSymbols(elf_fname, entry, image),
+        self.assertEqual(elf.LookupAndWriteSymbols(elf_fname, entry, section),
                          None)
 
     def testBadSymbolSize(self):
         entry = FakeEntry(10)
-        image = FakeImage()
+        section = FakeSection()
         elf_fname = os.path.join(binman_dir, 'test', 'u_boot_binman_syms_size')
         with self.assertRaises(ValueError) as e:
-            syms = elf.LookupAndWriteSymbols(elf_fname, entry, image)
+            syms = elf.LookupAndWriteSymbols(elf_fname, entry, section)
         self.assertIn('has size 1: only 4 and 8 are supported',
                       str(e.exception))
 
     def testNoValue(self):
         entry = FakeEntry(20)
-        image = FakeImage(sym_value=None)
+        section = FakeSection(sym_value=None)
         elf_fname = os.path.join(binman_dir, 'test', 'u_boot_binman_syms')
-        syms = elf.LookupAndWriteSymbols(elf_fname, entry, image)
+        syms = elf.LookupAndWriteSymbols(elf_fname, entry, section)
         self.assertEqual(chr(255) * 16 + 'a' * 4, entry.data)
 
     def testDebug(self):
         elf.debug = True
         entry = FakeEntry(20)
-        image = FakeImage()
+        section = FakeSection()
         elf_fname = os.path.join(binman_dir, 'test', 'u_boot_binman_syms')
         with capture_sys_output() as (stdout, stderr):
-            syms = elf.LookupAndWriteSymbols(elf_fname, entry, image)
+            syms = elf.LookupAndWriteSymbols(elf_fname, entry, section)
         elf.debug = False
         self.assertTrue(len(stdout.getvalue()) > 0)
 
diff --git a/tools/binman/etype/entry.py b/tools/binman/entry.py
similarity index 74%
rename from tools/binman/etype/entry.py
rename to tools/binman/entry.py
index c331312..e4d688c 100644
--- a/tools/binman/etype/entry.py
+++ b/tools/binman/entry.py
@@ -4,6 +4,8 @@
 # Base class for all entries
 #
 
+from __future__ import print_function
+
 # importlib was introduced in Python 2.7 but there was a report of it not
 # working in 2.7.12, so we work around this:
 # http://lists.denx.de/pipermail/u-boot/2016-October/269729.html
@@ -14,15 +16,19 @@
     have_importlib = False
 
 import fdt_util
+import os
+import sys
 import tools
 
 modules = {}
 
+our_path = os.path.dirname(os.path.realpath(__file__))
+
 class Entry(object):
-    """An Entry in the image
+    """An Entry in the section
 
     An entry corresponds to a single node in the device-tree description
-    of the image. Each entry ends up being a part of the final image.
+    of the section. Each entry ends up being a part of the final section.
     Entries can be placed either right next to each other, or with padding
     between them. The type of the entry determines the data that is in it.
 
@@ -30,9 +36,9 @@
     Entry.
 
     Attributes:
-        image: The image containing this entry
+        section: The section containing this entry
         node: The node that created this entry
-        pos: Absolute position of entry within the image, None if not known
+        pos: Absolute position of entry within the section, None if not known
         size: Entry size in bytes, None if not known
         contents_size: Size of contents in bytes, 0 by default
         align: Entry start position alignment, or None
@@ -42,10 +48,11 @@
         pad_after: Number of pad bytes after the contents, 0 if none
         data: Contents of entry (string of bytes)
     """
-    def __init__(self, image, etype, node, read_node=True):
-        self.image = image
+    def __init__(self, section, etype, node, read_node=True, name_prefix=''):
+        self.section = section
         self.etype = etype
         self._node = node
+        self.name = node and (name_prefix + node.name) or 'none'
         self.pos = None
         self.size = None
         self.contents_size = 0
@@ -59,11 +66,11 @@
             self.ReadNode()
 
     @staticmethod
-    def Create(image, node, etype=None):
+    def Create(section, node, etype=None):
         """Create a new entry for a node.
 
         Args:
-            image:  Image object containing this node
+            section:  Image object containing this node
             node:   Node object containing information about the entry to create
             etype:  Entry type to use, or None to work it out (used for tests)
 
@@ -72,11 +79,20 @@
         """
         if not etype:
             etype = fdt_util.GetString(node, 'type', node.name)
+
+        # Convert something like 'u-boot@0' to 'u_boot' since we are only
+        # interested in the type.
         module_name = etype.replace('-', '_')
+        if '@' in module_name:
+            module_name = module_name.split('@')[0]
         module = modules.get(module_name)
 
+        # Also allow entry-type modules to be brought in from the etype directory.
+
         # Import the module if we have not already done so.
         if not module:
+            old_path = sys.path
+            sys.path.insert(0, os.path.join(our_path, 'etype'))
             try:
                 if have_importlib:
                     module = importlib.import_module(module_name)
@@ -85,11 +101,13 @@
             except ImportError:
                 raise ValueError("Unknown entry type '%s' in node '%s'" %
                         (etype, node.path))
+            finally:
+                sys.path = old_path
             modules[module_name] = module
 
         # Call its constructor to get the object we want.
         obj = getattr(module, 'Entry_%s' % module_name)
-        return obj(image, etype, node)
+        return obj(section, etype, node)
 
     def ReadNode(self):
         """Read entry information from the node
@@ -111,6 +129,15 @@
         self.align_end = fdt_util.GetInt(self._node, 'align-end')
         self.pos_unset = fdt_util.GetBool(self._node, 'pos-unset')
 
+    def SetPrefix(self, prefix):
+        """Set the name prefix for a node
+
+        Args:
+            prefix: Prefix to set, or '' to not use a prefix
+        """
+        if prefix:
+            self.name = prefix + self.name
+
     def ObtainContents(self):
         """Figure out the contents of an entry.
 
@@ -122,7 +149,7 @@
         return True
 
     def Pack(self, pos):
-        """Figure out how to pack the entry into the image
+        """Figure out how to pack the entry into the section
 
         Most of the time the entries are not fully specified. There may be
         an alignment but no size. In that case we take the size from the
@@ -134,10 +161,10 @@
         entry will be know.
 
         Args:
-            Current image position pointer
+            Current section position pointer
 
         Returns:
-            New image position pointer (after this entry)
+            New section position pointer (after this entry)
         """
         if self.pos is None:
             if self.pos_unset:
@@ -198,10 +225,29 @@
     def ProcessContents(self):
         pass
 
-    def WriteSymbols(self, image):
+    def WriteSymbols(self, section):
         """Write symbol values into binary files for access at run time
 
         Args:
-          image: Image containing the entry
+          section: Section containing the entry
         """
         pass
+
+    def CheckPosition(self):
+        """Check that the entry positions are correct
+
+        This is used for entries which have extra position requirements (other
+        than having to be fully inside their section). Sub-classes can implement
+        this function and raise if there is a problem.
+        """
+        pass
+
+    def WriteMap(self, fd, indent):
+        """Write a map of the entry to a .map file
+
+        Args:
+            fd: File to write the map to
+            indent: Curent indent level of map (0=none, 1=one level, etc.)
+        """
+        print('%s%08x  %08x  %s' % (' ' * indent, self.pos, self.size,
+                                    self.name), file=fd)
diff --git a/tools/binman/etype/_testing.py b/tools/binman/etype/_testing.py
index b166a71..c376dd5 100644
--- a/tools/binman/etype/_testing.py
+++ b/tools/binman/etype/_testing.py
@@ -10,8 +10,8 @@
 import tools
 
 class Entry__testing(Entry):
-    def __init__(self, image, etype, node):
-        Entry.__init__(self, image, etype, node)
+    def __init__(self, section, etype, node):
+        Entry.__init__(self, section, etype, node)
 
     def ObtainContents(self):
         self.data = 'a'
diff --git a/tools/binman/etype/blob.py b/tools/binman/etype/blob.py
index 10e59e9..16b1e5f 100644
--- a/tools/binman/etype/blob.py
+++ b/tools/binman/etype/blob.py
@@ -10,8 +10,8 @@
 import tools
 
 class Entry_blob(Entry):
-    def __init__(self, image, etype, node):
-        Entry.__init__(self, image, etype, node)
+    def __init__(self, section, etype, node):
+        Entry.__init__(self, section, etype, node)
         self._filename = fdt_util.GetString(self._node, "filename", self.etype)
 
     def ObtainContents(self):
diff --git a/tools/binman/etype/intel_cmc.py b/tools/binman/etype/intel_cmc.py
index 07cad2e..b8621e0 100644
--- a/tools/binman/etype/intel_cmc.py
+++ b/tools/binman/etype/intel_cmc.py
@@ -9,5 +9,5 @@
 from blob import Entry_blob
 
 class Entry_intel_cmc(Entry_blob):
-    def __init__(self, image, etype, node):
-        Entry_blob.__init__(self, image, etype, node)
+    def __init__(self, section, etype, node):
+        Entry_blob.__init__(self, section, etype, node)
diff --git a/tools/binman/etype/intel_descriptor.py b/tools/binman/etype/intel_descriptor.py
index 4e0c85b..0e78655 100644
--- a/tools/binman/etype/intel_descriptor.py
+++ b/tools/binman/etype/intel_descriptor.py
@@ -32,8 +32,8 @@
     size of the ME region, allowing us to place the ME binary in the right
     place.
     """
-    def __init__(self, image, etype, node):
-        Entry_blob.__init__(self, image, etype, node)
+    def __init__(self, section, etype, node):
+        Entry_blob.__init__(self, section, etype, node)
         self._regions = []
 
     def GetPositions(self):
diff --git a/tools/binman/etype/intel_fsp.py b/tools/binman/etype/intel_fsp.py
index 827bd22..cb80a61 100644
--- a/tools/binman/etype/intel_fsp.py
+++ b/tools/binman/etype/intel_fsp.py
@@ -9,5 +9,5 @@
 from blob import Entry_blob
 
 class Entry_intel_fsp(Entry_blob):
-    def __init__(self, image, etype, node):
-        Entry_blob.__init__(self, image, etype, node)
+    def __init__(self, section, etype, node):
+        Entry_blob.__init__(self, section, etype, node)
diff --git a/tools/binman/etype/intel_me.py b/tools/binman/etype/intel_me.py
index e02e485..0682ead 100644
--- a/tools/binman/etype/intel_me.py
+++ b/tools/binman/etype/intel_me.py
@@ -9,5 +9,5 @@
 from blob import Entry_blob
 
 class Entry_intel_me(Entry_blob):
-    def __init__(self, image, etype, node):
-        Entry_blob.__init__(self, image, etype, node)
+    def __init__(self, section, etype, node):
+        Entry_blob.__init__(self, section, etype, node)
diff --git a/tools/binman/etype/intel_mrc.py b/tools/binman/etype/intel_mrc.py
index 7c01b77..305ac98 100644
--- a/tools/binman/etype/intel_mrc.py
+++ b/tools/binman/etype/intel_mrc.py
@@ -9,8 +9,8 @@
 from blob import Entry_blob
 
 class Entry_intel_mrc(Entry_blob):
-    def __init__(self, image, etype, node):
-        Entry_blob.__init__(self, image, etype, node)
+    def __init__(self, section, etype, node):
+        Entry_blob.__init__(self, section, etype, node)
 
     def GetDefaultFilename(self):
         return 'mrc.bin'
diff --git a/tools/binman/etype/intel_vbt.py b/tools/binman/etype/intel_vbt.py
index 4f082c3..d4e8c3f 100644
--- a/tools/binman/etype/intel_vbt.py
+++ b/tools/binman/etype/intel_vbt.py
@@ -8,5 +8,5 @@
 from blob import Entry_blob
 
 class Entry_intel_vbt(Entry_blob):
-    def __init__(self, image, etype, node):
-        Entry_blob.__init__(self, image, etype, node)
+    def __init__(self, section, etype, node):
+        Entry_blob.__init__(self, section, etype, node)
diff --git a/tools/binman/etype/intel_vga.py b/tools/binman/etype/intel_vga.py
index 277fff1..140dd40 100644
--- a/tools/binman/etype/intel_vga.py
+++ b/tools/binman/etype/intel_vga.py
@@ -9,5 +9,5 @@
 from blob import Entry_blob
 
 class Entry_intel_vga(Entry_blob):
-    def __init__(self, image, etype, node):
-        Entry_blob.__init__(self, image, etype, node)
+    def __init__(self, section, etype, node):
+        Entry_blob.__init__(self, section, etype, node)
diff --git a/tools/binman/etype/section.py b/tools/binman/etype/section.py
new file mode 100644
index 0000000..139fcad
--- /dev/null
+++ b/tools/binman/etype/section.py
@@ -0,0 +1,59 @@
+# SPDX-License-Identifier:      GPL-2.0+
+# Copyright (c) 2018 Google, Inc
+# Written by Simon Glass <sjg@chromium.org>
+#
+# Entry-type module for sections, which are entries which can contain other
+# entries.
+#
+
+from entry import Entry
+import fdt_util
+import tools
+
+import bsection
+
+class Entry_section(Entry):
+    def __init__(self, image, etype, node):
+        Entry.__init__(self, image, etype, node)
+        self._section = bsection.Section(node.name, node)
+
+    def ObtainContents(self):
+        self._section.GetEntryContents()
+
+    def GetData(self):
+        return self._section.GetData()
+
+    def GetPositions(self):
+        """Handle entries that want to set the position/size of other entries
+
+        This calls each entry's GetPositions() method. If it returns a list
+        of entries to update, it updates them.
+        """
+        self._section.GetEntryPositions()
+        return {}
+
+    def Pack(self, pos):
+        """Pack all entries into the section"""
+        self._section.PackEntries()
+        self.size = self._section.CheckSize()
+        return super(Entry_section, self).Pack(pos)
+
+    def WriteSymbols(self, section):
+        """Write symbol values into binary files for access at run time"""
+        self._section.WriteSymbols()
+
+    def ProcessContents(self):
+        self._section.ProcessEntryContents()
+        super(Entry_section, self).ProcessContents()
+
+    def CheckPosition(self):
+        self._section.CheckEntries()
+
+    def WriteMap(self, fd, indent):
+        """Write a map of the section to a .map file
+
+        Args:
+            fd: File to write the map to
+        """
+        super(Entry_section, self).WriteMap(fd, indent)
+        self._section.WriteMap(fd, indent + 1)
diff --git a/tools/binman/etype/u_boot.py b/tools/binman/etype/u_boot.py
index fc212d0..b6058bf 100644
--- a/tools/binman/etype/u_boot.py
+++ b/tools/binman/etype/u_boot.py
@@ -9,8 +9,8 @@
 from blob import Entry_blob
 
 class Entry_u_boot(Entry_blob):
-    def __init__(self, image, etype, node):
-        Entry_blob.__init__(self, image, etype, node)
+    def __init__(self, section, etype, node):
+        Entry_blob.__init__(self, section, etype, node)
 
     def GetDefaultFilename(self):
         return 'u-boot.bin'
diff --git a/tools/binman/etype/u_boot_dtb.py b/tools/binman/etype/u_boot_dtb.py
index f6704db..dd3c5b2 100644
--- a/tools/binman/etype/u_boot_dtb.py
+++ b/tools/binman/etype/u_boot_dtb.py
@@ -9,8 +9,8 @@
 from blob import Entry_blob
 
 class Entry_u_boot_dtb(Entry_blob):
-    def __init__(self, image, etype, node):
-        Entry_blob.__init__(self, image, etype, node)
+    def __init__(self, section, etype, node):
+        Entry_blob.__init__(self, section, etype, node)
 
     def GetDefaultFilename(self):
         return 'u-boot.dtb'
diff --git a/tools/binman/etype/u_boot_dtb_with_ucode.py b/tools/binman/etype/u_boot_dtb_with_ucode.py
index bedc398..1e530d6 100644
--- a/tools/binman/etype/u_boot_dtb_with_ucode.py
+++ b/tools/binman/etype/u_boot_dtb_with_ucode.py
@@ -16,8 +16,8 @@
     See Entry_u_boot_ucode for full details of the 3 entries involved in this
     process.
     """
-    def __init__(self, image, etype, node):
-        Entry_blob.__init__(self, image, etype, node)
+    def __init__(self, section, etype, node):
+        Entry_blob.__init__(self, section, etype, node)
         self.ucode_data = ''
         self.collate = False
         self.ucode_offset = None
@@ -29,10 +29,12 @@
     def ObtainContents(self):
         Entry_blob.ObtainContents(self)
 
-        # If the image does not need microcode, there is nothing to do
-        ucode_dest_entry = self.image.FindEntryType('u-boot-spl-with-ucode-ptr')
+        # If the section does not need microcode, there is nothing to do
+        ucode_dest_entry = self.section.FindEntryType(
+            'u-boot-spl-with-ucode-ptr')
         if not ucode_dest_entry or not ucode_dest_entry.target_pos:
-            ucode_dest_entry = self.image.FindEntryType('u-boot-with-ucode-ptr')
+            ucode_dest_entry = self.section.FindEntryType(
+                'u-boot-with-ucode-ptr')
         if not ucode_dest_entry or not ucode_dest_entry.target_pos:
             return True
 
diff --git a/tools/binman/etype/u_boot_img.py b/tools/binman/etype/u_boot_img.py
index d5f1eb3..6e0b736 100644
--- a/tools/binman/etype/u_boot_img.py
+++ b/tools/binman/etype/u_boot_img.py
@@ -9,8 +9,8 @@
 from blob import Entry_blob
 
 class Entry_u_boot_img(Entry_blob):
-    def __init__(self, image, etype, node):
-        Entry_blob.__init__(self, image, etype, node)
+    def __init__(self, section, etype, node):
+        Entry_blob.__init__(self, section, etype, node)
 
     def GetDefaultFilename(self):
         return 'u-boot.img'
diff --git a/tools/binman/etype/u_boot_nodtb.py b/tools/binman/etype/u_boot_nodtb.py
index 183b897..ca9e53a 100644
--- a/tools/binman/etype/u_boot_nodtb.py
+++ b/tools/binman/etype/u_boot_nodtb.py
@@ -9,8 +9,8 @@
 from blob import Entry_blob
 
 class Entry_u_boot_nodtb(Entry_blob):
-    def __init__(self, image, etype, node):
-        Entry_blob.__init__(self, image, etype, node)
+    def __init__(self, section, etype, node):
+        Entry_blob.__init__(self, section, etype, node)
 
     def GetDefaultFilename(self):
         return 'u-boot-nodtb.bin'
diff --git a/tools/binman/etype/u_boot_spl.py b/tools/binman/etype/u_boot_spl.py
index 6a1c123..9edd2da 100644
--- a/tools/binman/etype/u_boot_spl.py
+++ b/tools/binman/etype/u_boot_spl.py
@@ -11,12 +11,12 @@
 from blob import Entry_blob
 
 class Entry_u_boot_spl(Entry_blob):
-    def __init__(self, image, etype, node):
-        Entry_blob.__init__(self, image, etype, node)
+    def __init__(self, section, etype, node):
+        Entry_blob.__init__(self, section, etype, node)
         self.elf_fname = 'spl/u-boot-spl'
 
     def GetDefaultFilename(self):
         return 'spl/u-boot-spl.bin'
 
-    def WriteSymbols(self, image):
-        elf.LookupAndWriteSymbols(self.elf_fname, self, image)
+    def WriteSymbols(self, section):
+        elf.LookupAndWriteSymbols(self.elf_fname, self, section)
diff --git a/tools/binman/etype/u_boot_spl_bss_pad.py b/tools/binman/etype/u_boot_spl_bss_pad.py
index d14122b..3d2dea2 100644
--- a/tools/binman/etype/u_boot_spl_bss_pad.py
+++ b/tools/binman/etype/u_boot_spl_bss_pad.py
@@ -14,8 +14,8 @@
 import tools
 
 class Entry_u_boot_spl_bss_pad(Entry_blob):
-    def __init__(self, image, etype, node):
-        Entry_blob.__init__(self, image, etype, node)
+    def __init__(self, section, etype, node):
+        Entry_blob.__init__(self, section, etype, node)
 
     def ObtainContents(self):
         fname = tools.GetInputFilename('spl/u-boot-spl')
diff --git a/tools/binman/etype/u_boot_spl_dtb.py b/tools/binman/etype/u_boot_spl_dtb.py
index 43d2377..eefa1ff 100644
--- a/tools/binman/etype/u_boot_spl_dtb.py
+++ b/tools/binman/etype/u_boot_spl_dtb.py
@@ -9,8 +9,8 @@
 from blob import Entry_blob
 
 class Entry_u_boot_spl_dtb(Entry_blob):
-    def __init__(self, image, etype, node):
-        Entry_blob.__init__(self, image, etype, node)
+    def __init__(self, section, etype, node):
+        Entry_blob.__init__(self, section, etype, node)
 
     def GetDefaultFilename(self):
         return 'spl/u-boot-spl.dtb'
diff --git a/tools/binman/etype/u_boot_spl_nodtb.py b/tools/binman/etype/u_boot_spl_nodtb.py
index 5b058b4..99e56eb 100644
--- a/tools/binman/etype/u_boot_spl_nodtb.py
+++ b/tools/binman/etype/u_boot_spl_nodtb.py
@@ -9,8 +9,8 @@
 from blob import Entry_blob
 
 class Entry_u_boot_spl_nodtb(Entry_blob):
-    def __init__(self, image, etype, node):
-        Entry_blob.__init__(self, image, etype, node)
+    def __init__(self, section, etype, node):
+        Entry_blob.__init__(self, section, etype, node)
 
     def GetDefaultFilename(self):
         return 'spl/u-boot-spl-nodtb.bin'
diff --git a/tools/binman/etype/u_boot_spl_with_ucode_ptr.py b/tools/binman/etype/u_boot_spl_with_ucode_ptr.py
index 1e3181a..0dfe268 100644
--- a/tools/binman/etype/u_boot_spl_with_ucode_ptr.py
+++ b/tools/binman/etype/u_boot_spl_with_ucode_ptr.py
@@ -19,8 +19,8 @@
     See Entry_u_boot_ucode for full details of the entries involved in this
     process.
     """
-    def __init__(self, image, etype, node):
-        Entry_u_boot_with_ucode_ptr.__init__(self, image, etype, node)
+    def __init__(self, section, etype, node):
+        Entry_u_boot_with_ucode_ptr.__init__(self, section, etype, node)
         self.elf_fname = 'spl/u-boot-spl'
 
     def GetDefaultFilename(self):
diff --git a/tools/binman/etype/u_boot_ucode.py b/tools/binman/etype/u_boot_ucode.py
index 10130a2..3a0cff7 100644
--- a/tools/binman/etype/u_boot_ucode.py
+++ b/tools/binman/etype/u_boot_ucode.py
@@ -51,13 +51,13 @@
             the Entry_u_boot_dtb_with_ucode entry, and uses it as the
             contents of this entry.
     """
-    def __init__(self, image, etype, node):
-        Entry_blob.__init__(self, image, etype, node)
+    def __init__(self, section, etype, node):
+        Entry_blob.__init__(self, section, etype, node)
 
     def ObtainContents(self):
-        # If the image does not need microcode, there is nothing to do
-        ucode_dest_entry = self.image.FindEntryType('u-boot-with-ucode-ptr')
-        ucode_dest_entry_spl = self.image.FindEntryType(
+        # If the section does not need microcode, there is nothing to do
+        ucode_dest_entry = self.section.FindEntryType('u-boot-with-ucode-ptr')
+        ucode_dest_entry_spl = self.section.FindEntryType(
             'u-boot-spl-with-ucode-ptr')
         if ((not ucode_dest_entry or not ucode_dest_entry.target_pos) and
             (not ucode_dest_entry_spl or not ucode_dest_entry_spl.target_pos)):
@@ -65,12 +65,12 @@
             return True
 
         # Get the microcode from the device tree entry
-        fdt_entry = self.image.FindEntryType('u-boot-dtb-with-ucode')
+        fdt_entry = self.section.FindEntryType('u-boot-dtb-with-ucode')
         if not fdt_entry or not fdt_entry.ucode_data:
             return False
 
         if not fdt_entry.collate:
-            # This section can be empty
+            # This binary can be empty
             self.data = ''
             return True
 
diff --git a/tools/binman/etype/u_boot_with_ucode_ptr.py b/tools/binman/etype/u_boot_with_ucode_ptr.py
index 04b9f7c..41c2ded 100644
--- a/tools/binman/etype/u_boot_with_ucode_ptr.py
+++ b/tools/binman/etype/u_boot_with_ucode_ptr.py
@@ -20,8 +20,8 @@
     See Entry_u_boot_ucode for full details of the 3 entries involved in this
     process.
     """
-    def __init__(self, image, etype, node):
-        Entry_blob.__init__(self, image, etype, node)
+    def __init__(self, section, etype, node):
+        Entry_blob.__init__(self, section, etype, node)
         self.elf_fname = 'u-boot'
         self.target_pos = None
 
@@ -45,24 +45,24 @@
             return
 
         # Get the position of the microcode
-        ucode_entry = self.image.FindEntryType('u-boot-ucode')
+        ucode_entry = self.section.FindEntryType('u-boot-ucode')
         if not ucode_entry:
             self.Raise('Cannot find microcode region u-boot-ucode')
 
-        # Check the target pos is in the image. If it is not, then U-Boot is
+        # Check the target pos is in the section. If it is not, then U-Boot is
         # being linked incorrectly, or is being placed at the wrong position
-        # in the image.
+        # in the section.
         #
-        # The image must be set up so that U-Boot is placed at the
+        # The section must be set up so that U-Boot is placed at the
         # flash address to which it is linked. For example, if
         # CONFIG_SYS_TEXT_BASE is 0xfff00000, and the ROM is 8MB, then
-        # the U-Boot region must start at position 7MB in the image. In this
+        # the U-Boot region must start at position 7MB in the section. In this
         # case the ROM starts at 0xff800000, so the position of the first
-        # entry in the image corresponds to that.
+        # entry in the section corresponds to that.
         if (self.target_pos < self.pos or
                 self.target_pos >= self.pos + self.size):
             self.Raise('Microcode pointer _dt_ucode_base_size at %08x is '
-                'outside the image ranging from %08x to %08x' %
+                'outside the section ranging from %08x to %08x' %
                 (self.target_pos, self.pos, self.pos + self.size))
 
         # Get the microcode, either from u-boot-ucode or u-boot-dtb-with-ucode.
@@ -72,7 +72,7 @@
         if ucode_entry.size:
             pos, size = ucode_entry.pos, ucode_entry.size
         else:
-            dtb_entry = self.image.FindEntryType('u-boot-dtb-with-ucode')
+            dtb_entry = self.section.FindEntryType('u-boot-dtb-with-ucode')
             if not dtb_entry:
                 self.Raise('Cannot find microcode region u-boot-dtb-with-ucode')
             pos = dtb_entry.pos + dtb_entry.ucode_offset
diff --git a/tools/binman/etype/x86_start16.py b/tools/binman/etype/x86_start16.py
index 01e5b8b..23d27f0 100644
--- a/tools/binman/etype/x86_start16.py
+++ b/tools/binman/etype/x86_start16.py
@@ -9,8 +9,8 @@
 from blob import Entry_blob
 
 class Entry_x86_start16(Entry_blob):
-    def __init__(self, image, etype, node):
-        Entry_blob.__init__(self, image, etype, node)
+    def __init__(self, section, etype, node):
+        Entry_blob.__init__(self, section, etype, node)
 
     def GetDefaultFilename(self):
         return 'u-boot-x86-16bit.bin'
diff --git a/tools/binman/etype/x86_start16_spl.py b/tools/binman/etype/x86_start16_spl.py
index f0abbf0..176420b 100644
--- a/tools/binman/etype/x86_start16_spl.py
+++ b/tools/binman/etype/x86_start16_spl.py
@@ -9,8 +9,8 @@
 from blob import Entry_blob
 
 class Entry_x86_start16_spl(Entry_blob):
-    def __init__(self, image, etype, node):
-        Entry_blob.__init__(self, image, etype, node)
+    def __init__(self, section, etype, node):
+        Entry_blob.__init__(self, section, etype, node)
 
     def GetDefaultFilename(self):
         return 'spl/u-boot-x86-16bit-spl.bin'
diff --git a/tools/binman/ftest.py b/tools/binman/ftest.py
index a3abbc4b..eb8a079 100644
--- a/tools/binman/ftest.py
+++ b/tools/binman/ftest.py
@@ -146,15 +146,19 @@
         # options.verbosity = tout.DEBUG
         return control.Binman(options, args)
 
-    def _DoTestFile(self, fname, debug=False):
+    def _DoTestFile(self, fname, debug=False, map=False):
         """Run binman with a given test file
 
         Args:
-            fname: Device tree source filename to use (e.g. 05_simple.dts)
+            fname: Device-tree source filename to use (e.g. 05_simple.dts)
+            debug: True to enable debugging output
+            map: True to output map files for the images
         """
         args = ['-p', '-I', self._indir, '-d', self.TestFile(fname)]
         if debug:
             args.append('-D')
+        if map:
+            args.append('-m')
         return self._DoBinman(*args)
 
     def _SetupDtb(self, fname, outfile='u-boot.dtb'):
@@ -165,10 +169,10 @@
 
         Args:
             fname: Filename of .dts file to read
-            outfile: Output filename for compiled device tree binary
+            outfile: Output filename for compiled device-tree binary
 
         Returns:
-            Contents of device tree binary
+            Contents of device-tree binary
         """
         if not self._output_setup:
             tools.PrepareOutputDir(self._indir, True)
@@ -179,7 +183,7 @@
             TestFunctional._MakeInputFile(outfile, data)
             return data
 
-    def _DoReadFileDtb(self, fname, use_real_dtb=False):
+    def _DoReadFileDtb(self, fname, use_real_dtb=False, map=False):
         """Run binman and return the resulting image
 
         This runs binman with a given test file and then reads the resulting
@@ -189,16 +193,18 @@
         Raises an assertion failure if binman returns a non-zero exit code.
 
         Args:
-            fname: Device tree source filename to use (e.g. 05_simple.dts)
+            fname: Device-tree source filename to use (e.g. 05_simple.dts)
             use_real_dtb: True to use the test file as the contents of
                 the u-boot-dtb entry. Normally this is not needed and the
                 test contents (the U_BOOT_DTB_DATA string) can be used.
                 But in some test we need the real contents.
+            map: True to output map files for the images
 
         Returns:
             Tuple:
                 Resulting image contents
                 Device tree contents
+                Map data showing contents of image (or None if none)
         """
         dtb_data = None
         # Use the compiled test file as the u-boot-dtb input
@@ -206,22 +212,36 @@
             dtb_data = self._SetupDtb(fname)
 
         try:
-            retcode = self._DoTestFile(fname)
+            retcode = self._DoTestFile(fname, map=map)
             self.assertEqual(0, retcode)
 
             # Find the (only) image, read it and return its contents
             image = control.images['image']
             fname = tools.GetOutputFilename('image.bin')
             self.assertTrue(os.path.exists(fname))
+            if map:
+                map_fname = tools.GetOutputFilename('image.map')
+                with open(map_fname) as fd:
+                    map_data = fd.read()
+            else:
+                map_data = None
             with open(fname) as fd:
-                return fd.read(), dtb_data
+                return fd.read(), dtb_data, map_data
         finally:
             # Put the test file back
             if use_real_dtb:
                 TestFunctional._MakeInputFile('u-boot.dtb', U_BOOT_DTB_DATA)
 
     def _DoReadFile(self, fname, use_real_dtb=False):
-        """Helper function which discards the device-tree binary"""
+        """Helper function which discards the device-tree binary
+
+        Args:
+            fname: Device-tree source filename to use (e.g. 05_simple.dts)
+            use_real_dtb: True to use the test file as the contents of
+                the u-boot-dtb entry. Normally this is not needed and the
+                test contents (the U_BOOT_DTB_DATA string) can be used.
+                But in some test we need the real contents.
+        """
         return self._DoReadFileDtb(fname, use_real_dtb)[0]
 
     @classmethod
@@ -270,13 +290,13 @@
             pos += entry.size
 
     def GetFdtLen(self, dtb):
-        """Get the totalsize field from a device tree binary
+        """Get the totalsize field from a device-tree binary
 
         Args:
-            dtb: Device tree binary contents
+            dtb: Device-tree binary contents
 
         Returns:
-            Total size of device tree binary, from the header
+            Total size of device-tree binary, from the header
         """
         return struct.unpack('>L', dtb[4:8])[0]
 
@@ -326,7 +346,7 @@
                 str(e.exception))
 
     def testMissingDt(self):
-        """Test that an invalid device tree file generates an error"""
+        """Test that an invalid device-tree file generates an error"""
         with self.assertRaises(Exception) as e:
             self._RunBinman('-d', 'missing_file')
         # We get one error from libfdt, and a different one from fdtget.
@@ -334,7 +354,7 @@
                            'No such file or directory'], str(e.exception))
 
     def testBrokenDt(self):
-        """Test that an invalid device tree source file generates an error
+        """Test that an invalid device-tree source file generates an error
 
         Since this is a source file it should be compiled and the error
         will come from the device-tree compiler (dtc).
@@ -413,7 +433,7 @@
         self.assertEqual(0, retcode)
         self.assertIn('image', control.images)
         image = control.images['image']
-        entries = image._entries
+        entries = image.GetEntries()
         self.assertEqual(5, len(entries))
 
         # First u-boot
@@ -456,7 +476,7 @@
         self.assertEqual(0, retcode)
         self.assertIn('image', control.images)
         image = control.images['image']
-        entries = image._entries
+        entries = image.GetEntries()
         self.assertEqual(5, len(entries))
 
         # First u-boot with padding before and after
@@ -540,7 +560,7 @@
         """Test that entries which overflow the image size are detected"""
         with self.assertRaises(ValueError) as e:
             self._DoTestFile('16_pack_image_overflow.dts')
-        self.assertIn("Image '/binman': contents size 0x4 (4) exceeds image "
+        self.assertIn("Section '/binman': contents size 0x4 (4) exceeds section "
                       "size 0x3 (3)", str(e.exception))
 
     def testPackImageSize(self):
@@ -563,14 +583,14 @@
         """Test that invalid image alignment is detected"""
         with self.assertRaises(ValueError) as e:
             self._DoTestFile('19_pack_inv_image_align.dts')
-        self.assertIn("Image '/binman': Size 0x7 (7) does not match "
+        self.assertIn("Section '/binman': Size 0x7 (7) does not match "
                       "align-size 0x8 (8)", str(e.exception))
 
     def testPackAlignPowerOf2(self):
         """Test that invalid image alignment is detected"""
         with self.assertRaises(ValueError) as e:
             self._DoTestFile('20_pack_inv_image_align_power2.dts')
-        self.assertIn("Image '/binman': Alignment size 131 must be a power of "
+        self.assertIn("Section '/binman': Alignment size 131 must be a power of "
                       "two", str(e.exception))
 
     def testImagePadByte(self):
@@ -620,7 +640,7 @@
         """Test that the end-at-4gb property requires a size property"""
         with self.assertRaises(ValueError) as e:
             self._DoTestFile('27_pack_4gb_no_size.dts')
-        self.assertIn("Image '/binman': Image size must be provided when "
+        self.assertIn("Section '/binman': Section size must be provided when "
                       "using end-at-4gb", str(e.exception))
 
     def testPackX86RomOutside(self):
@@ -628,7 +648,7 @@
         with self.assertRaises(ValueError) as e:
             self._DoTestFile('28_pack_4gb_outside.dts')
         self.assertIn("Node '/binman/u-boot': Position 0x0 (0) is outside "
-                      "the image starting at 0xffffffe0 (4294967264)",
+                      "the section starting at 0xffffffe0 (4294967264)",
                       str(e.exception))
 
     def testPackX86Rom(self):
@@ -800,13 +820,13 @@
             self._DoReadFile('40_x86_ucode_not_in_image.dts', True)
         self.assertIn("Node '/binman/u-boot-with-ucode-ptr': Microcode "
                 "pointer _dt_ucode_base_size at fffffe14 is outside the "
-                "image ranging from 00000000 to 0000002e", str(e.exception))
+                "section ranging from 00000000 to 0000002e", str(e.exception))
 
     def testWithoutMicrocode(self):
         """Test that we can cope with an image without microcode (e.g. qemu)"""
         with open(self.TestFile('u_boot_no_ucode_ptr')) as fd:
             TestFunctional._MakeInputFile('u-boot', fd.read())
-        data, dtb = self._DoReadFileDtb('44_x86_optional_ucode.dts', True)
+        data, dtb, _ = self._DoReadFileDtb('44_x86_optional_ucode.dts', True)
 
         # Now check the device tree has no microcode
         self.assertEqual(U_BOOT_NODTB_DATA, data[:len(U_BOOT_NODTB_DATA)])
@@ -823,7 +843,7 @@
         """Test that microcode must be placed within the image"""
         with self.assertRaises(ValueError) as e:
             self._DoReadFile('41_unknown_pos_size.dts', True)
-        self.assertIn("Image '/binman': Unable to set pos/size for unknown "
+        self.assertIn("Section '/binman': Unable to set pos/size for unknown "
                 "entry 'invalid-entry'", str(e.exception))
 
     def testPackFsp(self):
@@ -909,6 +929,36 @@
                     sym_values + U_BOOT_SPL_DATA[16:])
         self.assertEqual(expected, data)
 
+    def testPackUnitAddress(self):
+        """Test that we support multiple binaries with the same name"""
+        data = self._DoReadFile('54_unit_address.dts')
+        self.assertEqual(U_BOOT_DATA + U_BOOT_DATA, data)
+
+    def testSections(self):
+        """Basic test of sections"""
+        data = self._DoReadFile('55_sections.dts')
+        expected = U_BOOT_DATA + '!' * 12 + U_BOOT_DATA + 'a' * 12 + '&' * 8
+        self.assertEqual(expected, data)
+
+    def testMap(self):
+        """Tests outputting a map of the images"""
+        _, _, map_data = self._DoReadFileDtb('55_sections.dts', map=True)
+        self.assertEqual('''Position      Size  Name
+00000000  00000010  section@0
+ 00000000  00000004  u-boot
+00000010  00000010  section@1
+ 00000000  00000004  u-boot
+''', map_data)
+
+    def testNamePrefix(self):
+        """Tests that name prefixes are used"""
+        _, _, map_data = self._DoReadFileDtb('56_name_prefix.dts', map=True)
+        self.assertEqual('''Position      Size  Name
+00000000  00000010  section@0
+ 00000000  00000004  ro-u-boot
+00000010  00000010  section@1
+ 00000000  00000004  rw-u-boot
+''', map_data)
 
 if __name__ == "__main__":
     unittest.main()
diff --git a/tools/binman/image.py b/tools/binman/image.py
index b10b188..835b66c 100644
--- a/tools/binman/image.py
+++ b/tools/binman/image.py
@@ -13,6 +13,7 @@
 import sys
 
 import fdt_util
+import bsection
 import tools
 
 class Image:
@@ -27,158 +28,36 @@
         _node: Node object that contains the image definition in device tree
         _name: Image name
         _size: Image size in bytes, or None if not known yet
-        _align_size: Image size alignment, or None
-        _pad_before: Number of bytes before the first entry starts. This
-            effectively changes the place where entry position 0 starts
-        _pad_after: Number of bytes after the last entry ends. The last
-            entry will finish on or before this boundary
-        _pad_byte: Byte to use to pad the image where there is no entry
         _filename: Output filename for image
-        _sort: True if entries should be sorted by position, False if they
-            must be in-order in the device tree description
-        _skip_at_start: Number of bytes before the first entry starts. These
-            effecively adjust the starting position of entries. For example,
-            if _pad_before is 16, then the first entry would start at 16.
-            An entry with pos = 20 would in fact be written at position 4
-            in the image file.
-        _end_4gb: Indicates that the image ends at the 4GB boundary. This is
-            used for x86 images, which want to use positions such that a
-             memory address (like 0xff800000) is the first entry position.
-             This causes _skip_at_start to be set to the starting memory
-             address.
-        _entries: OrderedDict() of entries
+        _sections: Sections present in this image (may be one or more)
+
+    Args:
+        test: True if this is being called from a test of Images. This this case
+            there is no device tree defining the structure of the section, so
+            we create a section manually.
     """
     def __init__(self, name, node, test=False):
-        global entry
-        global Entry
-        import entry
-        from entry import Entry
-
         self._node = node
         self._name = name
         self._size = None
-        self._align_size = None
-        self._pad_before = 0
-        self._pad_after = 0
-        self._pad_byte = 0
         self._filename = '%s.bin' % self._name
-        self._sort = False
-        self._skip_at_start = 0
-        self._end_4gb = False
-        self._entries = OrderedDict()
-
-        if not test:
+        if test:
+            self._section = bsection.Section('main-section', self._node, True)
+        else:
             self._ReadNode()
-            self._ReadEntries()
 
     def _ReadNode(self):
         """Read properties from the image node"""
         self._size = fdt_util.GetInt(self._node, 'size')
-        self._align_size = fdt_util.GetInt(self._node, 'align-size')
-        if tools.NotPowerOfTwo(self._align_size):
-            self._Raise("Alignment size %s must be a power of two" %
-                        self._align_size)
-        self._pad_before = fdt_util.GetInt(self._node, 'pad-before', 0)
-        self._pad_after = fdt_util.GetInt(self._node, 'pad-after', 0)
-        self._pad_byte = fdt_util.GetInt(self._node, 'pad-byte', 0)
         filename = fdt_util.GetString(self._node, 'filename')
         if filename:
             self._filename = filename
-        self._sort = fdt_util.GetBool(self._node, 'sort-by-pos')
-        self._end_4gb = fdt_util.GetBool(self._node, 'end-at-4gb')
-        if self._end_4gb and not self._size:
-            self._Raise("Image size must be provided when using end-at-4gb")
-        if self._end_4gb:
-            self._skip_at_start = 0x100000000 - self._size
-
-    def CheckSize(self):
-        """Check that the image contents does not exceed its size, etc."""
-        contents_size = 0
-        for entry in self._entries.values():
-            contents_size = max(contents_size, entry.pos + entry.size)
-
-        contents_size -= self._skip_at_start
-
-        size = self._size
-        if not size:
-            size = self._pad_before + contents_size + self._pad_after
-            size = tools.Align(size, self._align_size)
-
-        if self._size and contents_size > self._size:
-            self._Raise("contents size %#x (%d) exceeds image size %#x (%d)" %
-                       (contents_size, contents_size, self._size, self._size))
-        if not self._size:
-            self._size = size
-        if self._size != tools.Align(self._size, self._align_size):
-            self._Raise("Size %#x (%d) does not match align-size %#x (%d)" %
-                  (self._size, self._size, self._align_size, self._align_size))
-
-    def _Raise(self, msg):
-        """Raises an error for this image
-
-        Args:
-            msg: Error message to use in the raise string
-        Raises:
-            ValueError()
-        """
-        raise ValueError("Image '%s': %s" % (self._node.path, msg))
-
-    def GetPath(self):
-        """Get the path of an image (in the FDT)
-
-        Returns:
-            Full path of the node for this image
-        """
-        return self._node.path
-
-    def _ReadEntries(self):
-        for node in self._node.subnodes:
-            self._entries[node.name] = Entry.Create(self, node)
-
-    def FindEntryType(self, etype):
-        """Find an entry type in the image
-
-        Args:
-            etype: Entry type to find
-        Returns:
-            entry matching that type, or None if not found
-        """
-        for entry in self._entries.values():
-            if entry.etype == etype:
-                return entry
-        return None
+        self._section = bsection.Section('main-section', self._node)
 
     def GetEntryContents(self):
-        """Call ObtainContents() for each entry
-
-        This calls each entry's ObtainContents() a few times until they all
-        return True. We stop calling an entry's function once it returns
-        True. This allows the contents of one entry to depend on another.
-
-        After 3 rounds we give up since it's likely an error.
+        """Call ObtainContents() for the section
         """
-        todo = self._entries.values()
-        for passnum in range(3):
-            next_todo = []
-            for entry in todo:
-                if not entry.ObtainContents():
-                    next_todo.append(entry)
-            todo = next_todo
-            if not todo:
-                break
-
-    def _SetEntryPosSize(self, name, pos, size):
-        """Set the position and size of an entry
-
-        Args:
-            name: Entry name to update
-            pos: New position
-            size: New size
-        """
-        entry = self._entries.get(name)
-        if not entry:
-            self._Raise("Unable to set pos/size for unknown entry '%s'" % name)
-        entry.SetPositionSize(self._skip_at_start + pos, size)
+        self._section.GetEntryContents()
 
     def GetEntryPositions(self):
         """Handle entries that want to set the position/size of other entries
@@ -186,119 +65,44 @@
         This calls each entry's GetPositions() method. If it returns a list
         of entries to update, it updates them.
         """
-        for entry in self._entries.values():
-            pos_dict = entry.GetPositions()
-            for name, info in pos_dict.iteritems():
-                self._SetEntryPosSize(name, *info)
+        self._section.GetEntryPositions()
 
     def PackEntries(self):
         """Pack all entries into the image"""
-        pos = self._skip_at_start
-        for entry in self._entries.values():
-            pos = entry.Pack(pos)
+        self._section.PackEntries()
 
-    def _SortEntries(self):
-        """Sort entries by position"""
-        entries = sorted(self._entries.values(), key=lambda entry: entry.pos)
-        self._entries.clear()
-        for entry in entries:
-            self._entries[entry._node.name] = entry
+    def CheckSize(self):
+        """Check that the image contents does not exceed its size, etc."""
+        self._size = self._section.CheckSize()
 
     def CheckEntries(self):
         """Check that entries do not overlap or extend outside the image"""
-        if self._sort:
-            self._SortEntries()
-        pos = 0
-        prev_name = 'None'
-        for entry in self._entries.values():
-            if (entry.pos < self._skip_at_start or
-                entry.pos >= self._skip_at_start + self._size):
-                entry.Raise("Position %#x (%d) is outside the image starting "
-                            "at %#x (%d)" %
-                            (entry.pos, entry.pos, self._skip_at_start,
-                             self._skip_at_start))
-            if entry.pos < pos:
-                entry.Raise("Position %#x (%d) overlaps with previous entry '%s' "
-                            "ending at %#x (%d)" %
-                            (entry.pos, entry.pos, prev_name, pos, pos))
-            pos = entry.pos + entry.size
-            prev_name = entry.GetPath()
+        self._section.CheckEntries()
 
     def ProcessEntryContents(self):
         """Call the ProcessContents() method for each entry
 
         This is intended to adjust the contents as needed by the entry type.
         """
-        for entry in self._entries.values():
-            entry.ProcessContents()
+        self._section.ProcessEntryContents()
 
     def WriteSymbols(self):
         """Write symbol values into binary files for access at run time"""
-        for entry in self._entries.values():
-            entry.WriteSymbols(self)
+        self._section.WriteSymbols()
 
     def BuildImage(self):
         """Write the image to a file"""
         fname = tools.GetOutputFilename(self._filename)
         with open(fname, 'wb') as fd:
-            fd.write(chr(self._pad_byte) * self._size)
+            self._section.BuildSection(fd, 0)
 
-            for entry in self._entries.values():
-                data = entry.GetData()
-                fd.seek(self._pad_before + entry.pos - self._skip_at_start)
-                fd.write(data)
+    def GetEntries(self):
+        return self._section.GetEntries()
 
-    def LookupSymbol(self, sym_name, optional, msg):
-        """Look up a symbol in an ELF file
-
-        Looks up a symbol in an ELF file. Only entry types which come from an
-        ELF image can be used by this function.
-
-        At present the only entry property supported is pos.
-
-        Args:
-            sym_name: Symbol name in the ELF file to look up in the format
-                _binman_<entry>_prop_<property> where <entry> is the name of
-                the entry and <property> is the property to find (e.g.
-                _binman_u_boot_prop_pos). As a special case, you can append
-                _any to <entry> to have it search for any matching entry. E.g.
-                _binman_u_boot_any_prop_pos will match entries called u-boot,
-                u-boot-img and u-boot-nodtb)
-            optional: True if the symbol is optional. If False this function
-                will raise if the symbol is not found
-            msg: Message to display if an error occurs
-
-        Returns:
-            Value that should be assigned to that symbol, or None if it was
-                optional and not found
-
-        Raises:
-            ValueError if the symbol is invalid or not found, or references a
-                property which is not supported
-        """
-        m = re.match(r'^_binman_(\w+)_prop_(\w+)$', sym_name)
-        if not m:
-            raise ValueError("%s: Symbol '%s' has invalid format" %
-                             (msg, sym_name))
-        entry_name, prop_name = m.groups()
-        entry_name = entry_name.replace('_', '-')
-        entry = self._entries.get(entry_name)
-        if not entry:
-            if entry_name.endswith('-any'):
-                root = entry_name[:-4]
-                for name in self._entries:
-                    if name.startswith(root):
-                        rest = name[len(root):]
-                        if rest in ['', '-img', '-nodtb']:
-                            entry = self._entries[name]
-        if not entry:
-            err = ("%s: Entry '%s' not found in list (%s)" %
-                   (msg, entry_name, ','.join(self._entries.keys())))
-            if optional:
-                print('Warning: %s' % err, file=sys.stderr)
-                return None
-            raise ValueError(err)
-        if prop_name == 'pos':
-            return entry.pos
-        else:
-            raise ValueError("%s: No such property '%s'" % (msg, prop_name))
+    def WriteMap(self):
+        """Write a map of the image to a .map file"""
+        filename = '%s.map' % self._name
+        fname = tools.GetOutputFilename(filename)
+        with open(fname, 'w') as fd:
+            print('%8s  %8s  %s' % ('Position', 'Size', 'Name'), file=fd)
+            self._section.WriteMap(fd, 0)
diff --git a/tools/binman/image_test.py b/tools/binman/image_test.py
index 44a5a2c..45dd237 100644
--- a/tools/binman/image_test.py
+++ b/tools/binman/image_test.py
@@ -12,25 +12,28 @@
 class TestImage(unittest.TestCase):
     def testInvalidFormat(self):
         image = Image('name', 'node', test=True)
+        section = image._section
         with self.assertRaises(ValueError) as e:
-            image.LookupSymbol('_binman_something_prop_', False, 'msg')
+            section.LookupSymbol('_binman_something_prop_', False, 'msg')
         self.assertIn(
             "msg: Symbol '_binman_something_prop_' has invalid format",
             str(e.exception))
 
     def testMissingSymbol(self):
         image = Image('name', 'node', test=True)
-        image._entries = {}
+        section = image._section
+        section._entries = {}
         with self.assertRaises(ValueError) as e:
-            image.LookupSymbol('_binman_type_prop_pname', False, 'msg')
+            section.LookupSymbol('_binman_type_prop_pname', False, 'msg')
         self.assertIn("msg: Entry 'type' not found in list ()",
                       str(e.exception))
 
     def testMissingSymbolOptional(self):
         image = Image('name', 'node', test=True)
-        image._entries = {}
+        section = image._section
+        section._entries = {}
         with capture_sys_output() as (stdout, stderr):
-            val = image.LookupSymbol('_binman_type_prop_pname', True, 'msg')
+            val = section.LookupSymbol('_binman_type_prop_pname', True, 'msg')
         self.assertEqual(val, None)
         self.assertEqual("Warning: msg: Entry 'type' not found in list ()\n",
                          stderr.getvalue())
@@ -38,7 +41,8 @@
 
     def testBadProperty(self):
         image = Image('name', 'node', test=True)
-        image._entries = {'u-boot': 1}
+        section = image._section
+        section._entries = {'u-boot': 1}
         with self.assertRaises(ValueError) as e:
-            image.LookupSymbol('_binman_u_boot_prop_bad', False, 'msg')
+            section.LookupSymbol('_binman_u_boot_prop_bad', False, 'msg')
         self.assertIn("msg: No such property 'bad", str(e.exception))
diff --git a/tools/binman/test/54_unit_address.dts b/tools/binman/test/54_unit_address.dts
new file mode 100644
index 0000000..3216dbb
--- /dev/null
+++ b/tools/binman/test/54_unit_address.dts
@@ -0,0 +1,15 @@
+// SPDX-License-Identifier: GPL-2.0+
+
+/dts-v1/;
+
+/ {
+	#address-cells = <1>;
+	#size-cells = <1>;
+
+	binman {
+		u-boot@0 {
+		};
+		u-boot@1 {
+		};
+	};
+};
diff --git a/tools/binman/test/55_sections.dts b/tools/binman/test/55_sections.dts
new file mode 100644
index 0000000..2ada395
--- /dev/null
+++ b/tools/binman/test/55_sections.dts
@@ -0,0 +1,28 @@
+// SPDX-License-Identifier: GPL-2.0+
+
+/dts-v1/;
+
+/ {
+	#address-cells = <1>;
+	#size-cells = <1>;
+
+	binman {
+		pad-byte = <0x26>;
+		size = <0x28>;
+		section@0 {
+			read-only;
+			size = <0x10>;
+			pad-byte = <0x21>;
+
+			u-boot {
+			};
+		};
+		section@1 {
+			size = <0x10>;
+			pad-byte = <0x61>;
+
+			u-boot {
+			};
+		};
+	};
+};
diff --git a/tools/binman/test/56_name_prefix.dts b/tools/binman/test/56_name_prefix.dts
new file mode 100644
index 0000000..f38c80e
--- /dev/null
+++ b/tools/binman/test/56_name_prefix.dts
@@ -0,0 +1,30 @@
+// SPDX-License-Identifier: GPL-2.0+
+
+/dts-v1/;
+
+/ {
+	#address-cells = <1>;
+	#size-cells = <1>;
+
+	binman {
+		pad-byte = <0x26>;
+		size = <0x28>;
+		section@0 {
+			read-only;
+			name-prefix = "ro-";
+			size = <0x10>;
+			pad-byte = <0x21>;
+
+			u-boot {
+			};
+		};
+		section@1 {
+			name-prefix = "rw-";
+			size = <0x10>;
+			pad-byte = <0x61>;
+
+			u-boot {
+			};
+		};
+	};
+};
diff --git a/tools/buildman/README b/tools/buildman/README
index 6c43c54..7660190 100644
--- a/tools/buildman/README
+++ b/tools/buildman/README
@@ -1008,6 +1008,34 @@
 option to Kconfig. To disable this behaviour, use --squash-config-y.
 
 
+Checking the environment
+========================
+
+When converting CONFIG options which manipulate the default environment,
+a common requirement is to check that the default environment has not
+changed due to the conversion. Buildman supports this with the -U option,
+used after a build. This shows differences in the default environment
+between one commit and the next.
+
+For example:
+
+$ buildman -b squash brppt1 -sU
+boards.cfg is up to date. Nothing to do.
+Summary of 2 commits for 3 boards (3 threads, 3 jobs per thread)
+01: Migrate bootlimit to Kconfig
+02: Squashed commit of the following:
+   c brppt1_mmc: altbootcmd=mmc dev 1; run mmcboot0; -> mmc dev 1; run mmcboot0
+   c brppt1_spi: altbootcmd=mmc dev 1; run mmcboot0; -> mmc dev 1; run mmcboot0
+   + brppt1_nand: altbootcmd=run usbscript
+   - brppt1_nand:  altbootcmd=run usbscript
+(no errors to report)
+
+This shows that commit 2 modified the value of 'altbootcmd' for 'brppt1_mmc'
+and 'brppt1_spi', removing a trailing semicolon. 'brppt1_nand' gained an a
+value for 'altbootcmd', but lost one for ' altbootcmd'.
+
+The -U option uses the u-boot.env files which are produced by a build.
+
 Other options
 =============
 
diff --git a/tools/buildman/builder.py b/tools/buildman/builder.py
index 2ccdee0..a5a2ffd 100644
--- a/tools/buildman/builder.py
+++ b/tools/buildman/builder.py
@@ -127,6 +127,15 @@
                 val = val ^ hash(key) & hash(value)
         return val
 
+class Environment:
+    """Holds information about environment variables for a board."""
+    def __init__(self, target):
+        self.target = target
+        self.environment = {}
+
+    def Add(self, key, value):
+        self.environment[key] = value
+
 class Builder:
     """Class for building U-Boot for a particular commit.
 
@@ -199,13 +208,17 @@
                     value is itself a dictionary:
                         key: config name
                         value: config value
+            environment: Dictionary keyed by environment variable, Each
+                     value is the value of environment variable.
         """
-        def __init__(self, rc, err_lines, sizes, func_sizes, config):
+        def __init__(self, rc, err_lines, sizes, func_sizes, config,
+                     environment):
             self.rc = rc
             self.err_lines = err_lines
             self.sizes = sizes
             self.func_sizes = func_sizes
             self.config = config
+            self.environment = environment
 
     def __init__(self, toolchains, base_dir, git_dir, num_threads, num_jobs,
                  gnu_make='make', checkout=True, show_unknown=True, step=1,
@@ -310,7 +323,8 @@
 
     def SetDisplayOptions(self, show_errors=False, show_sizes=False,
                           show_detail=False, show_bloat=False,
-                          list_error_boards=False, show_config=False):
+                          list_error_boards=False, show_config=False,
+                          show_environment=False):
         """Setup display options for the builder.
 
         show_errors: True to show summarised error/warning info
@@ -319,6 +333,7 @@
         show_bloat: Show detail for each function
         list_error_boards: Show the boards which caused each error/warning
         show_config: Show config deltas
+        show_environment: Show environment deltas
         """
         self._show_errors = show_errors
         self._show_sizes = show_sizes
@@ -326,6 +341,7 @@
         self._show_bloat = show_bloat
         self._list_error_boards = list_error_boards
         self._show_config = show_config
+        self._show_environment = show_environment
 
     def _AddTimestamp(self):
         """Add a new timestamp to the list and record the build period.
@@ -609,8 +625,33 @@
                     config[key] = value
         return config
 
+    def _ProcessEnvironment(self, fname):
+        """Read in a uboot.env file
+
+        This function reads in environment variables from a file.
+
+        Args:
+            fname: Filename to read
+
+        Returns:
+            Dictionary:
+                key: environment variable (e.g. bootlimit)
+                value: value of environment variable (e.g. 1)
+        """
+        environment = {}
+        if os.path.exists(fname):
+            with open(fname) as fd:
+                for line in fd.read().split('\0'):
+                    try:
+                        key, value = line.split('=', 1)
+                        environment[key] = value
+                    except ValueError:
+                        # ignore lines we can't parse
+                        pass
+        return environment
+
     def GetBuildOutcome(self, commit_upto, target, read_func_sizes,
-                        read_config):
+                        read_config, read_environment):
         """Work out the outcome of a build.
 
         Args:
@@ -618,6 +659,7 @@
             target: Target board to check
             read_func_sizes: True to read function size information
             read_config: True to read .config and autoconf.h files
+            read_environment: True to read uboot.env files
 
         Returns:
             Outcome object
@@ -627,6 +669,7 @@
         sizes = {}
         func_sizes = {}
         config = {}
+        environment = {}
         if os.path.exists(done_file):
             with open(done_file, 'r') as fd:
                 return_code = int(fd.readline())
@@ -676,12 +719,18 @@
                     fname = os.path.join(output_dir, name)
                     config[name] = self._ProcessConfig(fname)
 
-            return Builder.Outcome(rc, err_lines, sizes, func_sizes, config)
+            if read_environment:
+                output_dir = self.GetBuildDir(commit_upto, target)
+                fname = os.path.join(output_dir, 'uboot.env')
+                environment = self._ProcessEnvironment(fname)
 
-        return Builder.Outcome(OUTCOME_UNKNOWN, [], {}, {}, {})
+            return Builder.Outcome(rc, err_lines, sizes, func_sizes, config,
+                                   environment)
+
+        return Builder.Outcome(OUTCOME_UNKNOWN, [], {}, {}, {}, {})
 
     def GetResultSummary(self, boards_selected, commit_upto, read_func_sizes,
-                         read_config):
+                         read_config, read_environment):
         """Calculate a summary of the results of building a commit.
 
         Args:
@@ -689,6 +738,7 @@
             commit_upto: Commit number to summarize (0..self.count-1)
             read_func_sizes: True to read function size information
             read_config: True to read .config and autoconf.h files
+            read_environment: True to read uboot.env files
 
         Returns:
             Tuple:
@@ -705,6 +755,9 @@
                     value is itself a dictionary:
                         key: config name
                         value: config value
+                Dictionary keyed by board.target. Each value is a dictionary:
+                    key: environment variable
+                    value: value of environment variable
         """
         def AddLine(lines_summary, lines_boards, line, board):
             line = line.rstrip()
@@ -720,10 +773,12 @@
         warn_lines_summary = []
         warn_lines_boards = {}
         config = {}
+        environment = {}
 
         for board in boards_selected.itervalues():
             outcome = self.GetBuildOutcome(commit_upto, board.target,
-                                           read_func_sizes, read_config)
+                                           read_func_sizes, read_config,
+                                           read_environment)
             board_dict[board.target] = outcome
             last_func = None
             last_was_warning = False
@@ -756,8 +811,14 @@
                         tconfig.Add(fname, key, value)
             config[board.target] = tconfig
 
+            tenvironment = Environment(board.target)
+            if outcome.environment:
+                for key, value in outcome.environment.iteritems():
+                    tenvironment.Add(key, value)
+            environment[board.target] = tenvironment
+
         return (board_dict, err_lines_summary, err_lines_boards,
-                warn_lines_summary, warn_lines_boards, config)
+                warn_lines_summary, warn_lines_boards, config, environment)
 
     def AddOutcome(self, board_dict, arch_list, changes, char, color):
         """Add an output to our list of outcomes for each architecture
@@ -810,12 +871,14 @@
         """
         self._base_board_dict = {}
         for board in board_selected:
-            self._base_board_dict[board] = Builder.Outcome(0, [], [], {}, {})
+            self._base_board_dict[board] = Builder.Outcome(0, [], [], {}, {},
+                                                           {})
         self._base_err_lines = []
         self._base_warn_lines = []
         self._base_err_line_boards = {}
         self._base_warn_line_boards = {}
         self._base_config = None
+        self._base_environment = None
 
     def PrintFuncSizeDetail(self, fname, old, new):
         grow, shrink, add, remove, up, down = 0, 0, 0, 0, 0, 0
@@ -1010,8 +1073,8 @@
 
     def PrintResultSummary(self, board_selected, board_dict, err_lines,
                            err_line_boards, warn_lines, warn_line_boards,
-                           config, show_sizes, show_detail, show_bloat,
-                           show_config):
+                           config, environment, show_sizes, show_detail,
+                           show_bloat, show_config, show_environment):
         """Compare results with the base results and display delta.
 
         Only boards mentioned in board_selected will be considered. This
@@ -1036,10 +1099,13 @@
                     value is itself a dictionary:
                         key: config name
                         value: config value
+            environment: Dictionary keyed by environment variable, Each
+                     value is the value of environment variable.
             show_sizes: Show image size deltas
             show_detail: Show detail for each board
             show_bloat: Show detail for each function
             show_config: Show config changes
+            show_environment: Show environment changes
         """
         def _BoardList(line, line_boards):
             """Helper function to get a line of boards containing a line
@@ -1188,6 +1254,36 @@
             self.PrintSizeSummary(board_selected, board_dict, show_detail,
                                   show_bloat)
 
+        if show_environment and self._base_environment:
+            lines = []
+
+            for target in board_dict:
+                if target not in board_selected:
+                    continue
+
+                tbase = self._base_environment[target]
+                tenvironment = environment[target]
+                environment_plus = {}
+                environment_minus = {}
+                environment_change = {}
+                base = tbase.environment
+                for key, value in tenvironment.environment.iteritems():
+                    if key not in base:
+                        environment_plus[key] = value
+                for key, value in base.iteritems():
+                    if key not in tenvironment.environment:
+                        environment_minus[key] = value
+                for key, value in base.iteritems():
+                    new_value = tenvironment.environment.get(key)
+                    if new_value and value != new_value:
+                        desc = '%s -> %s' % (value, new_value)
+                        environment_change[key] = desc
+
+                _AddConfig(lines, target, environment_plus, environment_minus,
+                           environment_change)
+
+            _OutputConfigInfo(lines)
+
         if show_config and self._base_config:
             summary = {}
             arch_config_plus = {}
@@ -1294,6 +1390,7 @@
         self._base_err_line_boards = err_line_boards
         self._base_warn_line_boards = warn_line_boards
         self._base_config = config
+        self._base_environment = environment
 
         # Get a list of boards that did not get built, if needed
         not_built = []
@@ -1306,10 +1403,11 @@
 
     def ProduceResultSummary(self, commit_upto, commits, board_selected):
             (board_dict, err_lines, err_line_boards, warn_lines,
-                    warn_line_boards, config) = self.GetResultSummary(
+             warn_line_boards, config, environment) = self.GetResultSummary(
                     board_selected, commit_upto,
                     read_func_sizes=self._show_bloat,
-                    read_config=self._show_config)
+                    read_config=self._show_config,
+                    read_environment=self._show_environment)
             if commits:
                 msg = '%02d: %s' % (commit_upto + 1,
                         commits[commit_upto].subject)
@@ -1317,8 +1415,8 @@
             self.PrintResultSummary(board_selected, board_dict,
                     err_lines if self._show_errors else [], err_line_boards,
                     warn_lines if self._show_errors else [], warn_line_boards,
-                    config, self._show_sizes, self._show_detail,
-                    self._show_bloat, self._show_config)
+                    config, environment, self._show_sizes, self._show_detail,
+                    self._show_bloat, self._show_config, self._show_environment)
 
     def ShowSummary(self, commits, board_selected):
         """Show a build summary for U-Boot for a given board list.
diff --git a/tools/buildman/builderthread.py b/tools/buildman/builderthread.py
index 0efe80d..c84ba6a 100644
--- a/tools/buildman/builderthread.py
+++ b/tools/buildman/builderthread.py
@@ -351,6 +351,16 @@
                     lines.append(size_result.stdout.splitlines()[1] + ' ' +
                                  rodata_size)
 
+            # Extract the environment from U-Boot and dump it out
+            cmd = ['%sobjcopy' % self.toolchain.cross, '-O', 'binary',
+                   '-j', '.rodata.default_environment',
+                   'env/built-in.o', 'uboot.env']
+            command.RunPipe([cmd], capture=True,
+                            capture_stderr=True, cwd=result.out_dir,
+                            raise_on_error=False, env=env)
+            ubootenv = os.path.join(result.out_dir, 'uboot.env')
+            self.CopyFiles(result.out_dir, build_dir, '', ['uboot.env'])
+
             # Write out the image sizes file. This is similar to the output
             # of binutil's 'size' utility, but it omits the header line and
             # adds an additional hex value at the end of each line for the
diff --git a/tools/buildman/cmdline.py b/tools/buildman/cmdline.py
index b8ddd47..e493b1a 100644
--- a/tools/buildman/cmdline.py
+++ b/tools/buildman/cmdline.py
@@ -92,6 +92,8 @@
           default=None, help='Number of builder threads to use')
     parser.add_option('-u', '--show_unknown', action='store_true',
           default=False, help='Show boards with unknown build result')
+    parser.add_option('-U', '--show-environment', action='store_true',
+          default=False, help='Show environment changes in summary')
     parser.add_option('-v', '--verbose', action='store_true',
           default=False, help='Show build results while the build progresses')
     parser.add_option('-V', '--verbose-build', action='store_true',
diff --git a/tools/buildman/control.py b/tools/buildman/control.py
index 4ac4386..bc08197 100644
--- a/tools/buildman/control.py
+++ b/tools/buildman/control.py
@@ -319,7 +319,8 @@
         builder.SetDisplayOptions(options.show_errors, options.show_sizes,
                                   options.show_detail, options.show_bloat,
                                   options.list_error_boards,
-                                  options.show_config)
+                                  options.show_config,
+                                  options.show_environment)
         if options.summary:
             builder.ShowSummary(commits, board_selected)
         else:
diff --git a/tools/buildman/func_test.py b/tools/buildman/func_test.py
index 9206fb2..363db9d 100644
--- a/tools/buildman/func_test.py
+++ b/tools/buildman/func_test.py
@@ -327,6 +327,9 @@
     def _HandleCommandObjdump(self, args):
         return command.CommandResult(return_code=0)
 
+    def _HandleCommandObjcopy(self, args):
+        return command.CommandResult(return_code=0)
+
     def _HandleCommandSize(self, args):
         return command.CommandResult(return_code=0)
 
@@ -359,6 +362,8 @@
             return self._HandleCommandNm(args)
         elif cmd.endswith('objdump'):
             return self._HandleCommandObjdump(args)
+        elif cmd.endswith('objcopy'):
+            return self._HandleCommandObjcopy(args)
         elif cmd.endswith( 'size'):
             return self._HandleCommandSize(args)
 
diff --git a/tools/patman/test.py b/tools/patman/test.py
index 343efc9..c7ba4e6 100644
--- a/tools/patman/test.py
+++ b/tools/patman/test.py
@@ -148,7 +148,7 @@
 --- /dev/null
 +++ b/common/bootstage.c
 @@ -0,0 +1,37 @@
-+/* SPDX-License-Identifier: GPL-2.0+ */
++// SPDX-License-Identifier: GPL-2.0+
 +/*
 + * Copyright (c) 2011, Google Inc. All rights reserved.
 + *