belkin-header.py 2.7 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394
  1. #!/usr/bin/python3
  2. # SPDX-License-Identifier: GPL-2.0-or-later
  3. #
  4. # Copyright (C) 2024 OpenWrt.org
  5. #
  6. # ./belkin-header.py <ImageFileIn> <ImageFileOut> <BelkinHeader> <BelkinModel>
  7. #
  8. # This script adds an image header for Belkin devices. As of now only Realtek
  9. # based switches of the Linksys LGS3xxC/LGS3xxMPC series are known to use this
  10. # format. It resembles a U-Boot legacy format image header, all data in network
  11. # byte order (aka natural aka big endian).
  12. #
  13. # Known values for BelkinHeader are
  14. #
  15. # 0x07800001 : RTL838x based switch
  16. # 0x07600001 : RTL93xx based switch
  17. #
  18. # Known values for BelkinModel are
  19. #
  20. # BKS-RTL83xx : RTL838x based switch
  21. # BKS-RTL93xx : RTL93xx based switch
  22. import argparse
  23. import os
  24. import zlib
  25. import array
  26. import sys
  27. import time
  28. VERSION1 = 1
  29. VERSION2 = 1
  30. VERSION3 = 2
  31. VERSION4 = 2
  32. COMPANY = "belkin"
  33. MODULE = "IMG"
  34. def xcrc32(buf):
  35. return (0xffffffff - zlib.crc32(buf, 0xffffffff)).to_bytes(4, byteorder='big')
  36. def encode_model(model):
  37. map = " 0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz-"
  38. code = bytearray()
  39. code.append(map.index(model[:1]))
  40. model = model[1:]
  41. model = model + " " * (3 - len(model) % 4)
  42. while model != "":
  43. b1 = map.index(model[0:1])
  44. b2 = map.index(model[1:2])
  45. b3 = map.index(model[2:3])
  46. b4 = map.index(model[3:4])
  47. model = model[4:]
  48. code.append(b1 << 2 | b2 >> 4)
  49. code.append((b2 & 0xf) << 4 | b3 >> 2)
  50. code.append((b3 & 0x3) << 6 | b4)
  51. return code
  52. def create_header(buf, belkin_header, belkin_model):
  53. head = bytearray(32)
  54. head[0:4] = int(belkin_header, 0).to_bytes(4, 'big')
  55. head[8:12] = int(time.time()).to_bytes(4, 'big')
  56. head[12:16] = len(buf).to_bytes(4, byteorder='big')
  57. head[24:28] = xcrc32(buf)
  58. head[28:29] = VERSION1.to_bytes(1, byteorder='big')
  59. head[29:30] = VERSION2.to_bytes(1, byteorder='big')
  60. head[30:31] = VERSION3.to_bytes(1, byteorder='big')
  61. head[31:32] = VERSION4.to_bytes(1, byteorder='big')
  62. head[16:16 + len(COMPANY)] = bytes(COMPANY,'ascii')
  63. mod = MODULE + "-{:1d}.{:02d}.{:02d}.{:02d}".format(VERSION1, VERSION2, VERSION3, VERSION4)
  64. head.extend(bytes(mod,'ascii'))
  65. head.append(0x00)
  66. head.extend(encode_model(belkin_model))
  67. head.extend(bytes([0x00] * (64 - len(head))))
  68. head[4:8] = xcrc32(head)
  69. return head
  70. parser = argparse.ArgumentParser(description='Generate Belkin header.')
  71. parser.add_argument('source', type=argparse.FileType('r+b'))
  72. parser.add_argument('dest', type=argparse.FileType('wb'))
  73. parser.add_argument('belkin_header')
  74. parser.add_argument('belkin_model')
  75. args = parser.parse_args()
  76. buf = bytearray(args.source.read())
  77. head = create_header(buf, args.belkin_header, args.belkin_model)
  78. args.dest.write(head)
  79. args.dest.write(buf)