forked from openwrt/openwrt
-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
ipq40xx: fix broken image generation for EX6150v2
All NETGEAR EX6150v2 validate the rootfs for which OpenWrt places a fakeheader at the position, where the bootloader expects it. Some EX6150v2 bootloaders do however make a broken assumption about where the rootfs starts. This is due to them calculating the rootfs start not based upon the kernel-length but the string-offset of the FIT-image. We have to be compatible with both this broken as well as the valid calculation. So we do relocate the FDT string section to a block-boundary and enlarge the FIT image to end at this boundary + BLOCKSIZE / 2. This way, both the broken as well as correct calculations do expect the rootfs-header at the same position. It is worth noting, that this is a rare edge-case in which only happens if the image-length as well as the start of the string-section are not placed in the same erase-block. This is an edge-case which happens very rarely (thus it was not spotted prior). Affected: - U-Boot 2012.07 (Jun 16 2016 - 11:59:37) Signed-off-by: David Bauer <[email protected]> (cherry picked from commit de59fc4)
- Loading branch information
1 parent
3287d82
commit d062070
Showing
2 changed files
with
96 additions
and
2 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,89 @@ | ||
#!/usr/bin/env python3 | ||
# SPDX-License-Identifier: MIT | ||
# -*- coding: utf-8 -*- | ||
|
||
# NETGEAR EX6150v2 padding tool | ||
# (c) 2024 David Bauer <[email protected]> | ||
|
||
import math | ||
import sys | ||
|
||
FLASH_BLOCK_SIZE = 64 * 1024 | ||
|
||
|
||
def read_field(data, offset): | ||
return data[offset + 3] | data[offset + 2] << 8 | data[offset + 1] << 16 | data[offset] << 24 | ||
|
||
|
||
if __name__ == '__main__': | ||
if len(sys.argv) != 3: | ||
print('Usage: {} <input-image> <output-image>'.format(sys.argv[0])) | ||
sys.exit(1) | ||
|
||
with open(sys.argv[1], 'rb') as f: | ||
data = f.read() | ||
|
||
file_len = len(data) | ||
|
||
# File-len in fdt header at offset 0x4 | ||
file_len_hdr = read_field(data, 0x4) | ||
# String offset in fdt header at offset 0xc | ||
str_off = read_field(data, 0xc) | ||
|
||
print("file_len={} hdr_file_len={} str_off={}".format(file_len, file_len_hdr, str_off)) | ||
|
||
# Off to NETGEAR calculations - Taken from u-boot source (cmd_dni.c:2145) | ||
# | ||
# rootfs_addr = (ntohl(hdr->ih_size)/CONFIG_SYS_FLASH_SECTOR_SIZE+1) * CONFIG_SYS_FLASH_SECTOR_SIZE + | ||
# 2*sizeof(image_header_t)-sizeof(image_header_t); | ||
# rootfs_addr = rootfs_addr - (0x80 - mem_addr); | ||
|
||
# NETGEAR did fuck up badly. The image uses a FIT header, while the calculation is done on a legacy header | ||
# assumption. 'ih_size' matches 'off_dt_strings' of a fdt_header. | ||
# From my observations, this seems to be fixed on newer bootloader versions. | ||
# However, we need to be compatible with both. | ||
|
||
# This presents a challenge: FDT_STR might end short of a block boundary, colliding with the rootfs_addr | ||
# | ||
# Our dirty solution: | ||
# - Move the string_table to match a block_boundary. | ||
# - Update the total file_len to end on 50% of a block boundary. | ||
# | ||
# This ensures all netgear calculations will be correct, regardless whether they are done based on the | ||
# 'off_dt_strings' or 'totalsize' fields of a fdt header. | ||
|
||
new_dt_strings = int((math.floor(file_len / FLASH_BLOCK_SIZE) + 2) * FLASH_BLOCK_SIZE) | ||
new_image_len = int(new_dt_strings + (FLASH_BLOCK_SIZE / 2)) | ||
new_file_len = int(new_dt_strings + FLASH_BLOCK_SIZE - 64) | ||
print(f"new_file_len={new_file_len} new_hdr_file_len={new_image_len} new_str_offset={new_dt_strings}") | ||
|
||
# Convert data to bytearray | ||
data = bytearray(data) | ||
|
||
# Enlarge byte-array to new size | ||
data.extend(bytearray(new_file_len - file_len)) | ||
|
||
# Assert that the new and old string-tables are at least 256 bytes apart. | ||
# We pad by two blocks, but let's be extra sure. | ||
assert new_dt_strings - str_off >= 256 | ||
|
||
# Move the string table to the new offset | ||
for i in range(0, 256): | ||
data[new_dt_strings + i] = data[str_off + i] | ||
data[str_off + i] = 0 | ||
|
||
# Update the string offset in the header | ||
data[0xc] = (new_dt_strings >> 24) & 0xFF | ||
data[0xd] = (new_dt_strings >> 16) & 0xFF | ||
data[0xe] = (new_dt_strings >> 8) & 0xFF | ||
data[0xf] = new_dt_strings & 0xFF | ||
|
||
# Update the file length in the header | ||
data[0x4] = (new_image_len >> 24) & 0xFF | ||
data[0x5] = (new_image_len >> 16) & 0xFF | ||
data[0x6] = (new_image_len >> 8) & 0xFF | ||
data[0x7] = new_image_len & 0xFF | ||
|
||
# Write the new file | ||
with open(sys.argv[1] + '.new', 'wb') as f: | ||
f.write(data) |