genimage.sh 6.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272
  1. #!/bin/bash
  2. #
  3. # This file is subject to the terms and conditions of the GNU General Public
  4. # License. See the file "COPYING" in the main directory of this archive
  5. # for more details.
  6. #
  7. # Copyright (C) 2017 by Changbin Du <[email protected]>
  8. #
  9. # Adapted from code in arch/x86/boot/Makefile by H. Peter Anvin and others
  10. #
  11. # "make fdimage/fdimage144/fdimage288/hdimage/isoimage"
  12. # script for x86 architecture
  13. #
  14. # Arguments:
  15. # $1 - fdimage format
  16. # $2 - target image file
  17. # $3 - kernel bzImage file
  18. # $4 - mtools configuration file
  19. # $5 - kernel cmdline
  20. # $6+ - initrd image file(s)
  21. #
  22. # This script requires:
  23. # bash
  24. # syslinux
  25. # mtools (for fdimage* and hdimage)
  26. # edk2/OVMF (for hdimage)
  27. #
  28. # Otherwise try to stick to POSIX shell commands...
  29. #
  30. # Use "make V=1" to debug this script
  31. case "${KBUILD_VERBOSE}" in
  32. *1*)
  33. set -x
  34. ;;
  35. esac
  36. # Exit the top-level shell with an error
  37. topshell=$$
  38. trap 'exit 1' USR1
  39. die() {
  40. echo "" 1>&2
  41. echo " *** $*" 1>&2
  42. echo "" 1>&2
  43. kill -USR1 $topshell
  44. }
  45. # Verify the existence and readability of a file
  46. verify() {
  47. if [ ! -f "$1" -o ! -r "$1" ]; then
  48. die "Missing file: $1"
  49. fi
  50. }
  51. diskfmt="$1"
  52. FIMAGE="$2"
  53. FBZIMAGE="$3"
  54. MTOOLSRC="$4"
  55. KCMDLINE="$5"
  56. shift 5 # Remaining arguments = initrd files
  57. export MTOOLSRC
  58. # common options for dd
  59. dd='dd iflag=fullblock'
  60. # Make sure the files actually exist
  61. verify "$FBZIMAGE"
  62. declare -a FDINITRDS
  63. irdpfx=' initrd='
  64. initrdopts_syslinux=''
  65. initrdopts_efi=''
  66. for f in "$@"; do
  67. if [ -f "$f" -a -r "$f" ]; then
  68. FDINITRDS=("${FDINITRDS[@]}" "$f")
  69. fname="$(basename "$f")"
  70. initrdopts_syslinux="${initrdopts_syslinux}${irdpfx}${fname}"
  71. irdpfx=,
  72. initrdopts_efi="${initrdopts_efi} initrd=${fname}"
  73. fi
  74. done
  75. # Read a $3-byte littleendian unsigned value at offset $2 from file $1
  76. le() {
  77. local n=0
  78. local m=1
  79. for b in $(od -A n -v -j $2 -N $3 -t u1 "$1"); do
  80. n=$((n + b*m))
  81. m=$((m * 256))
  82. done
  83. echo $n
  84. }
  85. # Get the EFI architecture name such that boot{name}.efi is the default
  86. # boot file name. Returns false with no output if the file is not an
  87. # EFI image or otherwise unknown.
  88. efiarch() {
  89. [ -f "$1" ] || return
  90. [ $(le "$1" 0 2) -eq 23117 ] || return # MZ magic
  91. peoffs=$(le "$1" 60 4) # PE header offset
  92. [ $peoffs -ge 64 ] || return
  93. [ $(le "$1" $peoffs 4) -eq 17744 ] || return # PE magic
  94. case $(le "$1" $((peoffs+4+20)) 2) in # PE type
  95. 267) ;; # PE32
  96. 523) ;; # PE32+
  97. *) return 1 ;; # Invalid
  98. esac
  99. [ $(le "$1" $((peoffs+4+20+68)) 2) -eq 10 ] || return # EFI app
  100. case $(le "$1" $((peoffs+4)) 2) in # Machine type
  101. 332) echo i386 ;;
  102. 450) echo arm ;;
  103. 512) echo ia64 ;;
  104. 20530) echo riscv32 ;;
  105. 20580) echo riscv64 ;;
  106. 20776) echo riscv128 ;;
  107. 34404) echo x64 ;;
  108. 43620) echo aa64 ;;
  109. esac
  110. }
  111. # Get the combined sizes in bytes of the files given, counting sparse
  112. # files as full length, and padding each file to cluster size
  113. cluster=16384
  114. filesizes() {
  115. local t=0
  116. local s
  117. for s in $(ls -lnL "$@" 2>/dev/null | awk '/^-/{ print $5; }'); do
  118. t=$((t + ((s+cluster-1)/cluster)*cluster))
  119. done
  120. echo $t
  121. }
  122. # Expand directory names which should be in /usr/share into a list
  123. # of possible alternatives
  124. sharedirs() {
  125. local dir file
  126. for dir in /usr/share /usr/lib64 /usr/lib; do
  127. for file; do
  128. echo "$dir/$file"
  129. echo "$dir/${file^^}"
  130. done
  131. done
  132. }
  133. efidirs() {
  134. local dir file
  135. for dir in /usr/share /boot /usr/lib64 /usr/lib; do
  136. for file; do
  137. echo "$dir/$file"
  138. echo "$dir/${file^^}"
  139. done
  140. done
  141. }
  142. findsyslinux() {
  143. local f="$(find -L $(sharedirs syslinux isolinux) \
  144. -name "$1" -readable -type f -print -quit 2>/dev/null)"
  145. if [ ! -f "$f" ]; then
  146. die "Need a $1 file, please install syslinux/isolinux."
  147. fi
  148. echo "$f"
  149. return 0
  150. }
  151. findovmf() {
  152. local arch="$1"
  153. shift
  154. local -a names=(-false)
  155. local name f
  156. for name; do
  157. names=("${names[@]}" -or -iname "$name")
  158. done
  159. for f in $(find -L $(efidirs edk2 ovmf) \
  160. \( "${names[@]}" \) -readable -type f \
  161. -print 2>/dev/null); do
  162. if [ "$(efiarch "$f")" = "$arch" ]; then
  163. echo "$f"
  164. return 0
  165. fi
  166. done
  167. die "Need a $1 file for $arch, please install EDK2/OVMF."
  168. }
  169. do_mcopy() {
  170. if [ ${#FDINITRDS[@]} -gt 0 ]; then
  171. mcopy "${FDINITRDS[@]}" "$1"
  172. fi
  173. if [ -n "$efishell" ]; then
  174. mmd "$1"EFI "$1"EFI/Boot
  175. mcopy "$efishell" "$1"EFI/Boot/boot${kefiarch}.efi
  176. fi
  177. if [ -n "$kefiarch" ]; then
  178. echo linux "$KCMDLINE$initrdopts_efi" | \
  179. mcopy - "$1"startup.nsh
  180. fi
  181. echo default linux "$KCMDLINE$initrdopts_syslinux" | \
  182. mcopy - "$1"syslinux.cfg
  183. mcopy "$FBZIMAGE" "$1"linux
  184. }
  185. genbzdisk() {
  186. verify "$MTOOLSRC"
  187. mformat -v 'LINUX_BOOT' a:
  188. syslinux "$FIMAGE"
  189. do_mcopy a:
  190. }
  191. genfdimage144() {
  192. verify "$MTOOLSRC"
  193. $dd if=/dev/zero of="$FIMAGE" bs=1024 count=1440 2>/dev/null
  194. mformat -v 'LINUX_BOOT' v:
  195. syslinux "$FIMAGE"
  196. do_mcopy v:
  197. }
  198. genfdimage288() {
  199. verify "$MTOOLSRC"
  200. $dd if=/dev/zero of="$FIMAGE" bs=1024 count=2880 2>/dev/null
  201. mformat -v 'LINUX_BOOT' w:
  202. syslinux "$FIMAGE"
  203. do_mcopy w:
  204. }
  205. genhdimage() {
  206. verify "$MTOOLSRC"
  207. mbr="$(findsyslinux mbr.bin)"
  208. kefiarch="$(efiarch "$FBZIMAGE")"
  209. if [ -n "$kefiarch" ]; then
  210. # The efishell provides command line handling
  211. efishell="$(findovmf $kefiarch shell.efi shell${kefiarch}.efi)"
  212. ptype='-T 0xef' # EFI system partition, no GPT
  213. fi
  214. sizes=$(filesizes "$FBZIMAGE" "${FDINITRDS[@]}" "$efishell")
  215. # Allow 1% + 2 MiB for filesystem and partition table overhead,
  216. # syslinux, and config files; this is probably excessive...
  217. megs=$(((sizes + sizes/100 + 2*1024*1024 - 1)/(1024*1024)))
  218. $dd if=/dev/zero of="$FIMAGE" bs=$((1024*1024)) count=$megs 2>/dev/null
  219. mpartition -I -c -s 32 -h 64 $ptype -b 64 -a p:
  220. $dd if="$mbr" of="$FIMAGE" bs=440 count=1 conv=notrunc 2>/dev/null
  221. mformat -v 'LINUX_BOOT' -s 32 -h 64 -c $((cluster/512)) -t $megs h:
  222. syslinux --offset $((64*512)) "$FIMAGE"
  223. do_mcopy h:
  224. }
  225. geniso() {
  226. tmp_dir="$(dirname "$FIMAGE")/isoimage"
  227. rm -rf "$tmp_dir"
  228. mkdir "$tmp_dir"
  229. isolinux=$(findsyslinux isolinux.bin)
  230. ldlinux=$(findsyslinux ldlinux.c32)
  231. cp "$isolinux" "$ldlinux" "$tmp_dir"
  232. cp "$FBZIMAGE" "$tmp_dir"/linux
  233. echo default linux "$KCMDLINE" > "$tmp_dir"/isolinux.cfg
  234. cp "${FDINITRDS[@]}" "$tmp_dir"/
  235. genisoimage -J -r -appid 'LINUX_BOOT' -input-charset=utf-8 \
  236. -quiet -o "$FIMAGE" -b isolinux.bin \
  237. -c boot.cat -no-emul-boot -boot-load-size 4 \
  238. -boot-info-table "$tmp_dir"
  239. isohybrid "$FIMAGE" 2>/dev/null || true
  240. rm -rf "$tmp_dir"
  241. }
  242. rm -f "$FIMAGE"
  243. case "$diskfmt" in
  244. bzdisk) genbzdisk;;
  245. fdimage144) genfdimage144;;
  246. fdimage288) genfdimage288;;
  247. hdimage) genhdimage;;
  248. isoimage) geniso;;
  249. *) die "Unknown image format: $diskfmt";;
  250. esac