make-ras.sh 5.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196
  1. #!/usr/bin/env bash
  2. #
  3. # --- ZyXEL header format ---
  4. # Original Version by Benjamin Berg <[email protected]>
  5. #
  6. # The firmware image prefixed with a header (which is written into the MTD device).
  7. # The header is one erase block (~64KiB) in size, but the checksum only convers the
  8. # first 2KiB. Padding is 0xff. All integers are in big-endian.
  9. #
  10. # The checksum is always a 16-Bit System V checksum (sum -s) stored in a 32-Bit integer.
  11. #
  12. # 4 bytes: checksum of the rootfs image
  13. # 4 bytes: length of the contained rootfs image file (big endian)
  14. # 32 bytes: Firmware Version string (NUL terminated, 0xff padded)
  15. # 4 bytes: checksum over the header partition (big endian - see below)
  16. # 32 bytes: Model (e.g. "NBG6617", NUL termiated, 0xff padded)
  17. # 4 bytes: checksum of the kernel partition
  18. # 4 bytes: length of the contained kernel image file (big endian)
  19. # rest: 0xff padding
  20. #
  21. # The checksums are calculated by adding up all bytes and if a 16bit
  22. # overflow occurs, one is added and the sum is masked to 16 bit:
  23. # csum = csum + databyte; if (csum > 0xffff) { csum += 1; csum &= 0xffff };
  24. # Should the file have an odd number of bytes then the byte len-0x800 is
  25. # used additionally.
  26. #
  27. # The checksum for the header is calculated over the first 2048 bytes with
  28. # the rootfs image checksum as the placeholder during calculation.
  29. #
  30. # The header is padded with 0xff to the erase block size of the device.
  31. #
  32. board=""
  33. version=""
  34. kernel=""
  35. rootfs=""
  36. outfile=""
  37. err=""
  38. while [ "$1" ]; do
  39. case "$1" in
  40. "--board")
  41. board="$2"
  42. shift
  43. shift
  44. continue
  45. ;;
  46. "--version")
  47. version="$2"
  48. shift
  49. shift
  50. continue
  51. ;;
  52. "--kernel")
  53. kernel="$2"
  54. shift
  55. shift
  56. continue
  57. ;;
  58. "--rootfs")
  59. rootfs="$2"
  60. shift
  61. shift
  62. continue
  63. ;;
  64. "--rootfssize")
  65. rootfssize="$2"
  66. shift
  67. shift
  68. continue
  69. ;;
  70. *)
  71. if [ ! "$outfile" ]; then
  72. outfile=$1
  73. shift
  74. continue
  75. fi
  76. ;;
  77. esac
  78. done
  79. if [ ! -n "$board" -o ! -n "$version" -o ! -r "$kernel" -o ! -r "$rootfs" -o ! "$rootfssize" -o ! "$outfile" ]; then
  80. echo "syntax: $0 [--board ras-boardname] [--version ras-version] [--kernel kernelimage] [--rootfs rootfs] out"
  81. exit 1
  82. fi
  83. rootfs_len=$(wc -c < "$rootfs")
  84. if [ "$rootfs_len" -lt "$rootfssize" ]; then
  85. dd if=$rootfs of=$rootfs.new bs=$rootfssize conv=sync
  86. mv $rootfs.new $rootfs
  87. fi
  88. if [ ${#version} -ge 28 ]; then
  89. echo "version: '$version' is too long"
  90. exit 1
  91. fi
  92. tmpdir="$( mktemp -d 2> /dev/null )"
  93. if [ -z "$tmpdir" ]; then
  94. # try OSX signature
  95. tmpdir="$( mktemp -t 'ubitmp' -d )"
  96. fi
  97. if [ -z "$tmpdir" ]; then
  98. exit 1
  99. fi
  100. to_be() {
  101. local val="$1"
  102. local size="$2"
  103. case "$size" in
  104. 4)
  105. echo $(( "$val" >> 24 | ("$val" & 0xff0000) >> 8 | ("$val" & 0xff00) << 8 | ("$val" & 0xff) << 24 ))
  106. ;;
  107. 2)
  108. echo $(( "$val" >> 8 | ("$val" & 0xff) << 8))
  109. ;;
  110. esac
  111. }
  112. checksum_file() {
  113. local file=$1
  114. # ZyXEL seems to use System V sum mode... Now this is classy, who would have thought?!
  115. echo $(sum -s ${file} | cut -f1 -d" ")
  116. }
  117. append_bin() {
  118. local val=$1
  119. local size=$2
  120. local file=$3
  121. while [ "$size" -ne 0 ]; do
  122. printf \\$(printf %o $(("$val" & 0xff))) >> "$file"
  123. val=$(($val >> 8))
  124. let size-=1
  125. done
  126. return
  127. }
  128. tf=${tmpdir}/out
  129. pad=$(printf '%0.1s' $(printf "\xff"){1..64})
  130. rootfs_header_file="$tmpdir/rootfs_header"
  131. rootfs_chksum=$(to_be $(checksum_file ${rootfs}) 4)
  132. rootfs_len=$(to_be $(wc -c < "$rootfs") 4)
  133. versionpadlen=$(( 32 - ( ${#version} + 1) ))
  134. # 4 bytes: checksum of the rootfs image
  135. append_bin "$rootfs_chksum" 4 "$rootfs_header_file"
  136. # 4 bytes: length of the contained rootfs image file (big endian)
  137. append_bin "$rootfs_len" 4 "$rootfs_header_file"
  138. # 32 bytes: Firmware Version string (NUL terminated, 0xff padded)
  139. printf "%s\x00%.*s" "$version" "$versionpadlen" "$pad" >> "$rootfs_header_file"
  140. kernel_header_file="$tmpdir/kernel_header"
  141. kernel_chksum=$(to_be $(checksum_file ${kernel}) 4)
  142. kernel_len=$(to_be $(wc -c < "$kernel") 4)
  143. # 4 bytes: checksum of the kernel image
  144. append_bin "$kernel_chksum" 4 "$kernel_header_file"
  145. # 4 bytes: length of the contained kernel image file (big endian)
  146. append_bin "$kernel_len" 4 "$kernel_header_file"
  147. board_header_file="$tmpdir/board_header"
  148. board_file="$tmpdir/board"
  149. boardpadlen=$(( 64 - ( ${#board} + 1) ))
  150. # 32 bytes: Model (e.g. "NBG6617", NUL termiated, 0xff padded)
  151. printf "%s\x00%.*s" "$board" "$boardpadlen" "$pad" > "$board_file"
  152. cat "$kernel_header_file" >> "$board_file"
  153. printf "%.12s" "$pad" >> "$board_file"
  154. # rest: 0xff padding
  155. for i in {1..511}; do
  156. printf "%s%s" "$pad" "$pad" >> "$board_file"
  157. done
  158. tmp_board_file="$tmpdir/tmp_board_file"
  159. cat "$rootfs_header_file" > "$tmp_board_file"
  160. # The checksum for the header is calculated over the first 2048 bytes with
  161. # the rootfs image checksum as the placeholder during calculation.
  162. append_bin "$rootfs_chksum" 4 "$tmp_board_file"
  163. cat "$board_file" >> "$tmp_board_file"
  164. truncate -s 2048 $tmp_board_file
  165. board_chksum=$(to_be $(checksum_file ${tmp_board_file}) 4)
  166. # 4 bytes: checksum over the header partition (big endian)
  167. append_bin "$board_chksum" 4 "$board_header_file"
  168. cat "$board_file" >> "$board_header_file"
  169. cat "$rootfs_header_file" "$board_header_file" "$rootfs" "$kernel" > "$outfile"
  170. rm -rf "$tmpdir"