bcm4908: support "rootfs_data" on U-Boot devices
[openwrt/staging/hauke.git] / target / linux / bcm4908 / base-files / lib / upgrade / platform.sh
1 # SPDX-License-Identifier: GPL-2.0 OR BSD-2-Clause
2
3 . /lib/functions/bcm4908.sh
4
5 RAMFS_COPY_BIN="bcm4908img expr grep ln fdtget fw_printenv fw_setenv readlink tr"
6
7 PART_NAME=firmware
8
9 BCM4908_FW_FORMAT=
10 BCM4908_FW_BOARD_ID=
11 BCM4908_FW_INT_IMG_FORMAT=
12
13 # $(1): file to read from
14 # $(2): offset in bytes
15 # $(3): length in bytes
16 get_content() {
17 dd if="$1" skip=$2 bs=1 count=$3 2>/dev/null
18 }
19
20 # $(1): file to read from
21 # $(2): offset in bytes
22 get_hex_u32_le() {
23 dd if="$1" skip=$2 bs=1 count=4 2>/dev/null | hexdump -v -e '1/4 "%02x"'
24 }
25
26 # $(1): file to read from
27 # $(2): offset in bytes
28 get_hex_u32_be() {
29 dd if="$1" skip=$2 bs=1 count=4 2>/dev/null | hexdump -v -e '1/1 "%02x"'
30 }
31
32 platform_expected_image() {
33 local machine=$(board_name)
34
35 case "$machine" in
36 asus,gt-ac5300) echo "asus GT-AC5300";;
37 netgear,r8000p) echo "chk U12H359T00_NETGEAR";;
38 tplink,archer-c2300-v1) echo "";;
39 esac
40 }
41
42 platform_identify() {
43 local magic
44 local size
45
46 magic=$(get_hex_u32_be "$1" 0)
47 case "$magic" in
48 d00dfeed)
49 BCM4908_FW_FORMAT="pkgtb"
50 return
51 ;;
52 2a23245e)
53 local header_len=$((0x$(get_hex_u32_be "$1" 4)))
54 local board_id_len=$(($header_len - 40))
55
56 BCM4908_FW_FORMAT="chk"
57 BCM4908_FW_BOARD_ID=$(dd if="$1" skip=40 bs=1 count=$board_id_len 2>/dev/null | hexdump -v -e '1/1 "%c"')
58 magic=$(get_hex_u32_be "$1" "$header_len")
59 [ "$magic" = "d00dfeed" ] && {
60 BCM4908_FW_INT_IMG_FORMAT="pkgtb"
61 } || {
62 BCM4908_FW_INT_IMG_FORMAT="bcm4908img"
63 }
64 BCM4908_FW_INT_IMG_EXTRACT_CMD="dd skip=$header_len iflag=skip_bytes"
65 return
66 ;;
67 esac
68
69 size=$(wc -c "$1" | cut -d ' ' -f 1)
70
71 magic=$(get_content "$1" $((size - 20 - 64 + 8)) 12)
72 case "$magic" in
73 GT-AC5300)
74 local size=$(wc -c "$1" | cut -d ' ' -f 1)
75
76 BCM4908_FW_FORMAT="asus"
77 BCM4908_FW_BOARD_ID=$(get_content "$1" $((size - 20 - 64 + 8)) 12)
78 BCM4908_FW_INT_IMG_FORMAT="bcm4908img"
79 return
80 ;;
81 esac
82
83 # Detecting native format is a bit complex (it may start with CFE or
84 # JFFS2) so just use bcm4908img instead of bash hacks.
85 # Make it the last attempt as bcm4908img detects also vendor formats.
86 bcm4908img info -i "$1" > /dev/null && {
87 BCM4908_FW_FORMAT="bcm4908img"
88 return
89 }
90 }
91
92 #
93 # pkgtb helpers
94 #
95
96 platform_pkgtb_get_image_name() {
97 local configuration=$($2 < $1 | fdtget - /configurations default)
98 [ -z "$configuration" ] && {
99 echo "Failed to read default configuration from pkgtb" >&2
100 return
101 }
102
103 local image_name=$($2 < $1 | fdtget - /configurations/$configuration $3)
104 [ -z "$image_name" ] && {
105 echo "Failed to read $3 from pkgtb configuration \"$configuration\"" >&2
106 return
107 }
108
109 echo "$image_name"
110 }
111
112 platform_pkgtb_get_image() {
113 local cmd="${2:-cat}"
114
115 local image_name=$(platform_pkgtb_get_image_name "$1" "$cmd" "$3")
116
117 $cmd < $1 | fdtget -p - /images/$image_name | grep -Eq "^data$" && {
118 $cmd < $1 | fdtget -t r - /images/$image_name data
119 return
120 }
121
122 $cmd < $1 | fdtget -p - /images/$image_name | grep -Eq "^data-position$" && {
123 local data_position=$($cmd < $1 | fdtget - /images/$image_name data-position)
124 local data_size=$($cmd < $1 | fdtget - /images/$image_name data-size)
125 $cmd < $1 2>/dev/null | dd skip=$data_position count=$data_size iflag=skip_bytes,count_bytes
126 return
127 }
128
129 $cmd < $1 | fdtget -p - /images/$image_name | grep -Eq "^data-offset" && {
130 local data_offset=$($cmd < $1 | fdtget - /images/$image_name data-offset)
131 local totalsize=$(get_hex_u32_be "$1" 4)
132 local data_position=$(((0x$totalsize + data_offset + 3) & ~3))
133 local data_size=$($cmd < $1 | fdtget - /images/$image_name data-size)
134 $cmd < $1 2>/dev/null | dd skip=$data_position count=$data_size iflag=skip_bytes,count_bytes
135 return
136 }
137 }
138
139 platform_pkgtb_get_upgrade_index() {
140 case "$(fw_printenv -l /tmp -n -c /tmp/env.config COMMITTED)" in
141 1) echo 2;;
142 2) echo 1;;
143 *) echo 1;;
144 esac
145 }
146
147 platform_pkgtb_commit() {
148 local size=$((0x$(get_hex_u32_le /dev/ubi0_1 4)))
149 local valid1=0
150 local valid2=0
151 local seq1
152 local seq2
153 local tmp
154
155 # Read current values
156 for valid in $(fw_printenv -l /tmp -n -c /tmp/env.config VALID | tr ',' ' '); do
157 case "$valid" in
158 1) valid0=1;;
159 2) valid1=2;;
160 esac
161 done
162 seq0=$(fw_printenv -l /tmp -n -c /tmp/env.config SEQ | cut -d ',' -f 1)
163 seq1=$(fw_printenv -l /tmp -n -c /tmp/env.config SEQ | cut -d ',' -f 2)
164
165 # Calculate values
166 case "$1" in
167 1) valid0=1; seq0=$(((seq1 + 1) % 1000));;
168 2) valid1=2; seq1=$(((seq0 + 1) % 1000));;
169 esac
170
171 # Update variables
172 fw_setenv -l /tmp -c /tmp/env.config COMMITTED "$1"
173 fw_setenv -l /tmp -c /tmp/env.config VALID "$valid0,$valid1"
174 fw_setenv -l /tmp -c /tmp/env.config SEQ "$seq0,$seq1"
175
176 # Write
177 tmp=$(cat /tmp/env.head /tmp/env.body | wc -c)
178 cat /tmp/env.head /tmp/env.body | ubiupdatevol /dev/ubi0_1 -s $tmp -
179 }
180
181 #
182 # check
183 #
184
185 platform_check_pkgtb() {
186 local cmd="${2:-cat}"
187
188 [ -n "$(platform_pkgtb_get_image_name "$1" "$cmd" "bootfs")" -a -n "$(platform_pkgtb_get_image_name "$1" "$cmd" "rootfs")" ]
189 }
190
191 platform_check_image() {
192 [ "$#" -gt 1 ] && return 1
193
194 local expected_image=$(platform_expected_image)
195 local error=0
196
197 platform_identify "$1"
198 [ -z "$BCM4908_FW_FORMAT" ] && {
199 echo "Invalid image type. Please use firmware specific for this device."
200 notify_firmware_broken
201 return 1
202 }
203 echo "Found $BCM4908_FW_FORMAT firmware for device ${BCM4908_FW_BOARD_ID:----}"
204
205 local expected_image="$(platform_expected_image)"
206 [ -n "$expected_image" -a -n "$BCM4908_FW_BOARD_ID" -a "$BCM4908_FW_FORMAT $BCM4908_FW_BOARD_ID" != "$expected_image" ] && {
207 echo "Firmware doesn't match device ($expected_image)"
208 error=1
209 }
210
211 case "$BCM4908_FW_FORMAT" in
212 "bcm4908img")
213 bcm4908img info -i "$1" > /dev/null || {
214 echo "Failed to validate BCM4908 image" >&2
215 notify_firmware_broken
216 return 1
217 }
218
219 bcm4908img bootfs -i "$1" ls | grep -q "1-openwrt" || {
220 # OpenWrt images have 1-openwrt dummy file in the bootfs.
221 # Don't allow backup if it's missing
222 notify_firmware_no_backup
223 }
224 ;;
225 "pkgtb")
226 platform_check_pkgtb "$1" || {
227 echo "Failed to validate pkgtb firmware" >&2
228 notify_firmware_broken
229 return 1
230 }
231 ;;
232 *)
233 case "$BCM4908_FW_INT_IMG_FORMAT" in
234 "bcm4908img")
235 bcm4908img info -i "$1" > /dev/null || {
236 echo "Failed to validate BCM4908 image" >&2
237 notify_firmware_broken
238 return 1
239 }
240
241 bcm4908img bootfs -i "$1" ls | grep -q "1-openwrt" || {
242 # OpenWrt images have 1-openwrt dummy file in the bootfs.
243 # Don't allow backup if it's missing
244 notify_firmware_no_backup
245 }
246 ;;
247 "pkgtb")
248 platform_check_pkgtb "$1" "$BCM4908_FW_INT_IMG_EXTRACT_CMD" || {
249 echo "Failed to validate pkgtb firmware" >&2
250 notify_firmware_broken
251 return 1
252 }
253 ;;
254 esac
255 ;;
256 esac
257
258 return $error
259 }
260
261 #
262 # upgrade
263 #
264
265 platform_pkgtb_clean_rootfs_data() {
266 local ubidev=$(nand_find_ubi $CI_UBIPART)
267 local ubivol="$(nand_find_volume $ubidev rootfs_data)"
268
269 bcm4908_verify_rootfs_data "$ubivol"
270 }
271
272 platform_do_upgrade_pkgtb() {
273 local cmd="${2:-cat}"
274 local size
275 local idx bootfs_id rootfs_id
276
277 bcm4908_pkgtb_setup_env_config
278
279 idx=$(platform_pkgtb_get_upgrade_index)
280 case "$idx" in
281 1) bootfs_id=3; rootfs_id=4;;
282 2) bootfs_id=5; rootfs_id=6;;
283 esac
284
285 size=$(platform_pkgtb_get_image "$1" "$cmd" "bootfs" | wc -c)
286 ubirmvol /dev/ubi0 -N bootfs$idx
287 ubimkvol /dev/ubi0 -n $bootfs_id -N bootfs$idx -t static -s $size
288 platform_pkgtb_get_image "$1" "$cmd" "bootfs" | ubiupdatevol /dev/ubi0_$bootfs_id -s $size -
289
290 size=$(platform_pkgtb_get_image "$1" "$cmd" "rootfs" | wc -c)
291 ubirmvol /dev/ubi0 -N rootfs$idx
292 ubimkvol /dev/ubi0 -n $rootfs_id -N rootfs$idx -t dynamic -s $size
293 platform_pkgtb_get_image "$1" "$cmd" "rootfs" | ubiupdatevol /dev/ubi0_$rootfs_id -s $size -
294
295 platform_pkgtb_commit $idx
296
297 CI_UBIPART="image"
298 platform_pkgtb_clean_rootfs_data
299 nand_do_upgrade_success
300 }
301
302 # $1: cferam index increment value
303 platform_calc_new_cferam() {
304 local inc="$1"
305 local dir="/tmp/sysupgrade-bcm4908"
306
307 local mtd=$(find_mtd_part bootfs)
308 [ -z "$mtd" ] && {
309 echo "Failed to find bootfs partition" >&2
310 return
311 }
312
313 rm -fR $dir
314 mkdir -p $dir
315 mount -t jffs2 -o ro $mtd $dir || {
316 echo "Failed to mount bootfs partition $mtd" >&2
317 rm -fr $dir
318 return
319 }
320
321 local idx=$(ls $dir/cferam.??? | sed -n 's/.*cferam\.\(\d\d\d\)/\1/p')
322 [ -z "$idx" ] && {
323 echo "Failed to find cferam current index" >&2
324 rm -fr $dir
325 return
326 }
327
328 umount $dir
329 rm -fr $dir
330
331 idx=$(($(expr $idx + $inc) % 1000))
332
333 echo $(printf "cferam.%03d" $idx)
334 }
335
336 platform_do_upgrade_ubi() {
337 local dir="/tmp/sysupgrade-bcm4908"
338 local inc=1
339
340 # Verify new bootfs size
341 local mtd_bootfs_size=$(grep "\"bootfs\"" /proc/mtd | sed "s/mtd[0-9]*:[ \t]*\([^ \t]*\).*/\1/")
342 [ -z "$mtd_bootfs_size" ] && {
343 echo "Unable to find \"bootfs\" partition size"
344 return
345 }
346 mtd_bootfs_size=$((0x$mtd_bootfs_size))
347 local img_bootfs_size=$(bcm4908img extract -i "$1" -t bootfs | wc -c)
348 [ $img_bootfs_size -gt $mtd_bootfs_size ] && {
349 echo "New bootfs doesn't fit MTD partition."
350 return
351 }
352
353 # Find cferam name for new firmware
354 # For UBI we always flash "firmware" so don't increase cferam index if
355 # there is "fallback". That could result in cferam.999 & cferam.001
356 [ -n "$(find_mtd_index backup)" -o -n "$(find_mtd_index fallback)" ] && inc=0
357 local cferam=$(platform_calc_new_cferam $inc)
358 [ -z "$cferam" ] && exit 1
359
360 # Prepare new firmware
361 bcm4908img bootfs -i "$1" mv cferam.000 $cferam || {
362 echo "Failed to rename cferam.000 to $cferam" >&2
363 exit 1
364 }
365
366 # Extract rootfs for further flashing
367 rm -fr $dir
368 mkdir -p $dir
369 bcm4908img extract -i "$1" -t rootfs > $dir/root || {
370 echo "Failed to extract rootfs" >&2
371 rm -fr $dir
372 exit 1
373 }
374
375 # Flash bootfs MTD partition with new one
376 mtd erase bootfs || {
377 echo "Failed to erase bootfs" >&2
378 rm -fr $dir
379 exit 1
380 }
381 bcm4908img extract -i "$1" -t bootfs | mtd write - bootfs || {
382 echo "Failed to flash bootfs" >&2
383 rm -fr $dir
384 exit 1
385 }
386
387 nand_do_upgrade $dir/root
388 }
389
390 platform_do_upgrade() {
391 platform_identify "$1"
392
393 # Try NAND aware UBI upgrade for OpenWrt images
394 case "$BCM4908_FW_FORMAT" in
395 "bcm4908img")
396 bcm4908img bootfs -i "$1" ls | grep -q "1-openwrt" && platform_do_upgrade_ubi "$1"
397 ;;
398 "pkgtb")
399 platform_do_upgrade_pkgtb "$1"
400 ;;
401 *)
402 case "$BCM4908_FW_INT_IMG_FORMAT" in
403 "bcm4908img")
404 bcm4908img bootfs -i "$1" ls | grep -q "1-openwrt" && platform_do_upgrade_ubi "$1"
405 ;;
406 "pkgtb")
407 platform_do_upgrade_pkgtb "$1" "$BCM4908_FW_INT_IMG_EXTRACT_CMD"
408 ;;
409 *)
410 echo "NAND aware sysupgrade is unsupported for $BCM4908_FW_FORMAT format"
411 ;;
412 esac
413 ;;
414 esac
415
416 # Above calls exit on success.
417 # If we got here it isn't OpenWrt image or something went wrong.
418 [ "$BCM4908_FW_FORMAT" = "pkgtb" -o "$BCM4908_FW_INT_IMG_FORMAT" = "pkgtb" ] && {
419 echo "Failed to upgrade pkgtb. Fallback to raw flashing is impossible for this format." >&2
420 exit 1
421 }
422 echo "Writing whole image to NAND flash. All erase counters will be lost."
423
424 # Find cferam name for new firmware
425 local cferam=$(platform_calc_new_cferam 1)
426 [ -z "$cferam" ] && exit 1
427
428 # Prepare new firmware
429 bcm4908img bootfs -i "$1" mv cferam.000 $cferam || {
430 echo "Failed to rename cferam.000 to $cferam" >&2
431 exit 1
432 }
433
434 # Jush flash firmware partition as is
435 [ -n "$(find_mtd_index backup)" ] && PART_NAME=backup
436 [ -n "$(find_mtd_index fallback)" ] && PART_NAME=fallback
437 mtd erase $PART_NAME
438 default_do_upgrade "$1" "bcm4908img extract -t firmware"
439 }