[Guide] Linux on the Nexus 9

42 posts
Thanks Meter: 140
By sonicadvance1, Member on 27th December 2014, 07:22 PM
Post Reply Email Thread
This is a guide about getting a Linux distribution up and running on your Nexus 9. The goal of this thread is to give a basic understanding about how to get a basic system running, but not working throughout all the kinks that come with doing so on tablet style hardware.
This guide will be non-destructive, and won't touch the data on your tablet. Unless of course you've yet to unlock your bootloader, which if that is the case then shame on you.

Some prerequisites before starting this project with your Nexus 9
  • A Linux distribution running on your computer. Either in a virtual machine or as your main OS.
  • A USB flash drive
  • USB OTG cable
Some things that are nice to haveA brief overview about what we are going to be doing here.
First we are going to be building an initramfs. This is a small filesystem with a bare minimum set of tools that can be loaded in to RAM space and use. We will be using this while we are initially getting our kernel up and running, to make sure we didn't mess anything up.

Next we will move on to building the actual kernel for the device. We will be running a Android kernel since Nvidia hasn't been the quickest on their attempt to upstreaming everything required to run this device on a upstream Linux kernel. So we'll be running a 3.10 kernel, which is a heck of a lot better than some 3.0 or 3.4 thing that older devices are on.

Third we will be building our rootfs that will run from an external USB storage device, so we don't affect any data actually on the tablet. I may also add to the guide how to partition your internal storage space so we can use that instead.

[Step 0/4]
We need to set up our build environment on our host Linux distribution before we begin anything. I'm going to assume that the host Linux system we are running is Debian/Ubuntu based, so we'll be using apt to grab our packages we need.
sudo apt-get install gcc g++ git gcc-4.9-aarch64-linux-gnu g++-4.9-aarch64-linux-gnu libncurses5-dev
This installs the host gcc and AArch64 cross compiler, along with git. We will need these tools to build everything required.

Things that don't work.
  • Rebooting
  • Touchscreen
  • Way more things that people care about
The Following 27 Users Say Thank You to sonicadvance1 For This Useful Post: [ View ] Gift sonicadvance1 Ad-Free
27th December 2014, 07:25 PM |#2  
sonicadvance1's Avatar
OP Member
Thanks Meter: 140
The first step that we will be doing is building our filesystem that we will load in to memory for testing.
This is typically called an initramfs, which tends to be a compressed filesystem that gets copied in to a kernel image once the kernel is built. We won't be using this image for long, as it is mostly to make sure we have everything working correctly before jumping to a full linux distribution.

This step is technically unnecessary, but it brings you up to speed with what initially needs to be done to get something running.

So starting off, we are going to need to pick a target for the filesystem. I've decided to go with buildroot since it is a project that is really easy to get built.

Steps Overview
  • Clone buildroot with git
  • Setup buildroot to cross compile
  • Set up basic options
  • Compile!
1)*Clone buildroot from github.
git clone
cd buildroot
2)*Start configuring buildroot
make menuconfig
This will bring up a menu for determining how we want to build our buildroot. We have quite a few things to change.
These options enable building an AArch64 capable buildroot using the Linaro AArch64 toolchain, with it outputting a terminal to ttyFIQ0 which is the headphone UART terminal. Along with taking the filesystem and pushing it in to a cpio archive which later the linux kernel understands how to use.

Target options->Target Architecture->AArch64
Toolchain->Toolchain type->External toolchain
  • External toolchain automatically enables the Linaro AArch64 toolchain below it.
System configuration->getty options->TTY port->ttyFIQ0
Filesystem images->cpio the root filesystem

3)*Build the buildroot
This will go through downloading all the packages required to build an AArch64 buildroot. So go make a cup of tea, this should only take a few minutes depending on your internet speed and computer speed.

Once the system has gone through compiling everything, then you'll have a file available.
This file will end up in the `output/images/` folder as the file 'rootfs.cpio'. If that file isn't there then something terrible has happened.

If the file is there, then everything on this step is done! We built our temporary filesystem which we will be putting to use in the next step!
The Following 15 Users Say Thank You to sonicadvance1 For This Useful Post: [ View ] Gift sonicadvance1 Ad-Free
27th December 2014, 07:27 PM |#3  
sonicadvance1's Avatar
OP Member
Thanks Meter: 140
This is really the meat of the guide. Building the kernel correctly is half the battle with running a full Linux distribution on the device. We are going to be pulling our kernel from the official Android repository and changing the default configuration to suit our needs.

Steps Overview
  • Clone kernel with git
  • Configure environment for cross-compiling
  • Configure the kernel for building correctly
  • Test run kernel
1) Clone the kernel using git
This step will take quite a bit of time since it is fairly large.
git clone
cd tegra
2)*Checkout the correct branch
git checkout android-tegra-flounder-3.10-lollipop-release
3)*Set environment variables for cross-compiling the Linux kernel
export ARCH=arm64
export CROSS_COMPILE=aarch64-linux-gnu-
4) Configure the kernel
make flounder_defconfig
make menuconfig
This will bring up a menu just like how the buildroot menu came up. We need to set a few options to make sure everything comes up correctly on the Nexus 9.
General setup->Cross-compiler tool prefix->aarch64-linux-gnu-
General setup->Initramfs source file(s)-><The cpio file that was built in the buildroot>
  • Mine was '/home/ryanh/work/N9_Kernel_Tutorial/buildroot/output/images/rootfs.cpio'
Device Drivers->Character devices->Virtual Terminal
Device Drivers->Generic Driver Options->Maintain a devtmpfs filesystem to mount at /dev
Device Drivers->Generic Driver Options->Automount devtmpfs at /dev...
Device Drivers->Network device support->Wireless LAN->Firmware path->/lib/firmware/fw_bcmdhd.bin
Device Drivers->Network device support->Wireless LAN->NVRAM path->/etc/wifi/
Device Drivers->Character devices->Virtual terminal
Device Drivers->Watchdog Timer Support->Tegra watchdog (Disable it)
Device Drivers->Staging drivers->Android->Put the FIQ debugger into console mode by default
Device Drivers->Graphics support->Tegra Framebuffer driver
Device Drivers->Graphics support->Console display driver support->Framebuffer Console support
Device Drivers->Graphics support->Console display driver support->Map the console to the primary display device
Device Drivers->Graphics support->Console display driver support->Framebuffer Console Rotation

We've got to enable the tegra watchdog otherwise the device will reboot automatically every two minutes.
The few other things are needed for a sane Linux environment and also setting the initramfs file as our root filesystem.

5)*Compile the kernel
make -j5
If you're lucky it will build without error and you'll have a file in 'arch/arm64/boot/' named 'Image.gz-dtb' which is the final kernel file.
With this file we should be able to boot a kernel with a buildroot automatically mounted.

Test run the kernel!
This is where I recommend having your headphone UART cable, because this won't update the screen at all and it will seem like it has locked up. The cable is fairly easy to make with a little bit of know how about soldering and electronics, and will save you from tearing out your hair trying to figure out what went wrong.

Without the cable you won't be able to see any output, so either build the cable or skip this step.
To see output from the cable you need to connect to it with either gnu screen or minicom
sudo screen /dev/ttyUSB0 115200
To boot the kernel reboot in to the bootloader and then boot the kernel using fastboot
adb reboot bootloader
fastboot -c "console=ttyFIQ0,115200n8 rw" boot arch/arm64/boot/Image.gz-dtb
Once you do this, over the UART cable you'll see the Linux kernel booting and have a buildroot login terminal.
The default username is 'root' without any password.
Welcome to Buildroot
buildroot login: root
# uname -a
Linux buildroot 3.10.40-ga3846f1 #2 SMP PREEMPT Sat Dec 27 06:15:13 CST 2014 aarch64 GNU/Linux
This shows that the kernel is booting properly and jumping in to our buildroot correctly.

To go back in to Android from this area just type reboot in to the terminal instance.
The Following 16 Users Say Thank You to sonicadvance1 For This Useful Post: [ View ] Gift sonicadvance1 Ad-Free
27th December 2014, 07:29 PM |#4  
sonicadvance1's Avatar
OP Member
Thanks Meter: 140
The final step in getting a Linux distro is to build our root filesystem on a USB flash drive.

Steps Overview
  • Format flash drive as ext2/ext3/ext4
  • Dump Ubuntu rootfs on to the flash drive
  • Minor configuration
  • Rebuild Linux kernel with new options
The first thing we will want to be doing is formatting a flash drive to a Linux partition type. I am going to be using an ext3 partition on a 64GB USB 3.0 flash drive. I'd recommend at least an 8GB flash drive, anything smaller may have issues with fitting everything on to it.

Once you're done flashing your drive you'll need to download an Ubuntu Core image for AArch64/ARM64/ARMv8.
Download the Ubuntu Core 14.04.1 image*Here. Make sure to grab the 'arm64' tar.gz file, not the 'amd64' file.

Once you have the flash drive formatted and mounted, extract the ubuntu core image to the flash drive.
sudo tar zxf ubuntu-core-14.04.1-core-arm64.tar.gz -C Mount/
Configuring the Ubuntu Core image for the Nexus 9
Steps Overview
  • Terminal over UART configuration
  • DNS configuration
  • Firmware files
  • Add user
In order to properly get a terminal instance over the UART we have to add a package to the base Ubuntu core image.
This is a small package that all it does is open a new terminal instance over the configured getty instance. The package can be found*Here.
To install it to the root filesystem just unpack it to the root filesystem on the USB flash drive
sudo tar xf console.tar -C Mount/

We need to set up a DNS server so that the filesystem will be able to resolve addresses via DNS.
Let's just set it to Google's DNS.
sudo echo 'nameserver' >> Mount/etc/resolv.conf

We've got to grab the firmware files from the Nexus 9 in order for the device to stop spamming warnings at us in the console.
These are used for multiple things, so it is a good idea to grab them. You can grab these either from a factory image or directly from the Nexus 9. I chose to grab mine directly from the Nexus 9.
sudo mkdir Mount/lib/firmware
sudo mkdir Mount/etc/wifi
sudo adb pull /vendor/firmware Mount/lib/firmware/
sudo adb pull /system/etc/wifi/ Mount/etc/wifi/
We need to add a user to the root filesystem. This is a fairly annoying step because we actually need to chroot in to the filesystem.
There is a really nice guide to doing this on a ARMv7 filesystem*Here. This won't work for our system because we are working with ARMv8 instead.
So we are going to use that guide as a base but change it over to support what we need to do for AArch64.
First thing we've got to do is build qemu as a static binary for AArch64
This is fairly straight forward.
git clone git://
cd qemu
sudo apt-get build-dep qemu
./configure --target-list=aarch64-linux-user *--static --disable-werror
make -j5
This will get us a binary in the aarch64-linux-user folder called 'qemu-aarch64'
We will need to rename this to 'qemu-arm64-static' and move it in to the '/usr/bin/' folder inside of our root partition
Once qemu is inside of the root partition, we will be able to chroot in to it and add our user.
So go in to the root directory of our filesystem we are generating, and run a few basic commands.

Set up some mounts inside of the chroot

for m in `echo 'sys dev proc'`; do sudo mount /$m ./$m -o bind; done
*Chroot in to the root filesystem
sudo LC_ALL=C chroot . /bin/bash
Now we are inside of the root filesystem, we can add the new user to it.
Let's just add a new user named 'ubuntu'. The first command will ask for a password for your user. The rest will add it to some default groups to make sure it can do things.
adduser ubuntu
addgroup ubuntu adm
addgroup ubuntu sudo
Once the user is added you can exit the root filesystem with a regular exit command, then we have to make sure to unmount all of the mounts we did prior to chrooting in to the filesystem.
for m in `echo 'sys dev proc'`; do sudo umount ./$m; done
Make sure to cleanly unmount the flash drive so everything is written to it.

Reconfigure the kernel to boot from flash drive instead of initramfs
1)*Go in to the kernel configuration
make menuconfig
Change the configuration to remove the initramfs
General Setup->Initial RAM filesystem and RAM disk (initramfs/initrd) support (Disable it)

Then exit the menu and rebuild the kernel
make -j5
Running the Ubuntu Core Image
So with the device sitting at the bootloader we will need to boot the new kernel
fastboot -c "fbcon=rotate:1 root=/dev/sda1 rootwait rw" boot arch/arm64/boot/Image.gz-dtb
This will boot the kernel and then it'll wait until you plug in the flash drive to continue booting.
So plug the USB hub in to the USB OTG cable. Then plug your USB flash drive and USB keyboard in to the USB hub.
After that plug the USB OTG cable in to the tablet and it will continue booting.
Give it roughly 20-60 seconds and it will show a login prompt on the devices screen.
You'll be able to login to the core image with the user you created earlier.

With some packages installed like the xubuntu-desktop environment you can have a full desktop available.
The Following 24 Users Say Thank You to sonicadvance1 For This Useful Post: [ View ] Gift sonicadvance1 Ad-Free
27th December 2014, 07:31 PM |#5  
sonicadvance1's Avatar
OP Member
Thanks Meter: 140
Reserved for QA - Additional Information - Misc

Hardware acceleration
Potentially hardware acceleration should be possible using the Nouveau video driver. This is completely untested, and the Nouveau version included with Ubuntu 14.04 isn't new enough to have GK20a support. One would most likely need to build Nouveau from ToT to get support.

Known Issues
  • If something tries enabling the wifi chipset then the kernel will hardlock and cause the device to reboot. It may be best to disable the broadcom driver in the kernel until that is figured out.
  • Bluetooth doesn't seem to work. Not sure why not. Seems to show up as a device, but it can't be used.
The Following 7 Users Say Thank You to sonicadvance1 For This Useful Post: [ View ] Gift sonicadvance1 Ad-Free
27th December 2014, 08:53 PM |#6  
Thanks Meter: 22
Wow thanks for taking the time to post this tuturial.
Greatly apriciated.
27th December 2014, 08:55 PM |#7  
di11igaf's Avatar
Inactive Recognized Developer
Flag East Coast
Thanks Meter: 746
Donate to Me
Nice work, excellent writeup.
28th December 2014, 05:35 AM |#8  
USBhost's Avatar
Recognized Contributor
Flag u:r:usbhost:s0
Thanks Meter: 6,988
Donate to Me
Cant wait to try!!

---------- Post added at 08:35 PM ---------- Previous post was at 08:31 PM ----------

Where did you get your headphone UART cable
28th December 2014, 06:38 AM |#9  
Art Vanderlay's Avatar
Retired Forum Moderator
Flag Melbourne
Thanks Meter: 2,946
Great work sonic, been waiting for this. Will try and report back.
28th December 2014, 12:24 PM |#10  
sonicadvance1's Avatar
OP Member
Thanks Meter: 140
I took some time to figure out how to fix the integrated wireless LAN in the tablet.
This requires grabbing an additional file from the tablet and configuring a few kernel options so they know where the new file locations are since previously they are configured to Android specific locations.
Seems to work fine on my 802.11n 2.4Ghz network.
The Following 3 Users Say Thank You to sonicadvance1 For This Useful Post: [ View ] Gift sonicadvance1 Ad-Free
28th December 2014, 07:44 PM |#11  
farmerbb's Avatar
Senior Member
Flag Somewhere, Utah
Thanks Meter: 343
Donate to Me
Running into a snag while compiling the kernel. It always fails at this exact spot on Ubuntu 14.10. Any ideas? (first time compiling a kernel from source )

Post Reply Subscribe to Thread

Guest Quick Reply (no urls or BBcode)
Previous Thread Next Thread
Thread Tools Search this Thread
Search this Thread:

Advanced Search
Display Modes