mirror of
https://github.com/openwrt/openwrt.git
synced 2024-12-29 10:08:59 +00:00
de59fc4540
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 <mail@david-bauer.net> (cherry picked from commit 8f9546f7b0a14f3afa813e39ed45c968ece24464)
90 lines
3.2 KiB
Python
Executable File
90 lines
3.2 KiB
Python
Executable File
#!/usr/bin/env python3
|
|
# SPDX-License-Identifier: MIT
|
|
# -*- coding: utf-8 -*-
|
|
|
|
# NETGEAR EX6150v2 padding tool
|
|
# (c) 2024 David Bauer <mail@david-bauer.net>
|
|
|
|
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)
|