nand.sh 9.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357
  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. CI_UBIPART="${CI_UBIPART:-ubi}"
  8. # 'rootfs' UBI volume on NAND contains the rootfs
  9. CI_ROOTPART="${CI_ROOTPART:-rootfs}"
  10. ubi_mknod() {
  11. local dir="$1"
  12. local dev="/dev/$(basename $dir)"
  13. [ -e "$dev" ] && return 0
  14. local devid="$(cat $dir/dev)"
  15. local major="${devid%%:*}"
  16. local minor="${devid##*:}"
  17. mknod "$dev" c $major $minor
  18. }
  19. nand_find_volume() {
  20. local ubidevdir ubivoldir
  21. ubidevdir="/sys/devices/virtual/ubi/$1"
  22. [ ! -d "$ubidevdir" ] && return 1
  23. for ubivoldir in $ubidevdir/${1}_*; do
  24. [ ! -d "$ubivoldir" ] && continue
  25. if [ "$( cat $ubivoldir/name )" = "$2" ]; then
  26. basename $ubivoldir
  27. ubi_mknod "$ubivoldir"
  28. return 0
  29. fi
  30. done
  31. }
  32. nand_find_ubi() {
  33. local ubidevdir ubidev mtdnum
  34. mtdnum="$( find_mtd_index $1 )"
  35. [ ! "$mtdnum" ] && return 1
  36. for ubidevdir in /sys/devices/virtual/ubi/ubi*; do
  37. [ ! -d "$ubidevdir" ] && continue
  38. cmtdnum="$( cat $ubidevdir/mtd_num )"
  39. [ ! "$mtdnum" ] && continue
  40. if [ "$mtdnum" = "$cmtdnum" ]; then
  41. ubidev=$( basename $ubidevdir )
  42. ubi_mknod "$ubidevdir"
  43. echo $ubidev
  44. return 0
  45. fi
  46. done
  47. }
  48. nand_get_magic_long() {
  49. dd if="$1" skip=$2 bs=4 count=1 2>/dev/null | hexdump -v -n 4 -e '1/1 "%02x"'
  50. }
  51. get_magic_long_tar() {
  52. ( tar xf $1 $2 -O | dd bs=4 count=1 | hexdump -v -n 4 -e '1/1 "%02x"') 2> /dev/null
  53. }
  54. identify_magic() {
  55. local magic=$1
  56. case "$magic" in
  57. "55424923")
  58. echo "ubi"
  59. ;;
  60. "31181006")
  61. echo "ubifs"
  62. ;;
  63. "68737173")
  64. echo "squashfs"
  65. ;;
  66. "d00dfeed")
  67. echo "fit"
  68. ;;
  69. "4349"*)
  70. echo "combined"
  71. ;;
  72. *)
  73. echo "unknown $magic"
  74. ;;
  75. esac
  76. }
  77. identify() {
  78. identify_magic $(nand_get_magic_long "$1" "${2:-0}")
  79. }
  80. identify_tar() {
  81. identify_magic $(get_magic_long_tar "$1" "$2")
  82. }
  83. nand_restore_config() {
  84. sync
  85. local ubidev=$( nand_find_ubi $CI_UBIPART )
  86. local ubivol="$( nand_find_volume $ubidev rootfs_data )"
  87. [ ! "$ubivol" ] &&
  88. ubivol="$( nand_find_volume $ubidev $CI_ROOTPART )"
  89. mkdir /tmp/new_root
  90. if ! mount -t ubifs /dev/$ubivol /tmp/new_root; then
  91. echo "mounting ubifs $ubivol failed"
  92. rmdir /tmp/new_root
  93. return 1
  94. fi
  95. mv "$1" "/tmp/new_root/$BACKUP_FILE"
  96. umount /tmp/new_root
  97. sync
  98. rmdir /tmp/new_root
  99. }
  100. nand_upgrade_prepare_ubi() {
  101. local rootfs_length="$1"
  102. local rootfs_type="$2"
  103. local rootfs_data_max="$(fw_printenv -n rootfs_data_max 2>/dev/null)"
  104. [ -n "$rootfs_data_max" ] && rootfs_data_max=$((rootfs_data_max))
  105. local kernel_length="$3"
  106. local has_env="${4:-0}"
  107. [ -n "$rootfs_length" -o -n "$kernel_length" ] || return 1
  108. local mtdnum="$( find_mtd_index "$CI_UBIPART" )"
  109. if [ ! "$mtdnum" ]; then
  110. echo "cannot find ubi mtd partition $CI_UBIPART"
  111. return 1
  112. fi
  113. local ubidev="$( nand_find_ubi "$CI_UBIPART" )"
  114. if [ ! "$ubidev" ]; then
  115. ubiattach -m "$mtdnum"
  116. sync
  117. ubidev="$( nand_find_ubi "$CI_UBIPART" )"
  118. fi
  119. if [ ! "$ubidev" ]; then
  120. ubiformat /dev/mtd$mtdnum -y
  121. ubiattach -m "$mtdnum"
  122. sync
  123. ubidev="$( nand_find_ubi "$CI_UBIPART" )"
  124. [ "$has_env" -gt 0 ] && {
  125. ubimkvol /dev/$ubidev -n 0 -N ubootenv -s 1MiB
  126. ubimkvol /dev/$ubidev -n 1 -N ubootenv2 -s 1MiB
  127. }
  128. fi
  129. local kern_ubivol="$( nand_find_volume $ubidev $CI_KERNPART )"
  130. local root_ubivol="$( nand_find_volume $ubidev $CI_ROOTPART )"
  131. local data_ubivol="$( nand_find_volume $ubidev rootfs_data )"
  132. local ubiblk ubiblkvol
  133. for ubiblk in /dev/ubiblock*_? ; do
  134. [ -e "$ubiblk" ] || continue
  135. echo "removing ubiblock${ubiblk:13}"
  136. ubiblkvol=ubi${ubiblk:13}
  137. if ! ubiblock -r /dev/$ubiblkvol; then
  138. echo "cannot remove $ubiblk"
  139. return 1
  140. fi
  141. done
  142. # kill volumes
  143. [ "$kern_ubivol" ] && ubirmvol /dev/$ubidev -N $CI_KERNPART || true
  144. [ "$root_ubivol" -a "$root_ubivol" != "$kern_ubivol" ] && ubirmvol /dev/$ubidev -N $CI_ROOTPART || true
  145. [ "$data_ubivol" ] && ubirmvol /dev/$ubidev -N rootfs_data || true
  146. # update kernel
  147. if [ -n "$kernel_length" ]; then
  148. if ! ubimkvol /dev/$ubidev -N $CI_KERNPART -s $kernel_length; then
  149. echo "cannot create kernel volume"
  150. return 1;
  151. fi
  152. fi
  153. # update rootfs
  154. if [ -n "$rootfs_length" ]; then
  155. local rootfs_size_param
  156. if [ "$rootfs_type" = "ubifs" ]; then
  157. rootfs_size_param="-m"
  158. else
  159. rootfs_size_param="-s $rootfs_length"
  160. fi
  161. if ! ubimkvol /dev/$ubidev -N $CI_ROOTPART $rootfs_size_param; then
  162. echo "cannot create rootfs volume"
  163. return 1;
  164. fi
  165. fi
  166. # create rootfs_data for non-ubifs rootfs
  167. if [ "$rootfs_type" != "ubifs" ]; then
  168. local availeb=$(cat /sys/devices/virtual/ubi/$ubidev/avail_eraseblocks)
  169. local ebsize=$(cat /sys/devices/virtual/ubi/$ubidev/eraseblock_size)
  170. local avail_size=$((availeb * ebsize))
  171. local rootfs_data_size_param="-m"
  172. if [ -n "$rootfs_data_max" ] &&
  173. [ "$rootfs_data_max" != "0" ] &&
  174. [ "$rootfs_data_max" -le "$avail_size" ]; then
  175. rootfs_data_size_param="-s $rootfs_data_max"
  176. fi
  177. if ! ubimkvol /dev/$ubidev -N rootfs_data $rootfs_data_size_param; then
  178. echo "cannot initialize rootfs_data volume"
  179. return 1
  180. fi
  181. fi
  182. sync
  183. return 0
  184. }
  185. nand_do_upgrade_success() {
  186. local conf_tar="/tmp/sysupgrade.tgz"
  187. sync
  188. [ -f "$conf_tar" ] && nand_restore_config "$conf_tar"
  189. echo "sysupgrade successful"
  190. umount -a
  191. reboot -f
  192. }
  193. # Flash the UBI image to MTD partition
  194. nand_upgrade_ubinized() {
  195. local ubi_file="$1"
  196. local mtdnum="$(find_mtd_index "$CI_UBIPART")"
  197. [ ! "$mtdnum" ] && {
  198. CI_UBIPART="rootfs"
  199. mtdnum="$(find_mtd_index "$CI_UBIPART")"
  200. }
  201. if [ ! "$mtdnum" ]; then
  202. echo "cannot find mtd device $CI_UBIPART"
  203. umount -a
  204. reboot -f
  205. fi
  206. local mtddev="/dev/mtd${mtdnum}"
  207. ubidetach -p "${mtddev}" || true
  208. sync
  209. ubiformat "${mtddev}" -y -f "${ubi_file}"
  210. ubiattach -p "${mtddev}"
  211. nand_do_upgrade_success
  212. }
  213. # Write the UBIFS image to UBI volume
  214. nand_upgrade_ubifs() {
  215. local rootfs_length=$( (cat $1 | wc -c) 2> /dev/null)
  216. nand_upgrade_prepare_ubi "$rootfs_length" "ubifs" "" ""
  217. local ubidev="$( nand_find_ubi "$CI_UBIPART" )"
  218. local root_ubivol="$(nand_find_volume $ubidev $CI_ROOTPART)"
  219. ubiupdatevol /dev/$root_ubivol -s $rootfs_length $1
  220. nand_do_upgrade_success
  221. }
  222. nand_upgrade_fit() {
  223. local fit_file="$1"
  224. local fit_length="$(wc -c < "$fit_file")"
  225. nand_upgrade_prepare_ubi "" "" "$fit_length" "1"
  226. local fit_ubidev="$(nand_find_ubi "$CI_UBIPART")"
  227. local fit_ubivol="$(nand_find_volume $fit_ubidev "$CI_KERNPART")"
  228. ubiupdatevol /dev/$fit_ubivol -s $fit_length $fit_file
  229. nand_do_upgrade_success
  230. }
  231. nand_upgrade_tar() {
  232. local tar_file="$1"
  233. local kernel_mtd="$(find_mtd_index $CI_KERNPART)"
  234. local board_dir=$(tar tf "$tar_file" | grep -m 1 '^sysupgrade-.*/$')
  235. board_dir=${board_dir%/}
  236. kernel_length=$( (tar xf "$tar_file" ${board_dir}/kernel -O | wc -c) 2> /dev/null)
  237. local has_rootfs=0
  238. local rootfs_length
  239. local rootfs_type
  240. tar tf "$tar_file" ${board_dir}/root 1>/dev/null 2>/dev/null && has_rootfs=1
  241. [ "$has_rootfs" = "1" ] && {
  242. rootfs_length=$( (tar xf "$tar_file" ${board_dir}/root -O | wc -c) 2> /dev/null)
  243. rootfs_type="$(identify_tar "$tar_file" ${board_dir}/root)"
  244. }
  245. local has_kernel=1
  246. local has_env=0
  247. [ "$kernel_length" != 0 -a -n "$kernel_mtd" ] && {
  248. tar xf "$tar_file" ${board_dir}/kernel -O | mtd write - $CI_KERNPART
  249. }
  250. [ "$kernel_length" = 0 -o ! -z "$kernel_mtd" ] && has_kernel=
  251. [ "$CI_KERNPART" = "none" ] && has_kernel=
  252. nand_upgrade_prepare_ubi "$rootfs_length" "$rootfs_type" "${has_kernel:+$kernel_length}" "$has_env"
  253. local ubidev="$( nand_find_ubi "$CI_UBIPART" )"
  254. [ "$has_kernel" = "1" ] && {
  255. local kern_ubivol="$( nand_find_volume $ubidev $CI_KERNPART )"
  256. tar xf "$tar_file" ${board_dir}/kernel -O | \
  257. ubiupdatevol /dev/$kern_ubivol -s $kernel_length -
  258. }
  259. [ "$has_rootfs" = "1" ] && {
  260. local root_ubivol="$( nand_find_volume $ubidev $CI_ROOTPART )"
  261. tar xf "$tar_file" ${board_dir}/root -O | \
  262. ubiupdatevol /dev/$root_ubivol -s $rootfs_length -
  263. }
  264. nand_do_upgrade_success
  265. }
  266. # Recognize type of passed file and start the upgrade process
  267. nand_do_upgrade() {
  268. local file_type=$(identify $1)
  269. [ ! "$(find_mtd_index "$CI_UBIPART")" ] && CI_UBIPART="rootfs"
  270. case "$file_type" in
  271. "fit") nand_upgrade_fit $1;;
  272. "ubi") nand_upgrade_ubinized $1;;
  273. "ubifs") nand_upgrade_ubifs $1;;
  274. *) nand_upgrade_tar $1;;
  275. esac
  276. }
  277. # Check if passed file is a valid one for NAND sysupgrade. Currently it accepts
  278. # 3 types of files:
  279. # 1) UBI - should contain an ubinized image, header is checked for the proper
  280. # MAGIC
  281. # 2) UBIFS - should contain UBIFS partition that will replace "rootfs" volume,
  282. # header is checked for the proper MAGIC
  283. # 3) TAR - archive has to include "sysupgrade-BOARD" directory with a non-empty
  284. # "CONTROL" file (at this point its content isn't verified)
  285. #
  286. # You usually want to call this function in platform_check_image.
  287. #
  288. # $(1): board name, used in case of passing TAR file
  289. # $(2): file to be checked
  290. nand_do_platform_check() {
  291. local board_name="$1"
  292. local tar_file="$2"
  293. local control_length=$( (tar xf $tar_file sysupgrade-$board_name/CONTROL -O | wc -c) 2> /dev/null)
  294. local file_type="$(identify $2)"
  295. [ "$control_length" = 0 -a "$file_type" != "ubi" -a "$file_type" != "ubifs" -a "$file_type" != "fit" ] && {
  296. echo "Invalid sysupgrade file."
  297. return 1
  298. }
  299. return 0
  300. }