experimental diff for issue757
[grml-live.git] / scripts / bootgrub.mksh
1 #!/usr/bin/env mksh
2 # $Id$
3 # $miros: src/sys/arch/i386/stand/bootxx/mkbxinst.sh,v 1.25 2009/07/24 16:27:56 tg Exp $
4 # $miros: src/sys/arch/i386/stand/bootxx/bootxx.S,v 1.25 2009/06/29 20:50:59 tg Exp $ +t:GRUB +s:MBR
5 #-
6 # Copyright (c) 2007, 2008, 2009
7 #       Thorsten Glaser <tg@mirbsd.org>
8 #
9 # Provided that these terms and disclaimer and all copyright notices
10 # are retained or reproduced in an accompanying document, permission
11 # is granted to deal in this work without restriction, including un‐
12 # limited rights to use, publicly perform, distribute, sell, modify,
13 # merge, give away, or sublicence.
14 #
15 # This work is provided “AS IS” and WITHOUT WARRANTY of any kind, to
16 # the utmost extent permitted by applicable law, neither express nor
17 # implied; without malicious intent or gross negligence. In no event
18 # may a licensor, author or contributor be held liable for indirect,
19 # direct, other damage, loss, or other issues arising in any way out
20 # of dealing in the work, even if advised of the possibility of such
21 # damage or existence of a defect, except proven that it results out
22 # of said person’s immediate fault when using the work as intended.
23 #-
24 # Self-installing 32-bit x86 boot blocks for GNU GRUB2 on i386-pc
25 # Reads a list of extents (firstblock lastblock) from standard input
26 # and writes bootxx to standard output, which can subsequentially be
27 # stored as partition boot record (or floppy boot sector) on disc.
28
29 set -A thecode 0x66 0x31 0xC9 0x8E 0xD1 0xBC 0xFC 0x7B 0x66 0x51 0x66 0x9D 0x8E 0xC1 0xBE 0x00 0x7C 0x8E 0xD9 0xBB 0x00 0x08 0x53 0x53 0x8E 0xC3 0xBF 0x00 0xFE 0xB5 0x02 0xF3 0xA4 0x1F 0x68 0x6B 0xFE 0xCB 0x30 0x41 0x41 0x41 0x20 0x4C 0x6F 0x61 0x64 0x69 0x6E 0x67 0x20 0x00 0x20 0x65 0x72 0x72 0x6F 0x72 0x0D 0x0A 0x00 0x96 0x02 0x00 0x12 0x00 0x00 0x80 0x65 0xFF 0xB4 0x0E 0xBB 0x07 0x00 0xCD 0x10 0xAC 0x08 0xC0 0x75 0xF4 0xC3 0xE8 0xF7 0xFF 0xB8 0x41 0xFF 0x87 0x06 0x44 0xFE 0x3D 0x41 0xFF 0x75 0x19 0x31 0xC0 0xCD 0x16 0xEA 0xF0 0xFF 0x00 0xF0 0xFB 0x88 0x16 0x43 0xFE 0xBE 0x26 0xFE 0x80 0xFA 0x80 0x72 0xDB 0xE8 0xD2 0xFF 0x31 0xDB 0xBE 0x68 0xFF 0x0F 0xB6 0x2E 0x3D 0xFE 0xAC 0x0F 0xB6 0xC8 0xC0 0xE9 0x05 0x83 0xE0 0x1F 0x40 0x41 0x50 0xBF 0x10 0xFE 0xB8 0x10 0x00 0xAB 0xB0 0x01 0xAB 0x89 0xD8 0xAB 0x8C 0xC8 0xAB 0xF3 0xA4 0x31 0xC0 0xAB 0xAB 0xAB 0xAB 0x5F 0x55 0x56 0xBE 0x10 0xFE 0xFF 0x16 0x44 0xFE 0xBD 0x04 0x00 0x8A 0x16 0x43 0xFE 0x60 0xF9 0xCD 0x13 0x9C 0xB8 0x2E 0x0E 0xBB 0x07 0x00 0xCD 0x10 0x9D 0xFB 0x61 0x73 0x1C 0x4D 0x60 0x9C 0x31 0xC0 0xCD 0x13 0x9D 0xBE 0x34 0xFE 0x0F 0x84 0x77 0xFF 0xB8 0x30 0x0E 0x01 0xE8 0xBB 0x07 0x00 0xCD 0x10 0x61 0xEB 0xD2 0xB4 0x02 0x00 0xE7 0x89 0x5C 0x04 0x00 0xFC 0x72 0x14 0x66 0x83 0x44 0x08 0x01 0x66 0x83 0x54 0x0C 0x00 0x4F 0x75 0xAC 0x5E 0x5D 0x4D 0x0F 0x85 0x7D 0xFF 0xBE 0x3A 0xFE 0xE8 0x3F 0xFF 0x66 0x31 0xD2 0x52 0x66 0x4A 0x8A 0x16 0x43 0xFE 0xB8 0x00 0x82 0x50 0xFA 0xCB 0x60 0x06 0xB4 0x08 0x8A 0x16 0x43 0xFE 0xF9 0xCD 0x13 0xFB 0x07 0xBE 0x34 0xFE 0x0F 0x82 0x21 0xFF 0x80 0xE1 0x3F 0x88 0x0E 0x40 0xFE 0x0F 0xB6 0xC6 0x40 0xA3 0x3E 0xFE 0x61 0x8B 0x0E 0x40 0xFE 0xE3 0xD7 0x8B 0x44 0x08 0x8B 0x54 0x0A 0xF7 0xF1 0x42 0x31 0xC9 0x87 0xD1 0xF7 0x36 0x3E 0xFE 0xC0 0xE4 0x06 0x86 0xC4 0x09 0xC1 0x88 0xD6 0xB8 0x01 0x02 0xC3 0xB4 0x42 0xC3
30 typeset -i ofs_bkcnt=61
31 typeset -i ofs_geomh=62
32 typeset -i ofs_geoms=64
33 typeset -i ofs_partp=66
34 typeset -i ofs_secsz=234
35 typeset -i begptr=360
36 typeset -Uui8 thecode
37
38 typeset -Uui16 curptr=begptr
39 typeset -i wnum=0 wofs=0 wrec=0 bkend=0x1FE
40
41 function do_record {
42         typeset -Ui blk=$1 cnt=$2 n
43         typeset -Uui16 x=blk y
44
45         (( blk && cnt )) || return
46
47         print -u2 "$wrec @0x${curptr#16#}: $cnt @$blk (0x${x#16#})"
48
49         while (( cnt )); do
50                 let wrec++
51                 (( n = blk < 0x00000100 ? 0 :
52                     blk < 0x00010000 ? 1 :
53                     blk < 0x01000000 ? 2 : 3 ))
54                 (( x = cnt < 33 ? cnt : 32 ))
55                 (( y = blk ))
56                 print -u2 " - 0x${curptr#16#}: $((x)) (0x${x#16#}) @ $blk" \
57                     "(0x${y#16#})"
58                 (( thecode[curptr++] = (n++ << 5) | (x - 1) ))
59                 (( blk += x ))
60                 (( cnt -= x ))
61                 while (( n-- )); do
62                         (( thecode[curptr++] = y & 0xFF ))
63                         (( y >>= 8 ))
64                 done
65         done
66 }
67
68 function record_block {
69         typeset -Ui blk=$1
70
71         if (( !blk || (wofs && blk != (wofs + wnum)) )); then
72                 # flush the blocks from the cache
73                 (( wnum )) && do_record $wofs $wnum
74                 wofs=0
75                 wnum=0
76         fi
77         if (( blk )); then
78                 # record some new block into the cache
79                 (( wofs )) || let wofs=blk
80                 (( wnum += 1 << sscale ))
81         fi
82 }
83
84 typeset -i partp=0 numheads=0 numsecs=0 sscale=0 bsh=9 mbrpno=0 mbrptp=0
85 set -A g_code 0 0 0
86
87 while getopts ":0:1AB:g:h:M:p:S:s:" ch; do
88         case $ch {
89         (0)     ;;
90         (1)     ;;
91         (A)     numheads=0
92                 numsecs=99
93                 ;;
94         (B)     if (( (bsh = OPTARG) < 9 || OPTARG > 15 )); then
95                         print -u2 error: invalid block size "2^'$OPTARG'"
96                         exit 1
97                 fi
98                 ;;
99         (g)     if [[ $OPTARG != +([0-9]):+([0-9]):+([0-9]) ]]; then
100                         print -u2 Error: invalid geometry code "'$OPTARG'"
101                         exit 1
102                 fi
103                 saveIFS=$IFS
104                 IFS=:
105                 set -A g_code -- $OPTARG
106                 IFS=$saveIFS ;;
107         (h)     if (( (numheads = OPTARG) < 1 || OPTARG > 256 )); then
108                         print -u2 warning: invalid head count "'$OPTARG'"
109                         numheads=0
110                 fi ;;
111         (M)     if [[ $OPTARG != +([0-9])?(:?(0[Xx])+([0-9])) ]]; then
112                         print -u2 warning: invalid partition info "'$OPTARG'"
113                         mbrpno=0
114                         mbrptp=0
115                 fi
116                 saveIFS=$IFS
117                 IFS=:
118                 set -A mbr_code -- $OPTARG
119                 IFS=$saveIFS
120                 (( mbrpno = mbr_code[0] ))
121                 (( mbrptp = mbr_code[1] ))
122                 if (( mbrpno < 1 || mbrpno > 4 )); then
123                         print -u2 warning: invalid partition number "'$OPTARG'"
124                         mbrpno=0
125                 fi
126                 if (( mbrptp < 1 || mbrptp > 255 )); then
127                         print -u2 warning: invalid partition type "'$OPTARG'"
128                         mbrptp=0
129                 fi ;;
130         (p)     if (( (partp = OPTARG) < 1 || OPTARG > 255 )); then
131                         print -u2 warning: invalid partition type "'$OPTARG'"
132                         partp=0
133                 fi ;;
134         (S)     if (( (sscale = OPTARG) < 0 || OPTARG > 24 )); then
135                         print -u2 error: invalid input scale "'$OPTARG'"
136                         exit 1
137                 fi ;;
138         (s)     if (( (numsecs = OPTARG) < 1 || OPTARG > 63 )); then
139                         print -u2 warning: invalid sector count "'$OPTARG'"
140                         numsecs=0
141                 fi ;;
142         (*)     print -u2 'Syntax:
143         bxinst [-1A] [-B blocksize] [-g C:H:S] [-h heads] [-M pno(1..4)[:typ]]
144             [-p partitiontype] [-S scale] [-s sectors] <sectorlist | \\
145             dd of=image conv=notrunc
146 Default values: blocksize=9 heads=16 sectors=63 partitiontype=0x27 scale=0
147     partno=4 if -g (create MBR partition) is given; -A = auto boot geometry'
148                 exit 1 ;;
149         }
150 done
151 shift $((OPTIND - 1))
152
153 typeset -Ui psz=0       # must be unsigned
154 if (( g_code[0] )); then
155         # bounds check partition table values, calculate total sectors
156         if (( g_code[0] < 1 || g_code[1] < 1 || g_code[1] > 256 ||
157             g_code[2] < 1 || g_code[2] > 63 )); then
158                 print -u2 Invalid geometry, values out of bounds.
159         elif [[ $(print "(${g_code[0]} * ${g_code[1]} * ${g_code[2]})" \
160             "> 4294967295" | bc) = 1 ]]; then
161                 print -u2 Invalid geometry, more than 2 TiB of data.
162         else
163                 # we know it's <= 2^32-1
164                 (( psz = g_code[0] * g_code[1] * g_code[2] ))
165         fi
166 fi
167 if (( psz )); then
168         print -u2 geometry is $psz sectors \($(print \
169             "$psz * $((1 << bsh))" | bc) bytes\) in ${g_code[0]} cylinders, \
170             ${g_code[1]} heads, ${g_code[2]} sectors per track
171         if (( numsecs == 0 || (numsecs != 99 && numheads == 0) )); then
172                 print -u2 warning: using these values for C/H/S boot
173                 numheads=${g_code[1]}
174                 numsecs=${g_code[2]}
175         fi
176         (( mbrpno )) || mbrpno=4        # default partition number
177 fi
178 if (( mbrpno )); then
179         bkend=0x1BE
180         (( psz )) || print -u2 warning: no geometry given, will not \
181             create an MBR partition table entry
182 fi
183
184 if (( numsecs == 99 )); then
185         numheads=0
186         numsecs=0
187 else
188         if (( !numheads )); then
189                 print -u2 warning: using default value of 16 heads
190                 numheads=16
191         fi
192
193         if (( !numsecs )); then
194                 print -u2 warning: using default value of 63 sectors
195                 numsecs=63
196         fi
197 fi
198
199 # read in the extents
200 while read firstblock lastblock junk; do
201         while (( firstblock <= lastblock )); do
202                 record_block $((firstblock++ << sscale))
203         done
204 done
205 record_block 0  # just flush
206 print -u2 "using $wrec blocks, $((curptr-begptr)) bytes ($((bkend-curptr)) free)"
207
208 # fill the block table
209 if (( curptr-- > bkend )); then
210         print -u2 error: too many blocks
211         exit 1
212 fi
213 while (( ++curptr < bkend )); do
214         (( thecode[curptr] = (curptr & 0xFCF) == 0x1C2 ? 0 : RANDOM & 0xFF ))
215         # ensure the “active” flag is never set to 0x00 or 0x80
216         if (( ((curptr + 2) & 0xFCF) == 0x01C0 )); then
217                 (( thecode[curptr] & 0x7F )) || let --curptr
218         fi
219 done
220 thecode[510]=0x55
221 thecode[511]=0xAA
222
223 # fill in other data
224 (( thecode[ofs_bkcnt] = wrec ))
225 (( thecode[ofs_geomh] = numheads & 0xFF ))
226 (( thecode[ofs_geomh + 1] = numheads >> 8 ))
227 (( thecode[ofs_geoms] = numsecs ))
228 (( thecode[ofs_partp] = partp ))
229 print -u2 "using sectors of 2^$bsh = $((1 << bsh)) bytes"
230 (( thecode[ofs_secsz] = (1 << (bsh - 8)) ))
231
232 # create an MBR partition if desired
233 if (( psz )); then
234         (( mbrpno = 0x1BE + ((mbrpno - 1) * 16) ))
235         thecode[mbrpno++]=0x80
236         thecode[mbrpno++]=0
237         thecode[mbrpno++]=1
238         thecode[mbrpno++]=0
239         (( thecode[mbrpno++] = (mbrptp ? mbrptp : partp ? partp : 0x27) ))
240         (( thecode[mbrpno++] = g_code[1] - 1 ))
241         (( cylno = g_code[0] > 1024 ? 1023 : g_code[0] - 1 ))
242         (( thecode[mbrpno++] = g_code[2] | ((cylno & 0x0300) >> 2) ))
243         (( thecode[mbrpno++] = cylno & 0x00FF ))
244         thecode[mbrpno++]=0
245         thecode[mbrpno++]=0
246         thecode[mbrpno++]=0
247         thecode[mbrpno++]=0
248         (( thecode[mbrpno++] = psz & 0xFF ))
249         (( thecode[mbrpno++] = (psz >> 8) & 0xFF ))
250         (( thecode[mbrpno++] = (psz >> 16) & 0xFF ))
251         (( thecode[mbrpno++] = (psz >> 24) & 0xFF ))
252 fi
253
254 # create the output string
255 ostr=
256 curptr=0
257 while (( curptr < 512 )); do
258         ostr=$ostr\\0${thecode[curptr++]#8#}
259 done
260
261 # over and out
262 print -n "$ostr"
263 exit 0