Debian on ASUS C101PA Chromebook =============================== Overview -------- This document contains instructions to install the Debian Linux distribution on the internal eMMC of the ASUS C101PA Chromebook. It concerns the stable distribution at the time of this writing (9.0 stretch), but that can easily be changed to testing or unstable. It is assumed the device still runs the ChromeOS operating system. The procedure erases all data on the internal eMMC, so save any relevant files before starting. The installation steps are : - Enable boot from external device - Prepare rootfs image - Build Linux kernel packages - Create bootable external device as rescue (DESTRUCTIVE) - Deploy to internal eMMC (DESTRUCTIVE) Hardware required : - x86 machine running Debian - Bootable device (USB drive or microSD card) - Networking (wireless / USB-Ethernet adapter) This procedure is destructive and intended for advanced users. Be very careful not to destroy your own data by mistake. Make back-up copies if unsure. The kernel configuration in this repository was crafted to be close to the original Debian kernel configuration, without modules which may not ever be useful on the Chromebook, and with more specific options targetting a low latency laptop with energy saving needs. It also works around some defects in the ChromeOS branch, such as modules that must be built in the kernel instead of as loadable modules. The procedure is intended to get a system as close to Debian as possible. In particular, with actual kernel packages and a careful kernel configuration, it's very easy to get additional features such as an encrypted root file system, because Debian handles the generation of the initramfs image. The kernel/deploy.sh script, installed at /root/kernel/deploy.sh and linked at /etc/kernel/postinst.d/zz-deploy, is run when updating the kernel or adding packages that trigger a regeneration of the initramfs. It's the responsibility of the user to understand what this script does, and adjust it, as well as the files it uses, as necessary. Enable boot from external device -------------------------------- First, enable Developer Mode : while the laptop is off, hold down the ESC and Refresh (F3) keys and start it. From now on, the firmware presents a recovery screen saying "OS verification is OFF". Press Ctrl-D to boot from the internal eMMC or Ctrl-U to boot from an external USB drive / microSD card. Until the external "rescue" bootable system is available, you can only boot ChromeOS from the internal eMMC. Once in ChromeOS : - Press Ctrl-Alt-T to open the crosh shell - Type the "shell" command to start a real Unix shell - Type "sudo -s" to gain root privileges - Type "crossystem dev_boot_usb=1 dev_boot_signed_only=0" to enable booting from an external device Prepare rootfs image -------------------- There are several ways to do this, notably with the debootstrap tool, but this procedure uses a fully emulated Qemu virtual machine to achieve it. The idea is to use the arm64 Debian installer in the virtual machine, and extract the resulting root file system from it. Note that, because it's currently required to completely emulate the arm64 machine, this method is quite long. Reserve around an hour for it, depending on your host hardware and your Internet connection. First, prepare a disk image. This image can be the same for both the rescue device as well as the internal eMMC. It is suggested to use a small disk size, and resize later to the maximum size permitted by the device. Run the following command : qemu-img create disk.img 4G The procedure also requires the Debian installer, which you can find on any Debian mirror, such as http://ftp.fr.debian.org/. The two files required are the kernel image, named "linux", and the initrd containing the installer, named "initrd.gz". Those files are in the netboot directory of the installer of the version you want to install. Here is an example URL for the stretch stable release : http://ftp.fr.debian.org/debian/dists/stretch/main/installer-arm64/current/images/netboot/debian-installer/arm64 Move those files next to the disk image, then run the following command to start the installation in the virtual machine : qemu-system-aarch64 \ -nographic \ -M virt \ -m 1G \ -cpu cortex-a53 \ -kernel linux \ -initrd initrd.gz \ -drive media=disk,if=virtio,index=0,format=raw,file=disk.img These installation instructions assume the default single-partition guided format, with no swap and no /boot partition. Once installed, the root file system can be extracted by mounting the disk image as a loop device, and simply copying the relevant partition into a new file. As root, on the host : # Allocate a loop device, attach the disk image to it, and probe partitions losetup -f -P disk.img # Identify the root file system device losetup -a lsblk # This assumes the allocated device is /dev/loop0 and the root file system # is in the second partition dd if=/dev/loop0p2 of=rootfs.img bs=1M # Once done, detach the disk image. losetup -d /dev/loop0 Build Linux kernel packages --------------------------- Before starting this procedure, make sure the git submodule(s) are up to date. The kernel packages are pretty easy to build, thanks to the deb-pkg make target built in the Linux build system. While it's possible to build the kernel on the Chromebook, this procedure cross-compiles them from the x86 host system, mostly for better performance. As root, on the host : # Install build dependencies apt build-dep linux # Install the AArch64 GCC toolchain # XXX This seems to be in conflict with gcc-multilib apt install gcc-aarch64-linux-gnu As a non-privileged user, on the host : # Prepare the kernel build cp linux_config chromeos-kernel/.config cd chromeos-kernel make ARCH=arm64 oldconfig # Build the kernel # Replace -j6 with the number of processors on your machine make ARCH=arm64 -j6 deb-pkg The two packages required on the Chromebook are : - linux-image - linux-firmware-image Create bootable external device as rescue ----------------------------------------- For this, you need a USB drive or a microSD card. This procedure assumes a USB drive at /dev/sdc on the host, and /dev/sda on the target. Locating the USB drive on both the x86 host and the Chromebook is the responsibility of the user. Use a combination of lsblk and dmesg to make sure you're accessing the correct device, hereafter called the "rescue device". The idea is to create a ChromeOS-compatible GPT partition table on the rescue device, with two entries (one for the "kernel" and another for the root file system), and then write the kernel and the root file system into their respective partitions. Creating the partitions and writing the root file system is done from the host system, whereas installing the kernel and copying it to the kernel partition is done from the target system, either the original ChromeOS on the Chromebook (easier) or a QEMU virtual machine. Note that the Linux framebuffer console apparently suffers from a small bug that freezes it after input inactivity. As root, on the host : # Install partitioning tools apt install parted cgpt # Create the partition table parted --script /dev/sdc mklabel gpt # You may check your partition table at any time with cgpt show /dev/sdc # Create the kernel partition # # This line names the partition "kernel" (-l is label) starting at # sector 64 with a size of 131072 sectors (1 sector == 512 bytes, # making the partition size 64M). cgpt add -t kernel -l kernel -b 64 -s 131072 /dev/sdc # Set priority and "successful" counters for the kernel partition # This is required by the boot loader. Refer to the documentation of # cgpt for more details. cgpt add -i 1 -P 10 -S 1 /dev/sdc # Create the root file system partition # # After creating the kernel partition, print the partition table, and look # for a line similar to : # 15261663 32 Sec GPT table # The first value on this line is the last sector. To maximize your root # file system partition size, subtract its first sector from this size. # The first sector is the last sector of the kernel partition, so in # this example, it's 64 + 131072, so 131136, and 15261663 - 131136 is # 15130527. cgpt add -t data -l / -b 131136 -s 15130527 /dev/sdc # Write the root file system into the root file system partition dd if=rootfs.img of=/dev/sdc2 bs=1M ---- These steps are similar for the deployment on the ---- ---- internal eMMC, with /dev/mmcblk0p1 as the kernel ---- ---- partition and /dev/mmcblk0p2 as the root file system ---- ---- partition. ---- # Extend the root file system to the maximum available size e2fsck -fp /dev/sdc2 resize2fs /dev/sdc2 # Mount the root file system mount /dev/sdc2 /mnt # Copy the required kernel packages to the root file system cp /mnt/root # Copy the files used to generated a signed kernel image from this repository # to the rescue device cp -r kernel /mnt/root # Unmount the root file system umount /mnt Installing the kernel is done on the target device, either the original ChromeOS system, or a QEMU virtual machine. Refer to enabling developer mode for a procedure to gain root privileges on ChromeOS. With QEMU, make the rescue device available to the virtual machine using a line such as -drive media=disk,if=virtio,index=1,format=raw,file=/dev/sdc to the qemu-system-aarch64 command line used when preparing the root file system. From the installer, when asked for the root password, select "Go back", then select "Detect disks", and then start a root shell in the environment of the installer. # Once you have a root shell on the target system, with networking configured, # mount the root file system, and chroot into it. If using ChromeOS, unmount # the device if it was automatically mounted before proceeding. mount /dev/sda2 /mnt chroot /mnt mount -t proc none /proc mount -t sysfs none /sys mount -t devtmpfs none /dev # Copy the host /etc/resolv.conf to the chroot if needed # Install the packages required for partitioning and generating the kernel # image in the format expected by the boot loader apt install parted cgpt vboot-utils vboot-kernel-utils u-boot-tools # Copy the firmware blobs to /lib/firmware mkdir -p /lib/firmware cp -a /root/kernel/firmware/* /lib/firmware # Adjust the kernel image generation files in /root/kernel to refer to the # correct kernel partition (the relevant files are deploy.sh and kernel args). # One the Chromebook, the USB drive is /dev/sda, whereas the external microSD # card is /dev/mmcblk1. The files to adjust are deploys.sh and kernel.args. # Create a link to the deploy.sh script so that every time a kernel is # installed, a signed kernel image is generated and written to the kernel # partition (note that the link must not end with .sh for this to work). ln -s /root/kernel/deploy.sh /etc/kernel/postinst.d/zz-deploy # Install the kernel packages dpkg -i # Depending on how you installed Debian, the previous operation may fail # because it can't find a symlink to the kernel. Solve this with the # linux-update-symlinks command, e.g. linux-update-symlinks install 4.4.121-c101pa+ /boot/vmlinuz-4.4.121-c101pa+ # Once done, you may remove the Debian kernel apt purge linux-image-4.9.0-6-arm64 linux-image-arm64 # It's recommended to install software to configure wireless networking, # such as wicd apt install wicd-curses # Don't forget to properly unmount after leaving chroot, as this makes # sure all the written data is actually on disk afterwards. umount /mnt/dev umount /mnt/sys umount /mnt/proc umount /mnt Deploy to internal eMMC ----------------------- Once the rescue device boots, deploying to the internal eMMC is quite simple. Make sure not to leave any data of value on the Chromebook since this is a destructive operation. Also, make sure to set up networking on the rescue device so that additional software can easily be installed. The idea is to boot from the rescue device (Ctrl-U from the boot screen), repartition the internal eMMC, write the root file system to the data partition, mount it and chroot inside, and install the kernel from there. Depending on the rescue device capacity, you may store the root file system image and kernel packages and generation files inside, on an additional USB drive (if booting from a microSD card) or microSD card (if booting from a USB drive), or you may fetch them through the network. As root, on the target, after booting on the rescue device : # Create the partition table parted --script /dev/mmcblk0 mklabel gpt # Create the kernel partition cgpt add -t kernel -l kernel -P 10 -S 1 -b 64 -s 131072 /dev/mmcblk0 # Create the root file system partition # Compute the sizes from cgpt show /dev/mmcblk0 as for the rescue device cgpt add -t data -l / -b 131136 -s 30646175 /dev/mmcblk0 # Write the root file system into the root file system partition The rest is very similar to the rescue device, with the exception that the target device is /dev/mmcblk0, with /dev/mmcblk0p1 being the kernel partition and /dev/mmcblk0p2 the root file system partition. Once done, boot from the internal eMMC with Ctrl-D from the boot screen.