create.sh 9.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353
  1. #!/usr/bin/env bash
  2. #-
  3. # Copyright © 2010, 2011, 2012
  4. # Thorsten Glaser <tg@mirbsd.org>
  5. # Copyright © 2010, 2011
  6. # Waldemar Brodkorb <wbx@openadk.org>
  7. #
  8. # Provided that these terms and disclaimer and all copyright notices
  9. # are retained or reproduced in an accompanying document, permission
  10. # is granted to deal in this work without restriction, including un‐
  11. # limited rights to use, publicly perform, distribute, sell, modify,
  12. # merge, give away, or sublicence.
  13. #
  14. # This work is provided “AS IS” and WITHOUT WARRANTY of any kind, to
  15. # the utmost extent permitted by applicable law, neither express nor
  16. # implied; without malicious intent or gross negligence. In no event
  17. # may a licensor, author or contributor be held liable for indirect,
  18. # direct, other damage, loss, or other issues arising in any way out
  19. # of dealing in the work, even if advised of the possibility of such
  20. # damage or existence of a defect, except proven that it results out
  21. # of said person’s immediate fault when using the work as intended.
  22. #
  23. # Alternatively, this work may be distributed under the Terms of the
  24. # General Public License, any version as published by the Free Soft‐
  25. # ware Foundation.
  26. #-
  27. # Create a hard disc image, bootable using GNU GRUB2, with an ext2fs
  28. # root partition and an OpenADK cfgfs partition.
  29. TOPDIR=$(pwd)
  30. HOST=$(gcc -dumpmachine)
  31. me=$0
  32. case :$PATH: in
  33. (*:$TOPDIR/host_$HOST/usr/bin:*) ;;
  34. (*) export PATH=$PATH:$TOPDIR/host_$HOST/usr/bin ;;
  35. esac
  36. test -n "$KSH_VERSION" || if ! which mksh >/dev/null 2>&1; then
  37. make package=mksh fetch || exit 1
  38. df=mksh-R48b.tgz
  39. rm -rf build_mksh
  40. mkdir -p build_mksh
  41. gzip -dc dl/"$df" | (cd build_mksh; cpio -mid)
  42. cd build_mksh/mksh
  43. bash Build.sh -r -c lto || bash Build.sh -r || exit 1
  44. cp mksh "$TOPDIR"/bin
  45. cd "$TOPDIR"
  46. rm -rf build_mksh
  47. fi
  48. test -n "$KSH_VERSION" || exec mksh "$me" "$@"
  49. if test -z "$KSH_VERSION"; then
  50. echo >&2 Fatal error: could not run myself with mksh!
  51. exit 255
  52. fi
  53. ### run with mksh from here onwards ###
  54. me=${me##*/}
  55. TOPDIR=$(realpath .)
  56. ostype=$(uname -s)
  57. function usage {
  58. cat >&2 <<EOF
  59. Syntax: $me [-c cfgfssize] [+g] [-i imagesize] [-p panictime]
  60. [-s serialspeed] [-t] [-T imagetype] [+U] target.ima source.tgz
  61. Explanation/Defaults:
  62. -c: minimum 0, maximum 5, default 1 (MiB)
  63. +g: disables installing GNU GRUB 2 (-g enables it, default)
  64. -i: total image, default 512 (MiB; max. approx. 2 TiB)
  65. -p: default 10 (seconds; 0 disables; max. 300)
  66. -s: default 115200 (bps, others: 9600 19200 38400 57600)
  67. -t: enable serial console (+t disables it, default)
  68. -T: image type (default raw, others: vdi)
  69. EOF
  70. exit ${1:-1}
  71. }
  72. cfgfs=1
  73. usegrub=1
  74. tgtmib=512
  75. panicreboot=10
  76. speed=115200
  77. serial=0
  78. tgttype=raw
  79. while getopts "c:ghi:p:s:tT:" ch; do
  80. case $ch {
  81. (c) if (( (cfgfs = OPTARG) < 0 || cfgfs > 5 )); then
  82. print -u2 "$me: -c $OPTARG out of bounds"
  83. usage
  84. fi ;;
  85. (g) usegrub=1 ;;
  86. (+g) usegrub=0 ;;
  87. (h) usage 0 ;;
  88. (i) if (( (tgtmib = OPTARG) < 7 || tgtmib > 2097150 )); then
  89. print -u2 "$me: -i $OPTARG out of bounds"
  90. usage
  91. fi ;;
  92. (p) if (( (panicreboot = OPTARG) < 0 || panicreboot > 300 )); then
  93. print -u2 "$me: -p $OPTARG out of bounds"
  94. usage
  95. fi ;;
  96. (s) if [[ $OPTARG != @(96|192|384|576|1152)00 ]]; then
  97. print -u2 "$me: serial speed $OPTARG invalid"
  98. usage
  99. fi
  100. speed=$OPTARG ;;
  101. (t) serial=1 ;;
  102. (+t) serial=0 ;;
  103. (T) if [[ $OPTARG != @(raw|vdi) ]]; then
  104. print -u2 "$me: image type $OPTARG invalid"
  105. usage
  106. fi
  107. tgttype=$OPTARG ;;
  108. (*) usage 1 ;;
  109. }
  110. done
  111. shift $((OPTIND - 1))
  112. (( $# == 2 )) || usage 1
  113. f=0
  114. tools='bc genext2fs'
  115. [[ $tgttype = vdi ]] && tools="$tools VBoxManage"
  116. for tool in $tools; do
  117. print -n Checking if $tool is installed...
  118. if whence -p $tool >/dev/null; then
  119. print " okay"
  120. else
  121. print " failed"
  122. f=1
  123. fi
  124. done
  125. (( f )) && exit 1
  126. if bc --help >/dev/null 2>&1; then
  127. # GNU bc shows a “welcome message” which irritates scripts
  128. bc='bc -q'
  129. else
  130. bc=bc
  131. fi
  132. tgt=$1
  133. [[ $tgt = /* ]] || tgt=$PWD/$tgt
  134. src=$2
  135. if [[ ! -f $src ]]; then
  136. print -u2 "'$src' is not a file, exiting"
  137. exit 1
  138. fi
  139. if ! T=$(mktemp -d /tmp/openadk.XXXXXXXXXX); then
  140. print -u2 Error creating temporary directory.
  141. exit 1
  142. fi
  143. print "Installing $src on $tgt."
  144. cyls=$tgtmib
  145. heads=64
  146. secs=32
  147. if stat -qs .>/dev/null 2>&1; then
  148. statcmd='stat -f %z' # BSD stat (or so we assume)
  149. else
  150. statcmd='stat -c %s' # GNU stat
  151. fi
  152. if (( usegrub )); then
  153. tar -xOzf "$src" usr/share/grub-bin/core.img >"$T/core.img"
  154. integer coreimgsz=$($statcmd "$T/core.img")
  155. if (( coreimgsz < 1024 )); then
  156. print -u2 core.img is probably too small: $coreimgsz
  157. rm -rf "$T"
  158. exit 1
  159. fi
  160. if (( coreimgsz > 65024 )); then
  161. print -u2 core.img is larger than 64K-512: $coreimgsz
  162. rm -rf "$T"
  163. exit 1
  164. fi
  165. else
  166. # fake it
  167. integer coreimgsz=1
  168. fi
  169. (( coreendsec = (coreimgsz + 511) / 512 ))
  170. corestartsec=1
  171. corepatchofs=$((0x414))
  172. # partition offset: at least coreendsec+1 but aligned on a multiple of secs
  173. (( partofs = ((coreendsec / secs) + 1) * secs ))
  174. # calculate size of ext2fs in KiB as image size minus cfgfs minus firsttrack
  175. ((# partfssz = ((cyls - cfgfs) * 64 * 32 - partofs) / 2 ))
  176. if (( usegrub )); then
  177. print Preparing MBR and GRUB2...
  178. else
  179. print Preparing partition table...
  180. fi
  181. dd if=/dev/zero of="$T/firsttrack" count=$partofs 2>/dev/null
  182. echo $corestartsec $coreendsec | mksh "$TOPDIR/scripts/bootgrub.mksh" \
  183. -A -g $((cyls - cfgfs)):$heads:$secs -M 1:0x83 -O $partofs | \
  184. dd of="$T/firsttrack" conv=notrunc 2>/dev/null
  185. if (( usegrub )); then
  186. dd if="$T/core.img" of="$T/firsttrack" conv=notrunc \
  187. seek=$corestartsec 2>/dev/null
  188. # set partition where it can find /boot/grub
  189. print -n '\0\0\0\0' | \
  190. dd of="$T/firsttrack" conv=notrunc bs=1 seek=$corepatchofs \
  191. 2>/dev/null
  192. fi
  193. # create cfgfs partition (mostly taken from bootgrub.mksh)
  194. set -A thecode
  195. typeset -Uui8 thecode
  196. mbrpno=0
  197. set -A g_code $cyls $heads $secs
  198. (( pssz = cfgfs * g_code[1] * g_code[2] ))
  199. (( pofs = (cyls - cfgfs) * g_code[1] * g_code[2] ))
  200. set -A o_code # g_code equivalent for partition offset
  201. (( o_code[2] = pofs % g_code[2] + 1 ))
  202. (( o_code[1] = pofs / g_code[2] ))
  203. (( o_code[0] = o_code[1] / g_code[1] + 1 ))
  204. (( o_code[1] = o_code[1] % g_code[1] + 1 ))
  205. # boot flag; C/H/S offset
  206. thecode[mbrpno++]=0x00
  207. (( thecode[mbrpno++] = o_code[1] - 1 ))
  208. (( cylno = o_code[0] > 1024 ? 1023 : o_code[0] - 1 ))
  209. (( thecode[mbrpno++] = o_code[2] | ((cylno & 0x0300) >> 2) ))
  210. (( thecode[mbrpno++] = cylno & 0x00FF ))
  211. # partition type; C/H/S end
  212. (( thecode[mbrpno++] = 0x88 ))
  213. (( thecode[mbrpno++] = g_code[1] - 1 ))
  214. (( cylno = g_code[0] > 1024 ? 1023 : g_code[0] - 1 ))
  215. (( thecode[mbrpno++] = g_code[2] | ((cylno & 0x0300) >> 2) ))
  216. (( thecode[mbrpno++] = cylno & 0x00FF ))
  217. # partition offset, size (LBA)
  218. (( thecode[mbrpno++] = pofs & 0xFF ))
  219. (( thecode[mbrpno++] = (pofs >> 8) & 0xFF ))
  220. (( thecode[mbrpno++] = (pofs >> 16) & 0xFF ))
  221. (( thecode[mbrpno++] = (pofs >> 24) & 0xFF ))
  222. (( thecode[mbrpno++] = pssz & 0xFF ))
  223. (( thecode[mbrpno++] = (pssz >> 8) & 0xFF ))
  224. (( thecode[mbrpno++] = (pssz >> 16) & 0xFF ))
  225. (( thecode[mbrpno++] = (pssz >> 24) & 0xFF ))
  226. # write partition table entry
  227. ostr=
  228. curptr=0
  229. while (( curptr < 16 )); do
  230. ostr=$ostr\\0${thecode[curptr++]#8#}
  231. done
  232. print -n "$ostr" | \
  233. dd of="$T/firsttrack" conv=notrunc bs=1 seek=$((0x1CE)) 2>/dev/null
  234. print Extracting installation archive...
  235. mkdir "$T/src"
  236. gzip -dc "$src" | (cd "$T/src"; tar -xpf -)
  237. cd "$T/src"
  238. rnddev=/dev/urandom
  239. [[ -c /dev/arandom ]] && rnddev=/dev/arandom
  240. dd if=$rnddev bs=16 count=1 >>etc/.rnd 2>/dev/null
  241. print Fixing up permissions...
  242. #chown 0:0 tmp
  243. chmod 1777 tmp
  244. chmod 4755 bin/busybox
  245. [[ -f usr/bin/Xorg ]] && chmod 4755 usr/bin/Xorg
  246. [[ -f usr/bin/sudo ]] && chmod 4755 usr/bin/sudo
  247. if (( usegrub )); then
  248. print Configuring GRUB2 bootloader...
  249. mkdir -p boot/grub
  250. (
  251. print set default=0
  252. print set timeout=1
  253. if (( serial )); then
  254. print serial --unit=0 --speed=$speed
  255. print terminal_output serial
  256. print terminal_input serial
  257. consargs="console=ttyS0,$speed console=tty0"
  258. else
  259. print terminal_output console
  260. print terminal_input console
  261. consargs="console=tty0"
  262. fi
  263. print
  264. print 'menuentry "GNU/Linux (OpenADK)" {'
  265. linuxargs="root=/dev/sda1 $consargs"
  266. (( panicreboot )) && linuxargs="$linuxargs panic=$panicreboot"
  267. print "\tlinux /boot/kernel $linuxargs"
  268. print '}'
  269. ) >boot/grub/grub.cfg
  270. set -A grubfiles
  271. ngrubfiles=0
  272. for a in usr/lib/grub/*-pc/{*.mod,efiemu??.o,command.lst,moddep.lst,fs.lst,handler.lst,parttool.lst}; do
  273. [[ -e $a ]] && grubfiles[ngrubfiles++]=$a
  274. done
  275. cp "${grubfiles[@]}" boot/grub/
  276. fi
  277. print "Creating ext2fs filesystem image..."
  278. cd "$T"
  279. f=0
  280. genext2fs -U -N 32768 -b $((partfssz)) -d src fsimg || f=1
  281. if (( !f )); then
  282. # use bc(1): this may be over the shell’s 32-bit arithmetics
  283. wantsz=$($bc <<<"$((partfssz))*1024")
  284. gotsz=$($statcmd fsimg)
  285. if [[ $wantsz != "$gotsz" ]]; then
  286. print -u2 "Error: want $wantsz bytes, got $gotsz bytes!"
  287. f=1
  288. fi
  289. fi
  290. if (( f )); then
  291. print -u2 "Error creating ext2fs filesystem image"
  292. cd /
  293. rm -rf "$T"
  294. exit 1
  295. fi
  296. # delete source tree, to save disc space
  297. rm -rf src
  298. if [[ $tgttype = raw ]]; then
  299. tgttmp=$tgt
  300. else
  301. tgttmp=$T/dst.ima
  302. fi
  303. print "Putting together raw output image $tgttmp..."
  304. dd if=/dev/zero bs=1048576 count=$cfgfs 2>/dev/null | \
  305. cat firsttrack fsimg - >"$tgttmp"
  306. # use bc(1): this may be over the shell’s 32-bit arithmetics
  307. wantsz=$($bc <<<"$tgtmib*1048576")
  308. gotsz=$($statcmd "$tgttmp")
  309. if [[ $wantsz != "$gotsz" ]]; then
  310. print -u2 "Error: want $wantsz bytes, got $gotsz bytes!"
  311. cd /
  312. rm -rf "$T"
  313. exit 1
  314. fi
  315. case $tgttype {
  316. (raw)
  317. ;;
  318. (vdi)
  319. print "Converting raw image to VDI..."
  320. VBoxManage convertdd dst.ima dst.vdi
  321. rm dst.ima
  322. print "Moving VDI image to $tgt..."
  323. mv -f dst.vdi "$tgt".vdi
  324. ;;
  325. }
  326. print Finishing up...
  327. cd "$TOPDIR"
  328. rm -rf "$T"
  329. exit 0