nand.sh 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469
  1. # Copyright (C) 2014 OpenWrt.org
  2. #
  3. . /lib/functions.sh
  4. # 'kernel' partition or UBI volume on NAND contains the kernel
  5. CI_KERNPART="${CI_KERNPART:-kernel}"
  6. # 'ubi' partition on NAND contains UBI
  7. # There are also CI_KERN_UBIPART and CI_ROOT_UBIPART if kernel
  8. # and rootfs are on separated UBIs.
  9. CI_UBIPART="${CI_UBIPART:-ubi}"
  10. # 'rootfs' UBI volume on NAND contains the rootfs
  11. CI_ROOTPART="${CI_ROOTPART:-rootfs}"
  12. ubi_mknod() {
  13. local dir="$1"
  14. local dev="/dev/$(basename $dir)"
  15. [ -e "$dev" ] && return 0
  16. local devid="$(cat $dir/dev)"
  17. local major="${devid%%:*}"
  18. local minor="${devid##*:}"
  19. mknod "$dev" c $major $minor
  20. }
  21. nand_find_volume() {
  22. local ubidevdir ubivoldir
  23. ubidevdir="/sys/devices/virtual/ubi/$1"
  24. [ ! -d "$ubidevdir" ] && return 1
  25. for ubivoldir in $ubidevdir/${1}_*; do
  26. [ ! -d "$ubivoldir" ] && continue
  27. if [ "$( cat $ubivoldir/name )" = "$2" ]; then
  28. basename $ubivoldir
  29. ubi_mknod "$ubivoldir"
  30. return 0
  31. fi
  32. done
  33. }
  34. nand_find_ubi() {
  35. local ubidevdir ubidev mtdnum
  36. mtdnum="$( find_mtd_index $1 )"
  37. [ ! "$mtdnum" ] && return 1
  38. for ubidevdir in /sys/devices/virtual/ubi/ubi*; do
  39. [ ! -d "$ubidevdir" ] && continue
  40. cmtdnum="$( cat $ubidevdir/mtd_num )"
  41. [ ! "$mtdnum" ] && continue
  42. if [ "$mtdnum" = "$cmtdnum" ]; then
  43. ubidev=$( basename $ubidevdir )
  44. ubi_mknod "$ubidevdir"
  45. echo $ubidev
  46. return 0
  47. fi
  48. done
  49. }
  50. nand_get_magic_long() {
  51. (${3}cat "$1" | dd bs=4 "skip=${2:-0}" count=1 | hexdump -v -n 4 -e '1/1 "%02x"') 2> /dev/null
  52. }
  53. get_magic_long_tar() {
  54. (tar xO${3}f "$1" "$2" | dd bs=4 count=1 | hexdump -v -n 4 -e '1/1 "%02x"') 2> /dev/null
  55. }
  56. identify() {
  57. identify_magic_long $(nand_get_magic_long "$@")
  58. }
  59. identify_tar() {
  60. identify_magic_long $(get_magic_long_tar "$@")
  61. }
  62. identify_if_gzip() {
  63. if [ "$(identify "$1")" = gzip ]; then echo -n z; fi
  64. }
  65. nand_restore_config() {
  66. local ubidev=$( nand_find_ubi "${CI_ROOT_UBIPART:-$CI_UBIPART}" )
  67. local ubivol="$( nand_find_volume $ubidev rootfs_data )"
  68. if [ ! "$ubivol" ]; then
  69. ubivol="$( nand_find_volume $ubidev "$CI_ROOTPART" )"
  70. if [ ! "$ubivol" ]; then
  71. echo "cannot find ubifs data volume"
  72. return 1
  73. fi
  74. fi
  75. mkdir /tmp/new_root
  76. if ! mount -t ubifs /dev/$ubivol /tmp/new_root; then
  77. echo "cannot mount ubifs volume $ubivol"
  78. rmdir /tmp/new_root
  79. return 1
  80. fi
  81. if mv "$1" "/tmp/new_root/$BACKUP_FILE"; then
  82. if umount /tmp/new_root; then
  83. echo "configuration saved"
  84. rmdir /tmp/new_root
  85. return 0
  86. fi
  87. else
  88. umount /tmp/new_root
  89. fi
  90. echo "could not save configuration to ubifs volume $ubivol"
  91. rmdir /tmp/new_root
  92. return 1
  93. }
  94. nand_remove_ubiblock() {
  95. local ubivol="$1"
  96. local ubiblk="ubiblock${ubivol:3}"
  97. if [ -e "/dev/$ubiblk" ]; then
  98. umount "/dev/$ubiblk" && echo "unmounted /dev/$ubiblk" || :
  99. if ! ubiblock -r "/dev/$ubivol"; then
  100. echo "cannot remove $ubiblk"
  101. return 1
  102. fi
  103. fi
  104. }
  105. nand_attach_ubi() {
  106. local ubipart="$1"
  107. local has_env="${2:-0}"
  108. local mtdnum="$( find_mtd_index "$ubipart" )"
  109. if [ ! "$mtdnum" ]; then
  110. >&2 echo "cannot find ubi mtd partition $ubipart"
  111. return 1
  112. fi
  113. local ubidev="$( nand_find_ubi "$ubipart" )"
  114. if [ ! "$ubidev" ]; then
  115. >&2 ubiattach -m "$mtdnum"
  116. ubidev="$( nand_find_ubi "$ubipart" )"
  117. if [ ! "$ubidev" ]; then
  118. >&2 ubiformat /dev/mtd$mtdnum -y
  119. >&2 ubiattach -m "$mtdnum"
  120. ubidev="$( nand_find_ubi "$ubipart" )"
  121. if [ ! "$ubidev" ]; then
  122. >&2 echo "cannot attach ubi mtd partition $ubipart"
  123. return 1
  124. fi
  125. if [ "$has_env" -gt 0 ]; then
  126. >&2 ubimkvol /dev/$ubidev -n 0 -N ubootenv -s 1MiB
  127. >&2 ubimkvol /dev/$ubidev -n 1 -N ubootenv2 -s 1MiB
  128. fi
  129. fi
  130. fi
  131. echo "$ubidev"
  132. return 0
  133. }
  134. nand_detach_ubi() {
  135. local ubipart="$1"
  136. local mtdnum="$( find_mtd_index "$ubipart" )"
  137. if [ ! "$mtdnum" ]; then
  138. echo "cannot find ubi mtd partition $ubipart"
  139. return 1
  140. fi
  141. local ubidev="$( nand_find_ubi "$ubipart" )"
  142. if [ "$ubidev" ]; then
  143. for ubivol in $(find /dev -name "${ubidev}_*" -maxdepth 1 | sort); do
  144. ubivol="${ubivol:5}"
  145. nand_remove_ubiblock "$ubivol" || :
  146. umount "/dev/$ubivol" && echo "unmounted /dev/$ubivol" || :
  147. done
  148. if ! ubidetach -m "$mtdnum"; then
  149. echo "cannot detach ubi mtd partition $ubipart"
  150. return 1
  151. fi
  152. fi
  153. }
  154. nand_upgrade_prepare_ubi() {
  155. local rootfs_length="$1"
  156. local rootfs_type="$2"
  157. local rootfs_data_max="$(fw_printenv -n rootfs_data_max 2> /dev/null)"
  158. [ -n "$rootfs_data_max" ] && rootfs_data_max=$((rootfs_data_max))
  159. local kernel_length="$3"
  160. local has_env="${4:-0}"
  161. local kern_ubidev
  162. local root_ubidev
  163. [ -n "$rootfs_length" -o -n "$kernel_length" ] || return 1
  164. if [ -n "$CI_KERN_UBIPART" -a -n "$CI_ROOT_UBIPART" ]; then
  165. kern_ubidev="$( nand_attach_ubi "$CI_KERN_UBIPART" "$has_env" )"
  166. [ -n "$kern_ubidev" ] || return 1
  167. root_ubidev="$( nand_attach_ubi "$CI_ROOT_UBIPART" )"
  168. [ -n "$root_ubidev" ] || return 1
  169. else
  170. kern_ubidev="$( nand_attach_ubi "$CI_UBIPART" "$has_env" )"
  171. [ -n "$kern_ubidev" ] || return 1
  172. root_ubidev="$kern_ubidev"
  173. fi
  174. local kern_ubivol="$( nand_find_volume $kern_ubidev "$CI_KERNPART" )"
  175. local root_ubivol="$( nand_find_volume $root_ubidev "$CI_ROOTPART" )"
  176. local data_ubivol="$( nand_find_volume $root_ubidev rootfs_data )"
  177. [ "$root_ubivol" = "$kern_ubivol" ] && root_ubivol=
  178. # remove ubiblocks
  179. [ "$kern_ubivol" ] && { nand_remove_ubiblock $kern_ubivol || return 1; }
  180. [ "$root_ubivol" ] && { nand_remove_ubiblock $root_ubivol || return 1; }
  181. [ "$data_ubivol" ] && { nand_remove_ubiblock $data_ubivol || return 1; }
  182. # kill volumes
  183. [ "$kern_ubivol" ] && ubirmvol /dev/$kern_ubidev -N "$CI_KERNPART" || :
  184. [ "$root_ubivol" ] && ubirmvol /dev/$root_ubidev -N "$CI_ROOTPART" || :
  185. [ "$data_ubivol" ] && ubirmvol /dev/$root_ubidev -N rootfs_data || :
  186. # create kernel vol
  187. if [ -n "$kernel_length" ]; then
  188. if ! ubimkvol /dev/$kern_ubidev -N "$CI_KERNPART" -s $kernel_length; then
  189. echo "cannot create kernel volume"
  190. return 1;
  191. fi
  192. fi
  193. # create rootfs vol
  194. if [ -n "$rootfs_length" ]; then
  195. local rootfs_size_param
  196. if [ "$rootfs_type" = "ubifs" ]; then
  197. rootfs_size_param="-m"
  198. else
  199. rootfs_size_param="-s $rootfs_length"
  200. fi
  201. if ! ubimkvol /dev/$root_ubidev -N "$CI_ROOTPART" $rootfs_size_param; then
  202. echo "cannot create rootfs volume"
  203. return 1;
  204. fi
  205. fi
  206. # create rootfs_data vol for non-ubifs rootfs
  207. if [ "$rootfs_type" != "ubifs" ]; then
  208. local rootfs_data_size_param="-m"
  209. if [ -n "$rootfs_data_max" ]; then
  210. rootfs_data_size_param="-s $rootfs_data_max"
  211. fi
  212. if ! ubimkvol /dev/$root_ubidev -N rootfs_data $rootfs_data_size_param; then
  213. if ! ubimkvol /dev/$root_ubidev -N rootfs_data -m; then
  214. echo "cannot initialize rootfs_data volume"
  215. return 1
  216. fi
  217. fi
  218. fi
  219. return 0
  220. }
  221. # Write the UBI image to MTD ubi partition
  222. nand_upgrade_ubinized() {
  223. local ubi_file="$1"
  224. local gz="$2"
  225. nand_detach_ubi "$CI_UBIPART" || return 1
  226. local mtdnum="$( find_mtd_index "$CI_UBIPART" )"
  227. ${gz}cat "$ubi_file" | ubiformat "/dev/mtd$mtdnum" -y -f - && ubiattach -m "$mtdnum"
  228. }
  229. # Write the UBIFS image to UBI rootfs volume
  230. nand_upgrade_ubifs() {
  231. local ubifs_file="$1"
  232. local gz="$2"
  233. local ubifs_length=$( (${gz}cat "$ubifs_file" | wc -c) 2> /dev/null)
  234. nand_upgrade_prepare_ubi "$ubifs_length" "ubifs" "" "" || return 1
  235. local ubidev="$( nand_find_ubi "$CI_UBIPART" )"
  236. local root_ubivol="$(nand_find_volume $ubidev "$CI_ROOTPART")"
  237. ${gz}cat "$ubifs_file" | ubiupdatevol /dev/$root_ubivol -s "$ubifs_length" -
  238. }
  239. # Write the FIT image to UBI kernel volume
  240. nand_upgrade_fit() {
  241. local fit_file="$1"
  242. local gz="$2"
  243. local fit_length=$( (${gz}cat "$fit_file" | wc -c) 2> /dev/null)
  244. nand_upgrade_prepare_ubi "" "" "$fit_length" "1" || return 1
  245. local fit_ubidev="$(nand_find_ubi "$CI_UBIPART")"
  246. local fit_ubivol="$(nand_find_volume $fit_ubidev "$CI_KERNPART")"
  247. ${gz}cat "$fit_file" | ubiupdatevol /dev/$fit_ubivol -s "$fit_length" -
  248. }
  249. # Write images in the TAR file to MTD partitions and/or UBI volumes as required
  250. nand_upgrade_tar() {
  251. local tar_file="$1"
  252. local gz="$2"
  253. # WARNING: This fails if tar contains more than one 'sysupgrade-*' directory.
  254. local board_dir="$(tar t${gz}f "$tar_file" | grep -m 1 '^sysupgrade-.*/$')"
  255. board_dir="${board_dir%/}"
  256. local kernel_mtd kernel_length
  257. if [ "$CI_KERNPART" != "none" ]; then
  258. kernel_mtd="$(find_mtd_index "$CI_KERNPART")"
  259. kernel_length=$( (tar xO${gz}f "$tar_file" "$board_dir/kernel" | wc -c) 2> /dev/null)
  260. [ "$kernel_length" = 0 ] && kernel_length=
  261. fi
  262. local rootfs_length=$( (tar xO${gz}f "$tar_file" "$board_dir/root" | wc -c) 2> /dev/null)
  263. [ "$rootfs_length" = 0 ] && rootfs_length=
  264. local rootfs_type
  265. [ "$rootfs_length" ] && rootfs_type="$(identify_tar "$tar_file" "$board_dir/root" "$gz")"
  266. local ubi_kernel_length
  267. if [ "$kernel_length" ]; then
  268. if [ "$kernel_mtd" ]; then
  269. # On some devices, the raw kernel and ubi partitions overlap.
  270. # These devices brick if the kernel partition is erased.
  271. # Hence only invalidate kernel for now.
  272. dd if=/dev/zero bs=4096 count=1 2> /dev/null | \
  273. mtd write - "$CI_KERNPART"
  274. else
  275. ubi_kernel_length="$kernel_length"
  276. fi
  277. fi
  278. local has_env=0
  279. nand_upgrade_prepare_ubi "$rootfs_length" "$rootfs_type" "$ubi_kernel_length" "$has_env" || return 1
  280. if [ "$rootfs_length" ]; then
  281. local ubidev="$( nand_find_ubi "${CI_ROOT_UBIPART:-$CI_UBIPART}" )"
  282. local root_ubivol="$( nand_find_volume $ubidev "$CI_ROOTPART" )"
  283. tar xO${gz}f "$tar_file" "$board_dir/root" | \
  284. ubiupdatevol /dev/$root_ubivol -s "$rootfs_length" -
  285. fi
  286. if [ "$kernel_length" ]; then
  287. if [ "$kernel_mtd" ]; then
  288. tar xO${gz}f "$tar_file" "$board_dir/kernel" | \
  289. mtd write - "$CI_KERNPART"
  290. else
  291. local ubidev="$( nand_find_ubi "${CI_KERN_UBIPART:-$CI_UBIPART}" )"
  292. local kern_ubivol="$( nand_find_volume $ubidev "$CI_KERNPART" )"
  293. tar xO${gz}f "$tar_file" "$board_dir/kernel" | \
  294. ubiupdatevol /dev/$kern_ubivol -s "$kernel_length" -
  295. fi
  296. fi
  297. return 0
  298. }
  299. nand_verify_if_gzip_file() {
  300. local file="$1"
  301. local gz="$2"
  302. if [ "$gz" = z ]; then
  303. echo "verifying compressed sysupgrade file integrity"
  304. if ! gzip -t "$file"; then
  305. echo "corrupted compressed sysupgrade file"
  306. return 1
  307. fi
  308. fi
  309. }
  310. nand_verify_tar_file() {
  311. local file="$1"
  312. local gz="$2"
  313. echo "verifying sysupgrade tar file integrity"
  314. if ! tar xO${gz}f "$file" > /dev/null; then
  315. echo "corrupted sysupgrade tar file"
  316. return 1
  317. fi
  318. }
  319. nand_do_flash_file() {
  320. local file="$1"
  321. local gz="$(identify_if_gzip "$file")"
  322. local file_type="$(identify "$file" "" "$gz")"
  323. [ ! "$(find_mtd_index "$CI_UBIPART")" ] && CI_UBIPART=rootfs
  324. case "$file_type" in
  325. "fit")
  326. nand_verify_if_gzip_file "$file" "$gz" || return 1
  327. nand_upgrade_fit "$file" "$gz"
  328. ;;
  329. "ubi")
  330. nand_verify_if_gzip_file "$file" "$gz" || return 1
  331. nand_upgrade_ubinized "$file" "$gz"
  332. ;;
  333. "ubifs")
  334. nand_verify_if_gzip_file "$file" "$gz" || return 1
  335. nand_upgrade_ubifs "$file" "$gz"
  336. ;;
  337. *)
  338. nand_verify_tar_file "$file" "$gz" || return 1
  339. nand_upgrade_tar "$file" "$gz"
  340. ;;
  341. esac
  342. }
  343. nand_do_restore_config() {
  344. local conf_tar="/tmp/sysupgrade.tgz"
  345. [ ! -f "$conf_tar" ] || nand_restore_config "$conf_tar"
  346. }
  347. # Recognize type of passed file and start the upgrade process
  348. nand_do_upgrade() {
  349. local file="$1"
  350. sync
  351. nand_do_flash_file "$file" && nand_do_upgrade_success
  352. nand_do_upgrade_failed
  353. }
  354. nand_do_upgrade_success() {
  355. if nand_do_restore_config && sync; then
  356. echo "sysupgrade successful"
  357. umount -a
  358. reboot -f
  359. fi
  360. nand_do_upgrade_failed
  361. }
  362. nand_do_upgrade_failed() {
  363. sync
  364. echo "sysupgrade failed"
  365. # Should we reboot or bring up some failsafe mode instead?
  366. umount -a
  367. reboot -f
  368. }
  369. # Check if passed file is a valid one for NAND sysupgrade.
  370. # Currently it accepts 4 types of files:
  371. # 1) UBI: a ubinized image containing required UBI volumes.
  372. # 2) UBIFS: a UBIFS rootfs volume image.
  373. # 3) FIT: a FIT image containing kernel and rootfs.
  374. # 4) TAR: an archive that includes directory "sysupgrade-${BOARD_NAME}" containing
  375. # a non-empty "CONTROL" file and required partition and/or volume images.
  376. #
  377. # You usually want to call this function in platform_check_image.
  378. #
  379. # $(1): board name, used in case of passing TAR file
  380. # $(2): file to be checked
  381. nand_do_platform_check() {
  382. local board_name="$1"
  383. local file="$2"
  384. local gz="$(identify_if_gzip "$file")"
  385. local file_type="$(identify "$file" "" "$gz")"
  386. local control_length=$( (tar xO${gz}f "$file" "sysupgrade-$board_name/CONTROL" | wc -c) 2> /dev/null)
  387. if [ "$control_length" != 0 ]; then
  388. nand_verify_tar_file "$file" "$gz" || return 1
  389. else
  390. nand_verify_if_gzip_file "$file" "$gz" || return 1
  391. if [ "$file_type" != "fit" -a "$file_type" != "ubi" -a "$file_type" != "ubifs" ]; then
  392. echo "invalid sysupgrade file"
  393. return 1
  394. fi
  395. fi
  396. return 0
  397. }