| #!/bin/sh |
| # |
| # You can run this script in case you boot with rdinit=/bin/sh and want to |
| # quickly mount the basic kernel filesystems. It mounts them, but doesn't |
| # switch_root. |
| # |
| |
| . /helpers.sh |
| |
| find_sata_blkdev() |
| { |
| local dev_node result= |
| |
| for dev_node in /dev/sd?; do |
| [ -b "$dev_node" ] || continue |
| |
| local blkdev=${dev_node#/dev/} |
| local dev_path=$(realpath "/sys/block/$blkdev/device") |
| if [ "${dev_path#*usb}" = "$dev_path" ]; then |
| result="$dev_node" |
| break |
| fi |
| done |
| |
| [ -n "$result" ] && echo "$result" |
| } |
| |
| # Finds the *last* /dev/mtd* device with the given realname |
| find_mtd() |
| { |
| local result= |
| while IFS=" :" read dev size erasesize name; do |
| name=${name#\"} |
| name=${name%\"} |
| if [ "$name" = "$1" ]; then |
| result=${dev#mtd} |
| # fall through in case there's a subsequent device with same name |
| fi |
| done </proc/mtd |
| [ -n "$result" ] && echo "$result" |
| } |
| |
| find_gpt() |
| { |
| local result= |
| sgdisk -p "$1" >/tmp/gptpart 2>/dev/null |
| while read number start end size unit code name; do |
| if [ "$name" = "$2" ]; then |
| result=$number |
| fi |
| done </tmp/gptpart |
| rm -f /tmp/gptpart |
| [ -n "$result" ] && echo "$result" |
| } |
| |
| find_mmc() |
| { |
| find_gpt /dev/mmcblk0 "$1" |
| } |
| |
| find_hdd() |
| { |
| find_gpt "$1" "$2" |
| } |
| |
| read_cmdline |
| |
| # NOTE on ubi.mtd= vs. root= |
| # |
| # Previously, we relied on the installer+bootloader to set ubi.mtd=xxx, and |
| # then root=mtdblock:rootfs. The problem with that is it depends on the |
| # installer/bootloader understanding about UBI devices, which means the |
| # installer for a *previous* version needs to understand the format of kernel |
| # parameters for the *next* (newly-installed) version. That can make future |
| # upgrades more complicated. |
| # |
| # So what we do now is provide root=xxx, where xxx is the name of the mtd |
| # partition where the rootfs is installed (either rootfs0 or rootfs1). It's |
| # the responsibility of the installed image, ie. this script, to know whether |
| # the stuff in rootfs0/rootfs1 is UBI or not. |
| # |
| # However, for backward compatibility, we still need to support kernel options |
| # of the form |
| # ubi.mtd=rootfs0 root=mtdblock:rootfs |
| # which we treat as the new-style |
| # root=rootfs0 |
| |
| # ubi.mtd= only overrides root= if root= is old-style. |
| if [ -n "$UBI_MTD" ]; then |
| if [ -z "$ROOTDEV" -o "${ROOTDEV%%:*}" = "mtdblock" ]; then |
| ROOTDEV=$UBI_MTD |
| fi |
| fi |
| |
| |
| replacefirst() { |
| local left right |
| # same as sed -e 's/$2/$3/' $1 |
| right=${1#*$2} |
| left=${1%%$2*} |
| echo "$left$3$right" |
| } |
| |
| |
| # Returns true if the string $1 starts with the string $2. |
| startswith() { |
| [ "${1#"$2"}" != "$1" ] |
| } |
| |
| oldargstonewargs() { |
| local root_dev=$(replacefirst "$4" "payload=" "") |
| local hash_dev=$(replacefirst "$5" "hashtree=" "") |
| local hashstart=$(replacefirst "$6" "hashstart=" "") |
| local alg=$(replacefirst "$7" "alg=" "") |
| local root_digest=$(replacefirst "$8" "root_hexdigest=" "") |
| local salt=$(replacefirst "$9" "salt=" "") |
| |
| local data_blockcount=$(($2 / 8)) |
| local hash_blockstart=$(($hashstart / 8)) |
| |
| echo "$1 $2 $3 0 $root_dev $hash_dev 4096 4096 $data_blockcount $hash_blockstart $alg $root_digest $salt" |
| } |
| |
| # From the verity command line, gets the hash start in Bytes! |
| get_hashstart() { |
| local hashstart=${6#hashstart=} |
| hashstart=$((hashstart * 512)) |
| echo "$hashstart" |
| } |
| |
| # Echo back the verity args, but with hashstart=0 for the loopback device. |
| clear_hashstart() { |
| echo "$1 $2 $3 $4 $5 hashstart=0 $7 $8 $9" |
| } |
| |
| # host:path format in root= means we should do an NFS root |
| if [ "${ROOTDEV#*:/}" != "$ROOTDEV" ]; then |
| NFSROOT=$ROOTDEV |
| fi |
| |
| if [ -n "$NFSROOT" ]; then |
| log "Mounting nfs: '$NFSROOT'" |
| wait # wait for any pending insmods launched by /init |
| NFSADDR=${NFSROOT%:*} |
| NFSOPTS="nolock,proto=tcp,mountproto=tcp,vers=3,mountvers=3,addr=$NFSADDR" |
| if [ -n "$IP" ]; then |
| # Can't necessarily count on the kernel to do this, in case our |
| # ethernet driver is a kernel module instead of built-in. |
| log " IP initialization: '$IP'" |
| echo "$IP" | { |
| IFS=: read client_ip server_ip gw_ip netmask hostname device \ |
| autoconf dns0_ip dns1_ip junk |
| [ -z "$device" ] && device=eth0 |
| if [ "$ip" != "auto" ]; then |
| ifconfig "$device" "$client_ip" netmask 255.255.255.255 |
| fi |
| if [ -n "$gw_ip" ]; then |
| route add -net "$gw_ip" netmask 255.255.255.255 dev "$device" |
| fi |
| if [ "$NFSADDR" != "$gw_ip" ]; then |
| if [ -n "$gw_ip" ]; then |
| route add -net "$NFSADDR" netmask 255.255.255.255 gw "$gw_ip" \ |
| dev "$device" |
| else |
| route add -net "$NFSADDR" netmask 255.255.255.255 dev "$device" |
| fi |
| fi |
| } |
| log " IP initialized." |
| fi |
| # Some PHYs take >1 second to get a link. Poll the connection with nc |
| # (with a nice short timeout so we cause transmit retries) until it's open, |
| # then move on to the real NFS mount. |
| for s in $(seq 10); do |
| nc -w1 "$NFSADDR" 2049 </dev/null && break |
| done && |
| mount -o $NFSOPTS -t nfs "$NFSROOT" /rootfs || |
| die "NFS mount failed" |
| log "Mounted nfsroot='$NFSROOT'" |
| elif [ -e /rootfs.img ]; then |
| cat /rootfs.img >/dev/ram0 |
| mount -t squashfs -o ro /dev/ram0 /rootfs || |
| die "root='/dev/ram0' mount failed" |
| log "Mounted root='/dev/ram0'" |
| elif [ -n "$ROOTDEV" ]; then |
| if startswith "$ROOTDEV" /dev; then |
| mount -o ro -t squashfs "$ROOTDEV" /rootfs || |
| die "root='$ROOTDEV' mount failed" |
| log "Mounted root='$ROOTDEV'" |
| elif startswith "$ROOTDEV" 9p:; then |
| fsdev="${ROOTDEV#9p:}" |
| mount -t 9p -o trans=virtio,version=9p2000.L "$fsdev" /rootfs || |
| die "root='$fsdev' (9p) mount failed" |
| log "Mounted root='$fsdev' (9p)" |
| else |
| for s in 1 1 0; do |
| KERNELNAME=$(replacefirst $ROOTDEV rootfs kernel) |
| mtd=/dev/mtd$(find_mtd "$ROOTDEV") |
| mmcraw=/dev/mmcblk0p$(find_mmc "$ROOTDEV") |
| sata_blkdev=$(find_sata_blkdev) |
| hdd=${sata_blkdev}$(find_hdd "$sata_blkdev" "$ROOTDEV") |
| IMAGENAME=$(replacefirst $ROOTDEV rootfs image) |
| mmcvfat=/dev/mmcblk0p$(find_mmc "$IMAGENAME") |
| [ -e "$mtd" -o -e "$mmcraw" -o -e "$hdd" -o -e "$mmcvfat" \ |
| -o -e $ROOTDEV ] && break |
| log "No rootfs found, sleeping" |
| # eMMC is sometimes slow to appear; try again. |
| sleep $s |
| false |
| done |
| if [ -e "$mtd" ]; then |
| log "Found root='$ROOTDEV': $mtd" |
| # In this build, the rootdev mtd is always an UBI device. |
| ubidetach -p "$mtd" # Just in case kernel automounted it somewhere |
| ubiattach -p "${mtd}ro" |
| # In this build, the newly-found mtd is always named 'rootfs' |
| ubi_mtd=$(find_mtd "rootfs") |
| [ -n "$ubi_mtd" ] || (cat /proc/mtd; die "no mtd named 'rootfs'") |
| ROOTDEV=/dev/mtdblock$ubi_mtd |
| VERITY_CHAR_DEV=/dev/mtd$(find_mtd "$KERNELNAME") |
| elif [ -e "$mmcraw" ]; then |
| log "Found root='$ROOTDEV': $mmcraw" |
| ROOTDEV=$mmcraw |
| VERITYDEV=/dev/mmcblk0p$(find_mmc "$KERNELNAME") |
| elif [ -e "$hdd" ]; then |
| log "Found root='$ROOTDEV': $hdd" |
| ROOTDEV=$hdd |
| # On SpaceCast the kernel still lives in the NOR flash |
| VERITY_CHAR_DEV=/dev/mtd$(find_mtd "$KERNELNAME") |
| elif [ -e "$mmcvfat" ]; then |
| # TODO(jnewlin): Remove this after verifying vfat is not used. |
| log "Found root='$ROOTDEV': $mmcvfat" |
| mount -t vfat -o ro,nodev,noexec,nosuid "$mmcvfat" /vfat |
| VERITYDEV=/dev/loop0 |
| ROOTDEV=/dev/loop1 |
| losetup $VERITYDEV /vfat/vmlinuz.img |
| losetup $ROOTDEV /vfat/rootfs.img |
| else |
| cat /proc/mtd |
| log "root='$ROOTDEV' is not an mtd device." |
| fi |
| if [ -n "$VERITY_CHAR_DEV" ]; then |
| # dump the verity header, and read the verity settings, they are stored |
| # in the first 4096 bytes of the image. |
| mount -t tmpfs none /veritytmp |
| nanddump -x -f /veritytmp/verity.args -l 4096 -s 0 "$VERITY_CHAR_DEV" |
| args=$(readverity /veritytmp/verity.args) |
| hash_size=$(readverity -s /veritytmp/verity.args) |
| rm /veritytmp/verity.args |
| hash_offset=$(get_hashstart $args) |
| args=$(clear_hashstart $args) |
| nanddump -x -f /veritytmp/hash.dat -l "$hash_size" -s "$hash_offset" "$VERITY_CHAR_DEV" |
| VERITYDEV=/dev/loop3 |
| losetup $VERITYDEV /veritytmp/hash.dat |
| else |
| args=$(readverity $VERITYDEV) |
| fi |
| args=$(replacefirst "$args" ROOT_DEV "$ROOTDEV") |
| args=$(replacefirst "$args" HASH_DEV "$VERITYDEV") |
| altargs=$(oldargstonewargs $args) |
| echo "dmsetup: $args" |
| echo "dmsetup: $altargs" |
| if ! dmsetup create vroot -r --table "$args" && ! dmsetup create vroot -r --table "$altargs"; then |
| veritynote= |
| read x y armplatform platform junk </proc/cpuinfo |
| if [ "$platform" = "BCM7425B0" ]; then |
| log 'dmsetup failed: platform is unsigned device, continuing anyway.' |
| SQUASHFS=$ROOTDEV |
| else |
| die "dmsetup failed" |
| fi |
| else |
| SQUASHFS="/dev/mapper/vroot" |
| veritynote="(verity)" |
| fi |
| |
| mount -o ro -t squashfs "$SQUASHFS" /rootfs || |
| die "root='$ROOTDEV' mount failed $veritynote" |
| log "Mounted root='$ROOTDEV' $veritynote" |
| fi |
| else |
| die "No root= or nfsroot= provided." |
| fi |
| |
| mount -t tmpfs none /rootfs/tmp |
| |
| #TODO(apenwarr): network setup code depends on this file, but it shouldn't. |
| # It should just always refuse to touch the network if the network was already |
| # set up. |
| [ -n "$NFSROOT" ] && echo NFS >/rootfs/tmp/NFS |
| |
| true |