build: remove GNU time dependency
[oweals/openwrt.git] / scripts / make-ras.sh
1 #!/usr/bin/env bash
2 #
3 # --- ZyXEL header format ---
4 # Original Version by Benjamin Berg <benjamin@sipsolutions.net>
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
39 while [ "$1" ]; do
40         case "$1" in
41         "--board")
42                 board="$2"
43                 shift
44                 shift
45                 continue
46                 ;;
47         "--version")
48                 version="$2"
49                 shift
50                 shift
51                 continue
52                 ;;
53         "--kernel")
54                 kernel="$2"
55                 shift
56                 shift
57                 continue
58                 ;;
59         "--rootfs")
60                 rootfs="$2"
61                 shift
62                 shift
63                 continue
64                 ;;
65         "--rootfssize")
66                 rootfssize="$2"
67                 shift
68                 shift
69                 continue
70                 ;;
71         *)
72                 if [ ! "$outfile" ]; then
73                         outfile=$1
74                         shift
75                         continue
76                 fi
77                 ;;
78         esac
79 done
80
81 if [ ! -n "$board" -o ! -n "$version" -o ! -r "$kernel" -o ! -r "$rootfs" -o ! "$rootfssize" -o ! "$outfile" ]; then
82         echo "syntax: $0 [--board ras-boardname] [--version ras-version] [--kernel kernelimage] [--rootfs rootfs] out"
83         exit 1
84 fi
85
86 rootfs_len=$(wc -c < "$rootfs")
87
88 if [ "$rootfs_len" -lt "$rootfssize" ]; then
89         dd if=$rootfs of=$rootfs.new bs=$rootfssize conv=sync
90         mv $rootfs.new $rootfs
91 fi
92
93 if [ ${#version} -ge 28 ]; then
94         echo "version: '$version' is too long"
95         exit 1
96 fi
97
98 tmpdir="$( mktemp -d 2> /dev/null )"
99 if [ -z "$tmpdir" ]; then
100         # try OSX signature
101         tmpdir="$( mktemp -t 'ubitmp' -d )"
102 fi
103
104 if [ -z "$tmpdir" ]; then
105         exit 1
106 fi
107
108 to_be() {
109         local val="$1"
110         local size="$2"
111
112         case "$size" in
113         4)
114                 echo $(( "$val" >> 24 | ("$val" & 0xff0000) >> 8 | ("$val" & 0xff00) << 8 | ("$val" & 0xff) << 24 ))
115                 ;;
116         2)
117                 echo $(( "$val" >> 8 | ("$val" & 0xff) << 8))
118                 ;;
119         esac
120 }
121
122 checksum_file() {
123         local file=$1
124
125         # ZyXEL seems to use System V sum mode... Now this is classy, who would have thought?!
126         echo $(sum -s ${file} | cut -f1 -d" ")
127 }
128
129 append_bin() {
130         local val=$1
131         local size=$2
132         local file=$3
133
134         while [ "$size" -ne 0 ]; do
135                 printf \\$(printf %o $(("$val" & 0xff)))  >> "$file"
136                 val=$(($val >> 8))
137                 let size-=1
138         done
139         return
140 }
141
142 tf=${tmpdir}/out
143 pad=$(printf '%0.1s' $(printf "\xff"){1..64})
144
145 rootfs_header_file="$tmpdir/rootfs_header"
146 rootfs_chksum=$(to_be $(checksum_file ${rootfs}) 4)
147 rootfs_len=$(to_be $(wc -c < "$rootfs") 4)
148
149 versionpadlen=$(( 32 - ( ${#version} + 1) ))
150
151 # 4 bytes:  checksum of the rootfs image
152 append_bin "$rootfs_chksum" 4 "$rootfs_header_file"
153 # 4 bytes:  length of the contained rootfs image file (big endian)
154 append_bin "$rootfs_len" 4 "$rootfs_header_file"
155 # 32 bytes:  Firmware Version string (NUL terminated, 0xff padded)
156 printf "%s\x00%.*s" "$version" "$versionpadlen" "$pad" >> "$rootfs_header_file"
157
158 kernel_header_file="$tmpdir/kernel_header"
159 kernel_chksum=$(to_be $(checksum_file ${kernel}) 4)
160 kernel_len=$(to_be $(wc -c < "$kernel") 4)
161
162 # 4 bytes:  checksum of the kernel image
163 append_bin "$kernel_chksum" 4 "$kernel_header_file"
164 # 4 bytes:  length of the contained kernel image file (big endian)
165 append_bin "$kernel_len" 4 "$kernel_header_file"
166
167 board_header_file="$tmpdir/board_header"
168 board_file="$tmpdir/board"
169 boardpadlen=$(( 64 - ( ${#board} + 1) ))
170 # 32 bytes:  Model (e.g. "NBG6617", NUL termiated, 0xff padded)
171 printf "%s\x00%.*s" "$board" "$boardpadlen" "$pad" > "$board_file"
172 cat "$kernel_header_file" >> "$board_file"
173 printf "%.12s" "$pad" >> "$board_file"
174 #      rest: 0xff padding
175 for i in {1..511}; do
176         printf "%s%s" "$pad" "$pad" >> "$board_file"
177 done
178
179 tmp_board_file="$tmpdir/tmp_board_file"
180 cat "$rootfs_header_file" > "$tmp_board_file"
181
182 # The checksum for the header is calculated over the first 2048 bytes with
183 # the rootfs image checksum as the placeholder during calculation.
184 append_bin "$rootfs_chksum" 4 "$tmp_board_file"
185 cat "$board_file" >> "$tmp_board_file"
186
187 truncate -s 2048 $tmp_board_file
188 board_chksum=$(to_be $(checksum_file ${tmp_board_file}) 4)
189
190 # 4 bytes:  checksum over the header partition (big endian)
191 append_bin "$board_chksum" 4 "$board_header_file"
192 cat "$board_file" >> "$board_header_file"
193
194 cat "$rootfs_header_file" "$board_header_file" "$rootfs" "$kernel" > "$outfile"
195
196 rm -rf "$tmpdir"