cros_bundle_firmware: Split RO and RW sections on eMMC
On the eMMC, if the flash map contains a wp-ro node to describe the size
of the read-only section, build a flasher that will flash the read-only
section to boot partition 1 and the read-write section to boot partition
2.
BUG=chromium:254233
BRANCH=none
TEST=On spring, cros_bundle_firmware -w usb -F sdmmc successfully writes
the RO section to boot partition 1, and the RW section to boot partition 2.
If /flash/wp-ro is removed from the flash map, the entire image is written
to boot partition 1, with a warning about being unable to detect the RO
section size.
SPI flashing is unaffected.
Change-Id: I061fb9b3c68b190c11dda2b09286453167c47681
Signed-off-by: Michael Pratt <mpratt@chromium.org>
Reviewed-on: https://gerrit.chromium.org/gerrit/62665
Reviewed-by: Simon Glass <sjg@chromium.org>
diff --git a/host/lib/write_firmware.py b/host/lib/write_firmware.py
index 96332ee..72eeeec 100644
--- a/host/lib/write_firmware.py
+++ b/host/lib/write_firmware.py
@@ -107,7 +107,7 @@
self._servo_port = int(servo)
self._out.Notice('Servo port %s' % str(self._servo_port))
- def _GetFlashScript(self, payload_size, flash_dest, checksum):
+ def _GetFlashScript(self, payload_size, flash_dest, checksum, ro_size=None):
"""Get the U-Boot boot command needed to flash U-Boot.
We leave a marker in the string for the load address of the image,
@@ -119,6 +119,8 @@
flash_dest: A dictionary of strings keyed by 'type' (nand, sdmmc,
or spi), 'bus', and 'dev'.
checksum: The checksum of the payload (an integer)
+ ro_size: Size of read-only partition. If set, split MMC image between
+ partition 1 (ro) and partition 2 (rw).
Returns:
A tuple containing:
@@ -126,8 +128,12 @@
embedded marker for the load address.
The marker string, which the caller should replace with the correct
load address as 8 hex digits, without changing its length.
+ The marker RW string, which the caller should replace with the correct
+ load address for the RW section as 8 hex digits, without changing
+ its length. This is only required if ro_size is set.
"""
replace_me = 'zsHEXYla'
+ replace_me_rw = 'zsHEXYrw'
page_size = 4096
boot_type = flash_dest['type']
if boot_type == 'sdmmc':
@@ -143,6 +149,17 @@
checksum,
'setenv _clear "echo Clearing RAM; mw.b ${address} 0 ${length}"',
]
+
+ if ro_size:
+ rw_size = payload_size - ro_size
+
+ cmds.extend([
+ 'setenv address_ro ${address}',
+ 'setenv address_rw 0x%s' % replace_me_rw,
+ 'setenv blocks_ro %#x' % (RoundUp(ro_size, page_size) / page_size),
+ 'setenv blocks_rw %#x' % (RoundUp(rw_size, page_size) / page_size),
+ ])
+
if boot_type == 'nand':
cmds.extend([
'setenv _init "echo Init NAND; nand info"',
@@ -161,15 +178,36 @@
cmds.extend([
'setenv _init "echo Init EMMC; mmc rescan"',
'setenv _erase "echo Erase EMMC; "',
- "setenv _write 'echo Write EMMC; mmc open 0 1;" \
- " mmc write ${address} 0 " \
- "${blocks};" \
- " mmc close 0 1'",
- "setenv _read 'echo Read EMMC; mmc open 0 1;" \
- " mmc read ${address} 0 " \
- "${blocks};" \
- " mmc close 0 1'",
])
+ if ro_size:
+ # Write RO section to partition 1, RW to partition 2
+ cmds.extend([
+ "setenv _write 'echo Write EMMC;" \
+ " mmc open 0 1;" \
+ " mmc write ${address_ro} 0 ${blocks_ro};" \
+ " mmc close 0 1;" \
+ " mmc open 0 2;" \
+ " mmc write ${address_rw} 0 ${blocks_rw};" \
+ " mmc close 0 2'",
+ "setenv _read 'echo Read EMMC;" \
+ " mmc open 0 1;" \
+ " mmc read ${address_ro} 0 ${blocks_ro};" \
+ " mmc close 0 1;" \
+ " mmc open 0 2;" \
+ " mmc read ${address_rw} 0 ${blocks_rw};" \
+ " mmc close 0 2'",
+ ])
+ else:
+ cmds.extend([
+ "setenv _write 'echo Write EMMC;" \
+ " mmc open 0 1;" \
+ " mmc write ${address} 0 ${blocks};" \
+ " mmc close 0 1'",
+ "setenv _read 'echo Read EMMC;" \
+ " mmc open 0 1;" \
+ " mmc read ${address} 0 ${blocks};" \
+ " mmc close 0 1'",
+ ])
else:
if flash_dest['bus'] is None:
flash_dest['bus'] = '0'
@@ -215,9 +253,33 @@
'fi',
])
script = '; '.join(cmds)
- return script, replace_me
+ return script, replace_me, replace_me_rw
- def _PrepareFlasher(self, uboot, payload, flash_dest):
+ def _ReplaceAddr(self, data, replace_me, replacement):
+ """Replace address in FDT
+
+ Detect and replace a placeholder address in the FDT.
+
+ Args:
+ data: FDT data to do replacement on
+ replace_me: String currently in FDT to replace
+ replacement: Replacement string
+
+ Returns:
+ Updated data with replacement
+ """
+ if len(replace_me) is not len(replacement):
+ raise ValueError("Internal error: replacement string '%s' length does "
+ "not match new string '%s'" % (replace_me, replacement))
+
+ matches = len(re.findall(replace_me, data))
+ if matches != 1:
+ raise ValueError("Internal error: replacement string '%s' already "
+ "exists in the fdt (%d matches)" % (replace_me, matches))
+
+ return re.sub(replace_me, replacement, data)
+
+ def _PrepareFlasher(self, uboot, payload, flash_dest, ro_size=None):
"""Get a flasher ready for sending to the board.
The flasher is an executable image consisting of:
@@ -233,6 +295,10 @@
payload: Full path to payload.
flash_dest: A dictionary of strings keyed by 'type' (nand, sdmmc,
or spi), 'bus', and 'dev'.
+ boot_type: the src for bootdevice (nand, sdmmc, or spi)
+ ro_size: Size of read-only partition on emmc. If set, indicates that
+ the image should be split, with half written to partition 1, and
+ half written to partition 2.
Returns:
Filename of the flasher binary created.
@@ -243,8 +309,8 @@
# Make sure that the checksum is not negative
checksum = binascii.crc32(payload_data) & 0xffffffff
- script, replace_me = self._GetFlashScript(len(payload_data), flash_dest,
- checksum)
+ script, replace_start, replace_rw = self._GetFlashScript(len(payload_data),
+ flash_dest, checksum, ro_size)
data = self._tools.ReadFile(uboot)
fdt.PutString('/config', 'bootcmd', script)
fdt_data = self._tools.ReadFile(fdt.fname)
@@ -268,14 +334,11 @@
load_address = self.text_base + payload_offset,
new_str = '%08x' % load_address
- if len(replace_me) is not len(new_str):
- raise ValueError("Internal error: replacement string '%s' length does "
- "not match new string '%s'" % (replace_me, new_str))
- matches = len(re.findall(replace_me, fdt_data))
- if matches != 1:
- raise ValueError("Internal error: replacement string '%s' already "
- "exists in the fdt (%d matches)" % (replace_me, matches))
- fdt_data = re.sub(replace_me, new_str, fdt_data)
+ fdt_data = self._ReplaceAddr(fdt_data, replace_start, new_str)
+
+ if ro_size:
+ new_str_rw = '%08x' % (load_address[0] + ro_size)
+ fdt_data = self._ReplaceAddr(fdt_data, replace_rw, new_str_rw)
# Now put it together.
data += fdt_data
@@ -588,7 +651,16 @@
flash_dest['bus'] = 1
if flash_dest['dev'] is None:
flash_dest['dev'] = 0
- image = self._PrepareFlasher(flash_uboot, payload, flash_dest)
+
+ # Try to determine RO section size
+ ro_size = None
+ if flash_dest['type'] == 'sdmmc':
+ try:
+ ro_size = self._fdt.GetFlashPartSize('wp', 'ro')
+ except (CmdError, ValueError):
+ self._out.Warning('Unable to detect RO section size')
+
+ image = self._PrepareFlasher(flash_uboot, payload, flash_dest, ro_size)
else:
bl1, bl2, image = payload_bl1, payload_bl2, payload_image