blob: 4cb68dad8b41a9ae7936bba7de81cbdf29126ff8 [file] [log] [blame]
Bill Richardsonc09b94f2010-03-15 11:40:30 -07001#!/bin/bash
2
3# Copyright (c) 2009 The Chromium OS Authors. All rights reserved.
4# Use of this source code is governed by a BSD-style license that can be
5# found in the LICENSE file.
6
7# Script to build a bootable keyfob-based chromeos system image from within
8# a chromiumos setup. This assumes that all needed packages have been built into
9# the given target's root with binary packages turned on. This script will
10# build the Chrome OS image using only pre-built binary packages.
11
12# Load common constants. This should be the first executable line.
13# The path to common.sh should be relative to your script's location.
14. "$(dirname "$0")/common.sh"
15
16# Script must be run inside the chroot.
17assert_inside_chroot
18
19get_default_board
20
21# Flags.
22DEFINE_string board "$DEFAULT_BOARD" \
23 "The board to build an image for."
24DEFINE_string build_root "/build" \
25 "The root location for board sysroots."
26DEFINE_integer build_attempt 1 \
27 "The build attempt for this image build."
28DEFINE_string output_root "${DEFAULT_BUILD_ROOT}/images" \
29 "Directory in which to place image result directories (named by version)"
30DEFINE_boolean replace $FLAGS_FALSE \
31 "Overwrite existing output, if any."
32DEFINE_boolean withdev $FLAGS_TRUE \
33 "Include useful developer friendly utilities in the image."
34DEFINE_boolean installmask $FLAGS_TRUE \
35 "Use INSTALL_MASK to shrink the resulting image."
36DEFINE_integer jobs -1 \
37 "How many packages to build in parallel at maximum."
38DEFINE_boolean statefuldev $FLAGS_FALSE \
39 "Install development packages on stateful partition -- still experimental"
40
41# Parse command line.
42FLAGS "$@" || exit 1
43eval set -- "${FLAGS_ARGV}"
44
45# Only now can we die on error. shflags functions leak non-zero error codes,
46# so will die prematurely if 'set -e' is specified before now.
47set -e
48
49if [ -z "$FLAGS_board" ] ; then
50 error "--board is required."
51 exit 1
52fi
53
54# Sanity check: statefuldev cannot be true if withdev is false.
55if [ $FLAGS_statefuldev -eq $FLAGS_TRUE ] &&
56 [ $FLAGS_withdev -eq $FLAGS_FALSE ] ; then
57 echo "ERROR: statefuldev flag cannot be set to true without withdev"
58 exit 1
59fi
60
61# Determine build version.
62. "${SCRIPTS_DIR}/chromeos_version.sh"
63
64# Use canonical path since some tools (e.g. mount) do not like symlinks.
65# Append build attempt to output directory.
66IMAGE_SUBDIR="${CHROMEOS_VERSION_STRING}-a${FLAGS_build_attempt}"
67OUTPUT_DIR="${FLAGS_output_root}/${FLAGS_board}/${IMAGE_SUBDIR}"
68ROOT_FS_DIR="${OUTPUT_DIR}/rootfs"
69ROOT_FS_IMG="${OUTPUT_DIR}/rootfs.image"
70MBR_IMG="${OUTPUT_DIR}/mbr.image"
71OUTPUT_IMG="${OUTPUT_DIR}/usb.img"
72
73BOARD="${FLAGS_board}"
74BOARD_ROOT="${FLAGS_build_root}/${BOARD}"
75
76LOOP_DEV=
77
78# What cross-build are we targeting?
79. "${BOARD_ROOT}/etc/make.conf.board_setup"
80LIBC_VERSION=${LIBC_VERSION:-"2.10.1-r1"}
81
82# Figure out ARCH from the given toolchain.
83# TODO: Move to common.sh as a function after scripts are switched over.
84TC_ARCH=$(echo "$CHOST" | awk -F'-' '{ print $1 }')
85case "$TC_ARCH" in
86 arm*)
87 ARCH="arm"
88 ;;
89 *86)
90 ARCH="x86"
91 ;;
92 *)
93 error "Unable to determine ARCH from toolchain: $CHOST"
94 exit 1
95esac
96
97# Hack to fix bug where x86_64 CHOST line gets incorrectly added.
98# ToDo(msb): remove this hack.
99PACKAGES_FILE="${BOARD_ROOT}/packages/Packages"
100sudo sed -e "s/CHOST: x86_64-pc-linux-gnu//" -i "${PACKAGES_FILE}"
101
102# Handle existing directory.
103if [[ -e "$OUTPUT_DIR" ]]; then
104 if [[ $FLAGS_replace -eq $FLAGS_TRUE ]]; then
105 sudo rm -rf "$OUTPUT_DIR"
106 else
107 echo "Directory $OUTPUT_DIR already exists."
108 echo "Use --build_attempt option to specify an unused attempt."
109 echo "Or use --replace if you want to overwrite this directory."
110 exit 1
111 fi
112fi
113
114# Create the output directory.
115mkdir -p "$OUTPUT_DIR"
116
117cleanup_rootfs_loop() {
118 sudo umount "$LOOP_DEV"
119 sleep 1 # in case $LOOP_DEV is in use (TODO: Try umount -l?).
120 sudo losetup -d "$LOOP_DEV"
121}
122
123cleanup_stateful_fs_loop() {
124 sudo umount "$STATEFUL_LOOP_DEV"
125 sleep 1 # follows from cleanup_root_fs_loop.
126 sudo losetup -d "$STATEFUL_LOOP_DEV"
127}
128
129cleanup() {
130 # Disable die on error.
131 set +e
132
133 # Unmount stateful partition from usr/local if bound.
134 if [ -s "$ROOT_FS_DIR/usr/local/bin" ] ; then
135 sudo umount $ROOT_FS_DIR/usr/local
136 fi
137
138 if [[ -n "$STATEFUL_LOOP_DEV" ]]; then
139 cleanup_stateful_fs_loop
140 fi
141
142 if [[ -n "$LOOP_DEV" ]]; then
143 cleanup_rootfs_loop
144 fi
145
146 # Turn die on error back on.
147 set -e
148}
149
150trap cleanup EXIT
151
152mkdir -p "$ROOT_FS_DIR"
153
154# Create and format the root file system.
155
156# Check for loop device before creating image.
157LOOP_DEV=$(sudo losetup -f)
158if [ -z "$LOOP_DEV" ] ; then
159 echo "No free loop device. Free up a loop device or reboot. exiting. "
160 exit 1
161fi
162
163# Create root file system disk image to fit on a 1GB memory stick.
164# 1 GB in hard-drive-manufacturer-speak is 10^9, not 2^30. 950MB < 10^9 bytes.
165ROOT_SIZE_BYTES=$((1024 * 1024 * 640))
166dd if=/dev/zero of="$ROOT_FS_IMG" bs=1 count=1 seek=$((ROOT_SIZE_BYTES - 1))
167sudo losetup "$LOOP_DEV" "$ROOT_FS_IMG"
168sudo mkfs.ext3 "$LOOP_DEV"
169
170# Tune and mount rootfs.
171UUID=$(uuidgen)
172DISK_LABEL="C-KEYFOB"
173sudo tune2fs -L "$DISK_LABEL" -U "$UUID" -c 0 -i 0 "$LOOP_DEV"
174sudo mount "$LOOP_DEV" "$ROOT_FS_DIR"
175
176# Create stateful partition of the same size as the rootfs.
177STATEFUL_IMG="$OUTPUT_DIR/stateful_partition.image"
178STATEFUL_DIR="$OUTPUT_DIR/stateful_partition"
179STATEFUL_LOOP_DEV=$(sudo losetup -f)
180if [ -z "$STATEFUL_LOOP_DEV" ] ; then
181 echo "No free loop device. Free up a loop device or reboot. exiting. "
182 exit 1
183fi
184dd if=/dev/zero of="$STATEFUL_IMG" bs=1 count=1 seek=$((ROOT_SIZE_BYTES - 1))
185sudo losetup "$STATEFUL_LOOP_DEV" "$STATEFUL_IMG"
186sudo mkfs.ext3 "$STATEFUL_LOOP_DEV"
187sudo tune2fs -L "C-STATE" -U "$UUID" -c 0 -i 0 \
188 "$STATEFUL_LOOP_DEV"
189
190# Mount the stateful partition.
191mkdir -p "$STATEFUL_DIR"
192sudo mount "$STATEFUL_LOOP_DEV" "$STATEFUL_DIR"
193
194# Turn root file system into bootable image.
195if [[ "$ARCH" = "x86" ]]; then
196 # Setup extlinux configuration.
197 # TODO: For some reason the /dev/disk/by-uuid is not being generated by udev
198 # in the initramfs. When we figure that out, switch to root=UUID=$UUID.
199 sudo mkdir -p "$ROOT_FS_DIR"/boot
200 # TODO(adlr): use initramfs for booting.
201 cat <<EOF | sudo dd of="$ROOT_FS_DIR"/boot/extlinux.conf
202DEFAULT chromeos-usb
203PROMPT 0
204TIMEOUT 0
205
206label chromeos-usb
207 menu label chromeos-usb
208 kernel vmlinuz
209 append quiet console=tty2 init=/sbin/init boot=local rootwait root=/dev/sdb3 ro noresume noswap i915.modeset=1 loglevel=1
210
211label chromeos-hd
212 menu label chromeos-hd
213 kernel vmlinuz
214 append quiet console=tty2 init=/sbin/init boot=local rootwait root=HDROOT ro noresume noswap i915.modeset=1 loglevel=1
215EOF
216
217 # Make partition bootable and label it.
218 sudo extlinux -z --install "${ROOT_FS_DIR}/boot"
219fi
220
221# -- Install packages into the root file system --
222
223# We need to install libc manually from the cross toolchain.
224# TODO: Improve this? We only want libc and not the whole toolchain.
225PKGDIR="/var/lib/portage/pkgs/cross/"
226sudo tar jxvpf \
227 "${PKGDIR}/${CHOST}/cross-${CHOST}"/glibc-${LIBC_VERSION}.tbz2 \
228 -C "$ROOT_FS_DIR" --strip-components=3 \
229 --exclude=usr/include --exclude=sys-include --exclude=*.a --exclude=*.o
230
231# We need to install libstdc++ manually from the cross toolchain.
232# TODO: Figure out a better way of doing this?
233sudo cp -a "${BOARD_ROOT}"/lib/libgcc_s.so* "${ROOT_FS_DIR}/lib"
234sudo cp -a "${BOARD_ROOT}"/usr/lib/libstdc++.so* "${ROOT_FS_DIR}/usr/lib"
235
236INSTALL_MASK=""
237if [[ $FLAGS_installmask -eq $FLAGS_TRUE ]] ; then
238 INSTALL_MASK="/usr/include/ /usr/man /usr/share/man /usr/share/doc /usr/share/gtk-doc /usr/share/gtk-2.0 /usr/lib/gtk-2.0/include /usr/share/info /usr/share/aclocal /usr/lib/gcc /usr/lib/pkgconfig /usr/share/pkgconfig /usr/share/gettext /usr/share/readline /etc/runlevels /usr/share/openrc /lib/rc *.a *.la"
239fi
240
241if [[ $FLAGS_jobs -ne -1 ]]; then
242 EMERGE_JOBS="--jobs=$FLAGS_jobs"
243fi
244
245if [ $FLAGS_statefuldev -eq $FLAGS_TRUE ] ; then
246 # Creating stateful partition.
247 echo "Setting up symlinks for stateful partition install"
248 DEV_IMAGE_ROOT="$STATEFUL_DIR/dev_image"
249 sudo mkdir -p "$DEV_IMAGE_ROOT/usr"
250
251 # Setup symlinks in stateful partition.
252 for path in bin include lib libexec sbin share; do
253 sudo mkdir "$DEV_IMAGE_ROOT/$path"
254 sudo ln -s "$DEV_IMAGE_ROOT/$path" "$DEV_IMAGE_ROOT/usr/$path"
255 done
256
257 # Setup symlinks that don't conform to above model.
258 sudo ln -s "$DEV_IMAGE_ROOT/lib" "$DEV_IMAGE_ROOT/usr/lib64"
259 sudo ln -s "$DEV_IMAGE_ROOT" "$DEV_IMAGE_ROOT/usr/local"
260
261 # Bind to rootfs.
262 echo "Binding stateful partition to rootfs's /usr/local"
263 sudo mkdir -p "$ROOT_FS_DIR/usr/local"
264 sudo mount -n --bind "$DEV_IMAGE_ROOT" "$ROOT_FS_DIR/usr/local"
265fi
266
267# We "emerge --root=$ROOT_FS_DIR --root-deps=rdeps --usepkgonly" all of the
268# runtime packages for chrome os. This builds up a chrome os image from binary
269# packages with runtime dependencies only. We use INSTALL_MASK to trim the
270# image size as much as possible.
271sudo INSTALL_MASK="$INSTALL_MASK" emerge-${BOARD} \
272 --root="$ROOT_FS_DIR" --root-deps=rdeps \
273 --usepkgonly chromeos $EMERGE_JOBS
274if [[ $FLAGS_withdev -eq $FLAGS_TRUE ]] ; then
275 # Determine the root dir for development packages
276 ROOT_DIR="$ROOT_FS_DIR"
277 [ $FLAGS_statefuldev -eq $FLAGS_TRUE ] && ROOT_DIR="$ROOT_FS_DIR/usr/local"
278
279 sudo INSTALL_MASK="$INSTALL_MASK" emerge-${BOARD} \
280 --root="$ROOT_DIR" --root-deps=rdeps \
281 --usepkgonly chromeos-dev $EMERGE_JOBS
282
283 if [ $FLAGS_statefuldev -eq $FLAGS_TRUE ] ; then
284 # Fix symlinks so they work on live system.
285 for path in bin include lib libexec sbin share; do
286 sudo unlink $DEV_IMAGE_ROOT/usr/$path
287 sudo ln -s /usr/local/$path $DEV_IMAGE_ROOT/usr/$path
288 done
289
290 # Fix exceptions.
291 sudo unlink "$DEV_IMAGE_ROOT/usr/lib64"
292 sudo unlink "$DEV_IMAGE_ROOT/usr/local"
293
294 sudo ln -s "/usr/local/lib" "$DEV_IMAGE_ROOT/usr/lib64"
295 sudo ln -s "/usr/local" "$DEV_IMAGE_ROOT/usr/local"
296 fi
297
298 # The ldd tool is a useful shell script but lives in glibc; just copy it.
299 sudo cp -a "$(which ldd)" "${ROOT_FS_DIR}/usr/bin"
300fi
301
302# Perform any customizations on the root file system that are needed.
303WITH_DEV=""
304if [[ $FLAGS_withdev -eq $FLAGS_TRUE ]]; then
305 WITH_DEV="--withdev"
306fi
307
308#TODO(sosa@chromium.org) - Does it make sense to leave /usr/local bound here?
309"${SCRIPTS_DIR}/customize_rootfs" \
310 --root="$ROOT_FS_DIR" \
311 --target="$ARCH" \
312 --board="$BOARD" \
313 $WITH_DEV
314
315# Check that the image has been correctly created.
316"${SCRIPTS_DIR}/test_image" \
317 --root="$ROOT_FS_DIR" \
318 --target="$ARCH"
319
320# Set dev_mode flag and update library cache.
321if [ $FLAGS_statefuldev -eq $FLAGS_TRUE ] ; then
322 sudo touch "$ROOT_FS_DIR/root/.dev_mode"
323 sudo /sbin/ldconfig -r "$ROOT_FS_DIR"
324fi
325
326# Only bound if installing dev to stateful.
327if [ $FLAGS_statefuldev -eq $FLAGS_TRUE ] ; then
328 sudo umount "$ROOT_FS_DIR/usr/local"
329fi
330
331# Cleanup loop devices.
332cleanup_stateful_fs_loop
333cleanup_rootfs_loop
334
335# Create a master boot record.
336# Start with the syslinux master boot record. We need to zero-pad to
337# fill out a 512-byte sector size.
338SYSLINUX_MBR="/usr/share/syslinux/mbr.bin"
339dd if="$SYSLINUX_MBR" of="$MBR_IMG" bs=512 count=1 conv=sync
340# Create a partition table in the MBR.
341NUM_SECTORS=$((`stat --format="%s" "$ROOT_FS_IMG"` / 512))
342KERNEL_SECTORS=8192
343sudo sfdisk -H64 -S32 -uS -f "$MBR_IMG" <<EOF
344,$NUM_SECTORS,L,-,
345,$NUM_SECTORS,S,-,
346,$NUM_SECTORS,L,*,
347,$KERNEL_SECTORS,L,-,
348;
349EOF
350if [[ "$ARCH" = "arm" ]]; then
351 # Write u-boot script into MBR code block that boots 4th partition kernel.
352 KERNEL_OFFSET=`printf "%08x" $(((3 * $NUM_SECTORS) + 1))`
353 KERNEL_SECS_HEX=`printf "%08x" $KERNEL_SECTORS`
354 MBR_SCRIPT="${OUTPUT_DIR}/mbr_script"
355 echo -e "echo\necho ---- ChromeOS Boot ----\necho\n" \
356 "mmc read 1 C0008000 0x$KERNEL_OFFSET 0x$KERNEL_SECS_HEX\n" \
357 "bootm C0008000" > ${MBR_SCRIPT}
358 MKIMAGE="${BOARD_ROOT}/u-boot/mkimage"
359 if [[ -f "$MKIMAGE".gz ]]; then
360 sudo gunzip "$MKIMAGE".gz
361 fi
362 if [[ -x "$MKIMAGE" ]]; then
363 MBR_SCRIPT_UIMG="${MBR_SCRIPT}.uimg"
364 "$MKIMAGE" -A "${ARCH}" -O linux -T script -a 0 -e 0 -n "COS boot" \
365 -d ${MBR_SCRIPT} ${MBR_SCRIPT_UIMG}
366 dd bs=1 count=`stat --printf="%s" ${MBR_SCRIPT_UIMG}` \
367 if="$MBR_SCRIPT_UIMG" of="$MBR_IMG"
368 hexdump -v -C "$MBR_IMG"
369 else
370 echo "Error: u-boot mkimage not found or not executable."
371 fi
372fi
373
374OUTSIDE_OUTPUT_DIR="../build/images/${FLAGS_board}/${IMAGE_SUBDIR}"
375echo "Done. Image created in ${OUTPUT_DIR}"
376echo "To copy to USB keyfob, OUTSIDE the chroot, do something like:"
377echo " ./image_to_usb.sh --from=${OUTSIDE_OUTPUT_DIR} --to=/dev/sdb"
378echo "To convert to VMWare image, OUTSIDE the chroot, do something like:"
379echo " ./image_to_vmware.sh --from=${OUTSIDE_OUTPUT_DIR}"
380echo "from the scripts directory where you entered the chroot."
381
382trap - EXIT