tazpanel diff bootloader @ rev 516

Makefile add install_extra
author Pascal Bellard <pascal.bellard@slitaz.org>
date Sun Aug 02 20:24:23 2015 +0200 (2015-08-02)
parents
children 506184fa1105
line diff
     1.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
     1.2 +++ b/bootloader	Sun Aug 02 20:24:23 2015 +0200
     1.3 @@ -0,0 +1,396 @@
     1.4 +#!/bin/sh
     1.5 +#
     1.6 +# This script creates a floppy image set from a linux bzImage and can merge
     1.7 +# a cmdline and/or one or more initramfs.
     1.8 +#
     1.9 +# (C) 2009-2015 Pascal Bellard - GNU General Public License v3.
    1.10 +
    1.11 +usage()
    1.12 +{
    1.13 +cat <<EOT
    1.14 +Usage: $0 bzImage [--prefix image_prefix] [--info file] [--quiet]
    1.15 +       [--format 1200|1440|1680|1920|2880|...] [--mem mb] [--tracks cnt]
    1.16 +       [--rdev device] [--video mode] [--flags rootflags] [--no-syssize-fix]
    1.17 +       [--dont-edit-cmdline] [--cmdline 'args'] [--hide-version-string]
    1.18 +       [--address-initrd address] [--initrd initrdfile]...
    1.19 +
    1.20 +Default values: --format 1440 --tracks 80 --rdev /dev/fd0 --prefix floppy. --mem 16
    1.21 +
    1.22 +Example:
    1.23 +$0 /boot/bzImage --rdev /dev/ram0 --video -3 --cmdline 'rw lang=fr_FR kmap=fr-latin1 laptop autologin' --initrd /boot/rootfs.gz --initrd ./myconfig.gz
    1.24 +
    1.25 +or: $0 --extract floppy*
    1.26 +
    1.27 +Create kernel and maybe cmdline, initrd and info files from a floppy images set 
    1.28 +EOT
    1.29 +exit 1
    1.30 +}
    1.31 +
    1.32 +# bzImage offsets
    1.33 +SetupSzOfs=497
    1.34 +FlagsOfs=498
    1.35 +OldRamfsLenOfs=504
    1.36 +VideoModeOfs=506
    1.37 +RootDevOfs=508
    1.38 +Magic=0x202
    1.39 +RamfsAdrOfs=0x218
    1.40 +RamfsLenOfs=0x21C
    1.41 +
    1.42 +ddq() { dd $@ 2> /dev/null; }
    1.43 +
    1.44 +get()
    1.45 +{
    1.46 +echo $( od -v -j $(($1)) -N ${4:-${3:-2}} -t u${3:-2} -w${3:-2} -An $2 2>/dev/null ||
    1.47 +	hexdump -v -s $(($1)) -n ${4:-${3:-2}} -e "\"\" 1/${3:-2} \" %d\n\"" $2 )
    1.48 +}
    1.49 +
    1.50 +trace()
    1.51 +{
    1.52 +	[ -n "$DEBUG" ] && printf "$@" 1>&2 && echo 1>&2
    1.53 +}
    1.54 +
    1.55 +# usage: store bits offset data file
    1.56 +store()
    1.57 +{
    1.58 +	n=$3; for i in $(seq 8 8 $1); do
    1.59 +		printf '\\\\x%02X' $(($n & 255))
    1.60 +		n=$(($n >> 8))
    1.61 +	done | xargs echo -en | ddq bs=1 conv=notrunc of=$4 seek=$(($2))
    1.62 +	s=$1; a=$2; d=$3; shift 4; c="$@"
    1.63 +	trace "store$s(%03X) = %0$(($s/4))X	$c" $a $d
    1.64 +}
    1.65 +
    1.66 +die()
    1.67 +{
    1.68 +	echo $@ 1>&2
    1.69 +	exit 1
    1.70 +}
    1.71 +extract()
    1.72 +{
    1.73 +	shift
    1.74 +	[ ! -s "$1" ] && die "No floppy ?"
    1.75 +	[ $(get 0x1FE "$1") -ne 43605 ] && die "Not bootable"
    1.76 +	[ $(get $Magic "$1" 4) != 1400005704 ] &&
    1.77 +	[ $(get 0x1F4 "$1") -gt 32768 -o $(get 0x1F6 "$1") -ne 0 ] &&
    1.78 +	die "Not linux."
    1.79 +	FORMAT="$(($(stat -c "%s" $1)/1024))"
    1.80 +	cat <<EOT
    1.81 +--format $FORMAT
    1.82 +--rdev $(printf "0x%04X" $(get $RootDevOfs $1))
    1.83 +--video $(get $VideoModeOfs $1)
    1.84 +--flags $(get $FlagsOfs $1)
    1.85 +EOT
    1.86 +	MYBB="$(ddq if=$1 bs=512 count=1 | strings | grep "Insert disk 00")"
    1.87 +	cmdline=0
    1.88 +	info=0
    1.89 +	if [ "$MYBB" ]; then
    1.90 +		cmdline=$(get 0x22 $1)
    1.91 +		info=$(get 0x1EF $1)
    1.92 +		[ $(get 0x75 $1) -eq $((0xDB0)) ] && echo "--dont-edit-cmdline"
    1.93 +		[ $(get 0x58 $1) -eq $((0x9090)) ] && echo "--hide-version-string"
    1.94 +	fi
    1.95 +	n=$(($(get $SetupSzOfs $1 1)+1))
    1.96 +	[ $n -eq 1 ] && n=5
    1.97 +	[ $info -ne 0 ] && infolen=$(($n-$info/512)) && n=$(($info/512))
    1.98 +	[ $cmdline -ne 0 ] && n=$(($cmdline/512))
    1.99 +	cat "$@" | {
   1.100 +		ddq bs=512 count=$n >kernel
   1.101 +		files="kernel"
   1.102 +		if [ "$MYBB" ]; then
   1.103 +			store 8 $SetupSzOfs $(($n-1)) kernel "setup size $n"
   1.104 +			store 16 0x22 0 kernel "clear cmdline"
   1.105 +			store 16 0x1EF 0 kernel "clear info"
   1.106 +		fi
   1.107 +		[ $cmdline -ne 0 ] && files="$files cmdline" &&
   1.108 +		ddq bs=512 count=1 | strings | sed q > cmdline
   1.109 +		[ $info -ne 0 ] && files="$files info" &&
   1.110 +		ddq bs=512 count=$infolen | sed \
   1.111 +		 's/'$(echo -en "\xff\xff$//;s/\xff/\f")'/g;s/\r/\n/g;q' > info
   1.112 +		syssz=$(get 0x1F4 kernel 4)
   1.113 +		ddq bs=16 count=$syssz >>kernel
   1.114 +		[ $(($syssz % 32)) -ne 0 ] &&
   1.115 +		ddq bs=16 of=/dev/null count=$((32 - ($syssz % 32)))
   1.116 +		if [ $(get $Magic kernel 4) == 1400005704 ]; then
   1.117 +	  		ddq bs=1 count=200 skip=$((512+$(get 0x20E kernel 2))) \
   1.118 +	  			if=kernel | strings | sed q
   1.119 +			len=$(get $RamfsLenOfs kernel 4)
   1.120 +			if [ $len -ne 0 ]; then
   1.121 +				adrs=$(get $RamfsAdrOfs kernel 4)
   1.122 +				printf "--address-initrd 0x%X \n" $adrs
   1.123 +				echo "--mem $(((($adrs+$len)/1024+512)/1024))"
   1.124 +				ddq bs=512 count=$((($len+511)/512)) > initrd
   1.125 +				ddq count=0 bs=1 seek=$len of=initrd
   1.126 +				files="$files initrd"
   1.127 +				store 64 $RamfsAdrOfs 0 kernel "reset initrd"
   1.128 +			fi
   1.129 +			if [ $(get 0x206 kernel) -ge 514 ]; then
   1.130 +				store 32 0x228 0 kernel "clean cmdline32"
   1.131 +			fi
   1.132 +		else
   1.133 +			len=$(get $OldRamfsLenOfs kernel)
   1.134 +			[ $len -ne 0 ] && files="$files initrd" &&
   1.135 +			if [ -s "$2" ]; then
   1.136 +				ddq if=$2 bs=1024 count=$len of=initrd
   1.137 +			else
   1.138 +				ddq if=$1 bs=1024 skip=256 count=$len of=initrd
   1.139 +			fi
   1.140 +		fi
   1.141 +		ls -l $files
   1.142 +	}
   1.143 +	exit
   1.144 +}
   1.145 +
   1.146 +KERNEL=""
   1.147 +INITRD=""
   1.148 +ADRSRD=""
   1.149 +CMDLINE=""
   1.150 +PREFIX="floppy."
   1.151 +FORMAT="1440"
   1.152 +RDEV=""
   1.153 +VIDEO=""
   1.154 +FLAGS=""
   1.155 +TRACKS="80"
   1.156 +MEM="16"
   1.157 +HIDE=""
   1.158 +NOEDIT=""
   1.159 +QUIET=""
   1.160 +NOSYSSIZEFIX=""
   1.161 +INFOFILE=""
   1.162 +DEBUG=""
   1.163 +while [ -n "$1" ]; do
   1.164 +	case "${1/--/-}" in
   1.165 +	-c*) CMDLINE="$2"; shift;;
   1.166 +	-inf*) INFOFILE="$2"; shift;;
   1.167 +	-i*) INITRD="$INITRD $2"; shift;;
   1.168 +	-a*) ADRSRD="$2"; shift;;
   1.169 +	-h*) HIDE="1";;
   1.170 +	-p*) PREFIX="$2"; shift;;
   1.171 +	-fl*)FLAGS="$2"; shift;;	# 1 read-only, 0 read-write
   1.172 +	-f*) FORMAT="$2"; shift;;
   1.173 +	-m*) MEM="$(echo $2 | sed 's/[^0-9]//g')"; shift;;
   1.174 +	-r*) RDEV="$2"; shift;;
   1.175 +	-v*) VIDEO="$2"; shift;;	# -3 .. n
   1.176 +	-t*) TRACKS="$2"; shift;;	# likely 81 .. 84
   1.177 +	-n*) NOSYSSIZEFIX="1";;
   1.178 +	-debug) DEBUG="1";;
   1.179 +	-d*) NOEDIT="1";;
   1.180 +	-q*) QUIET="1";;
   1.181 +	-e*) extract "$@";;
   1.182 +	*) KERNEL="$1";;
   1.183 +	esac
   1.184 +	shift
   1.185 +done
   1.186 +[ -n "$KERNEL" -a -f "$KERNEL" ] || usage
   1.187 +while [ -L "$KERNEL" ]; do KERNEL="$(readlink "$KERNEL")"; done
   1.188 +if [ $(( $FORMAT % $TRACKS )) -ne 0 ]; then
   1.189 +	echo "Invalid track count for format $FORMAT."
   1.190 +	usage
   1.191 +fi
   1.192 +[ 0$MEM -lt 2  ] && MEM=2
   1.193 +
   1.194 +patch()
   1.195 +{
   1.196 +	echo -en $(echo ":$2" | sed 's/:/\\x/g') | \
   1.197 +		ddq bs=1 conv=notrunc of=$3 seek=$((0x$1))
   1.198 +	trace "patch $1 $2		$4"
   1.199 +}
   1.200 +
   1.201 +error()
   1.202 +{
   1.203 +	rm -f $bs
   1.204 +	die $@
   1.205 +}
   1.206 +
   1.207 +floppyset()
   1.208 +{
   1.209 +	# boot+setup address
   1.210 +	SetupBase=0x90000
   1.211 +
   1.212 +	bs=/tmp/bs$$
   1.213 +
   1.214 +	# Get and patch boot sector
   1.215 +	# See http://hg.slitaz.org/wok/raw-file/13835bce7189/syslinux/stuff/iso2exe/bootloader.S
   1.216 +	trace "Read bootsector..."
   1.217 +	ddq if=$KERNEL bs=512 count=1 of=$bs
   1.218 +
   1.219 +	[ $(get 0x1FE $bs) -eq 43605 ] || error "Not bootable"
   1.220 +	
   1.221 +	uudecode <<EOT | ddq of=$bs conv=notrunc
   1.222 +begin-base64 644 -
   1.223 +v8adaACQF4n8FgcxwLk7APOqWx+g8X1AD6H6xXd4XwZXvQAAsQbzpRYfZGaP
   1.224 +R3jGRfg/l1hB6CQBMfYLNu8BdAzoggF0B+hgATwbdfS+AAKBTBAggMZEJZvo
   1.225 +ZwEx9gt3G3Q6x0cZP6PoWwGwIOgtASwYc/lO6DEBmDwIdAOIBK05dxt08OgV
   1.226 +ATwKdd+IfP4WB78AgIn+h3cbtQLzpFuJ5v9IEMdAFAiTgPMIdfO79AGxBaEV
   1.227 +AmaLH2ZLZtPrZkOJRBtmv4AAAABmKfuccwIB31BTVjHbaACAB+hwAF5bjMG0
   1.228 +hxYHzRVYBQABEEwfnXfPuQkCuxwCOE/+che0iM0VPQCwcgaIbB+Ib/+hGQLT
   1.229 +b+J1o5fNE+oAACCQWjjBdzRgzRP56HMAYVJQKMh3ArABOfhyAon4ULQCzRNa
   1.230 +WHLclQHRjukA1wDXKddadE2M6ZU4wXXUiMixATDOdcz+xYD9UHXFtQBgvtEB
   1.231 +U7sPAIAg8Ev+AIA4OXf16EwAW4n16CkAdRVSmM0TuAECzRNa0NQ6Zv516kVI
   1.232 +debrjbAxHAO0DrsHAM0QPA1088O/bARkxgWmuA0BZDoldArNFnT0mM0WjudH
   1.233 +wwN0DrAN6NL/rDwAf/jDSW5zZXJ0IGRpc2sgMDAxIGFuZCBFbnRlci4HDQAA
   1.234 +AAA=
   1.235 +====
   1.236 +EOT
   1.237 +	# Get setup
   1.238 +	setupsz=$(get $SetupSzOfs $bs 1)
   1.239 +	if [ $setupsz -eq 0 ]; then
   1.240 +		setupsz=4
   1.241 +		store 8 $SetupSzOfs $setupsz $bs "setup size $setupsz"
   1.242 +	fi
   1.243 +	trace "Read setup ($setupsz sectors) ..."
   1.244 +	ddq if=$KERNEL bs=512 skip=1 count=$setupsz >> $bs
   1.245 +
   1.246 +	Version=$(get 0x206 $bs)
   1.247 +	[ $(get $Magic $bs 4) != 1400005704 ] && Version=0
   1.248 +	feature=""
   1.249 +	while read prot kern info ; do
   1.250 +		[ $Version -lt $((0x$prot)) ] && continue
   1.251 +		feature="features $prot starting from kernel $kern "
   1.252 +	done <<EOT
   1.253 +200	1.3.73	kernel_version, bzImage, initrd, loadflags/type_of_loader
   1.254 +201	1.3.76	heap_end_ptr
   1.255 +202	2.4.0	new cmdline + relocatable setup
   1.256 +204	2.6.14	long syssize
   1.257 +206	2.6.22	cmdline maxsize $(get 0x238 $bs 4)
   1.258 +EOT
   1.259 +	trace "Protocol %X $feature" $Version
   1.260 +	
   1.261 +	# Old kernels need bootsector patches to disable rescent features
   1.262 +	while read minversion maxversion offset bytes rem; do
   1.263 +		[ $Version -gt $(( 0x$maxversion )) ] && continue
   1.264 +		[ $Version -lt $(( 0x$minversion )) ] && continue
   1.265 +		patch $offset $bytes $bs "$rem"
   1.266 +	done <<EOT
   1.267 +000 1FF 0B2	B8:00:01			force zImage (movw \$0x100, %ax)
   1.268 +000 1FF 0F9	EB				skip initrd code 
   1.269 +000 1FF 059	90:90:90			no kernel version
   1.270 +000 1FF 050	90:90:90:90:90			type_of_loader
   1.271 +000 200 055	90:90:90:90			heap_end_ptr
   1.272 +000 203 1F6	00:00 				syssize32
   1.273 +EOT
   1.274 +	[ -n "$CMDLINE" ] || patch 061 EB $bs "No cmdline"
   1.275 +	[ -n "$HIDE" ] && patch 058 90 90 90 $bs "Hide version"
   1.276 +	[ -n "$NOEDIT" ] && patch 075 B0:0D:90 $bs 'mov CR,%al ; nop'
   1.277 +	[ 1$TRACKS -ne 180 ] &&	store	8	0x15F		$TRACKS $bs TRACKS
   1.278 +	
   1.279 +	[ -n "$FLAGS" ] &&	store	16	$FlagsOfs	$FLAGS $bs FLAGS
   1.280 +	[ -n "$VIDEO" ] &&	store	16	$VideoModeOfs	$VIDEO $bs VIDEO
   1.281 +	[ -n "$RDEV" ] || case "$FORMAT" in
   1.282 +		1200)	RDEV=0x0208 ;;
   1.283 +		1440)	RDEV=0x021C ;;
   1.284 +		2880)	RDEV=0x0220 ;;
   1.285 +		*)	RDEV=0x0200 ;;
   1.286 +	esac
   1.287 +	while [ -L "$RDEV" ]; do RDEV="$(readlink "$RDEV")"; done
   1.288 +	[ -b "$RDEV" ] && RDEV=$(stat -c '0x%02t%02T' $RDEV 2> /dev/null)
   1.289 +	[ "$(echo $RDEV | tr '[0-9A-FXa-fx]' 0 | sed 's/0//g')" ] ||
   1.290 +	store 16 $RootDevOfs $RDEV $bs RDEV
   1.291 +
   1.292 +	[ $FORMAT -lt 720  ] && store 8 0x15F 40	 $bs	360K
   1.293 +	[ $FORMAT -lt 320  ] && store 8 0x158 237	 $bs	160K
   1.294 +	
   1.295 +	extra=0
   1.296 +	# Store cmdline after setup for kernels >= 0.99
   1.297 +	if [ -n "$CMDLINE" ]; then
   1.298 +		CmdlineOfs=$(stat -c '%s' $bs)
   1.299 +		store 16 0x22 $CmdlineOfs $bs "Cmdline @$CmdlineOfs '$CMDLINE'"
   1.300 +		[ $Version -ge 514 ] &&
   1.301 +		store 32 0x228 $(( $SetupBase + 0x8000 )) $bs "Cmdline32"
   1.302 +		echo -n "$CMDLINE" | ddq bs=512 count=1 conv=sync >> $bs
   1.303 +		extra=$(($extra+1))
   1.304 +		store 8 0x1F1  $(($setupsz+$extra))	 $bs	setup size
   1.305 +		[ $Version -ge 512 ] && [ -n "$QUIET" ] &&
   1.306 +		store 8 0x211 $(($(get 0x211 $bs 1) | 32)) $bs suppress early messages
   1.307 +	fi
   1.308 +
   1.309 +	# Info text after setup
   1.310 +	if [ -s "$INFOFILE" ]; then
   1.311 +		InfoOfs=$(stat -c '%s' $bs)
   1.312 +		sed -e ':a;N;$!ba;s/\r\n/\r/g;s/\n/\r/g' \
   1.313 +		    -e 's/'$(echo -e "\f/\xff/g;s/$/\xff\xff")'/' \
   1.314 +			< "$INFOFILE" > $bs.infotext
   1.315 +		ddq if=/dev/zero bs=512 count=1 >>$bs.infotext
   1.316 +		infolen=$(($(stat -c %s $bs.infotext)/512))
   1.317 +		ddq if=$bs.infotext count=$infolen bs=512 >> $bs
   1.318 +		extra=$(($extra+$infolen))
   1.319 +		rm -f $bs.infotext
   1.320 +		store 8 0x1F1  $(($setupsz+$extra))	$bs	setup size
   1.321 +		store 16 0x1EF  $InfoOfs $bs	update infoptr
   1.322 +	fi
   1.323 +
   1.324 +	syssz=$((($(stat -c %s $KERNEL)+15)/16-32*(1+$setupsz)))
   1.325 +	#syssz=$(get 0x1F4 $bs 4)
   1.326 +	sysszsect=$((($syssz+31)/32))
   1.327 +	store 16 $OldRamfsLenOfs 0 $bs clear oldramfs
   1.328 +	INITRD="${INITRD# }"
   1.329 +	INITRDPAD=4
   1.330 +	[ -n "$INITRD" ] &&
   1.331 +	if [ $Version -lt 512 ]; then
   1.332 +		# Compute initramfs location (protocol < 2.00)
   1.333 +		[ $syssz -gt 32768 ] && syssz=$(get 0x1F4 $bs 2)
   1.334 +		[ $syssz -eq 0 ] && syssz=$((0x7F00))
   1.335 +		sysszsect=$((($syssz+31)/32))
   1.336 +		INITRD="${INITRD%%,*}"
   1.337 +		initrdlen=$(stat -c %s "$INITRD")
   1.338 +		store 16 $OldRamfsLenOfs $(($initrdlen/1024)) $bs set oldramfs
   1.339 +		INITRDDISKALIGN=$((0x40000))
   1.340 +		[ $(($initrdlen+$INITRDDISKALIGN)) -gt $(($FORMAT*1024)) -o \
   1.341 +		  $(((512*$sysszsect) + $(stat -c %s $bs))) -gt $INITRDDISKALIGN -o \
   1.342 +		  -n "$ADRSRD" ] && INITRDDISKALIGN=$(($FORMAT*1024))
   1.343 +	else
   1.344 +INITRDRAMALIGN=0x1000
   1.345 +		# Compute initramfs size (protocol >= 2.00)
   1.346 +		initrdlen=0
   1.347 +		INITRDDISKALIGN=0
   1.348 +		for i in ${INITRD//,/ }; do
   1.349 +			[ -s "$i" ] || continue
   1.350 +			while [ -L "$i" ]; do i="$(readlink $i)"; done
   1.351 +			size=$(stat -c %s "$i")
   1.352 +			trace "initrd $i $size "
   1.353 +			initrdlen=$(((($initrdlen + $INITRDPAD - 1) & -$INITRDPAD) + $size))
   1.354 +			ADRSRD2=$(( (($MEM * 0x100000) - $initrdlen) & -$INITRDRAMALIGN ))
   1.355 +			store 32 $RamfsAdrOfs $(( ${ADRSRD:-$ADRSRD2} )) $bs initrd adrs
   1.356 +			store 32 $RamfsLenOfs $initrdlen $bs initrdlen
   1.357 +		done
   1.358 +	fi
   1.359 +
   1.360 +	[ -n "$NOSYSSIZEFIX" ] || store 32 0x1F4 $syssz $bs fix system size
   1.361 +
   1.362 +	# Output boot sector + setup + cmdline + info
   1.363 +	ddq if=$bs
   1.364 +
   1.365 +	# Output kernel code
   1.366 +	[ $INITRDDISKALIGN -ne 0 ] &&
   1.367 +	INITRDDISKALIGN=$(($INITRDDISKALIGN/512-$sysszsect-$extra-$setupsz-1))
   1.368 +	cat $KERNEL /dev/zero | ddq bs=512 skip=$(( $setupsz+1 )) \
   1.369 +		count=$(($sysszsect+$INITRDDISKALIGN)) conv=sync
   1.370 +
   1.371 +	# Output initramfs
   1.372 +	padding=$INITRDPAD
   1.373 +	for i in ${INITRD//,/ }; do
   1.374 +		[ -s "$i" ] || continue
   1.375 +		[ $padding -eq $INITRDPAD ] || ddq if=/dev/zero bs=1 count=$padding
   1.376 +		ddq if=$i
   1.377 +		trace "initrd $i ($(stat -c %s $i) bytes) padding $INITRDPAD"
   1.378 +		padding=$(( $INITRDPAD - ($(stat -c %s $i) % $INITRDPAD) ))
   1.379 +	done
   1.380 +
   1.381 +	# Cleanup
   1.382 +	rm -f $bs
   1.383 +}
   1.384 +
   1.385 +if [ "$FORMAT" == "0" ]; then # unsplitted
   1.386 +	floppyset > $PREFIX
   1.387 +	PAD=$(( 512 - ($(stat -c %s $PREFIX) % 512) ))
   1.388 +	[ $PAD -ne 512 ] && ddq if=/dev/zero bs=1 count=$PAD >> $PREFIX
   1.389 +	exit
   1.390 +fi
   1.391 +floppyset | split -b ${FORMAT}k /dev/stdin floppy$$
   1.392 +i=1
   1.393 +ls floppy$$* 2> /dev/null | while read file ; do
   1.394 +	output=$PREFIX$(printf "%03d" $i)
   1.395 +	cat $file /dev/zero | ddq bs=1k count=$FORMAT conv=sync of=$output
   1.396 +	echo $output
   1.397 +	rm -f $file
   1.398 +	i=$(( $i + 1 ))
   1.399 +done