This post explains how to install Debian wheezy onto an SD card for use with the Samsung ARM Chromebook (model number XE303C12), and how to compile Linux 3.15-rc7 so that the Chromebook boots with a (mostly) stock Linux kernel.
Table of contents:
An ARM Chromebook running stock Linux would be a great travel workstation:
Unfortunately, it is not straight forward to boot stock Debian with a stock Linux kernel on this device. It does not have a standard BIOS or bootloader.
This post explains how I set up my Chromebook. At the end, you should have a (mostly) stock Linux kernel booting a stock Debian distribution:
However, there are still some things that do not work (more on that later).
See https://github.com/jmpesp/debian-on-arm-chromebook for each of the linked scripts here.
Be careful, these scripts are dangerous to run if you don’t know what you’re doing. Be sure to check that the device targets and mount points are sane for your setup.
First, I followed the instructions at the debian.org ARMChromebook page.
Specifically:
The Chromebook will boot from a SD card.
The following script 00-partition-sd-card.sh
sets up the SD card partitions:
#!/bin/bash
# On a Linux workstation
DEV=/dev/sdb
MNT=/mnt
set -x
# Zero out current partition table
dd if=/dev/zero of=${DEV} bs=512 count=1
# Partitioning the device:
parted --script ${DEV} mklabel gpt
cgpt create ${DEV}
cgpt boot -p ${DEV}
# from https://plus.google.com/+OlofJohansson/posts/bQpzEGG15G8
#
# Partition 1: Kernel partition, regular Chrome OS kernel for whatever need you might have (i.e. same as previous instructions)
# Partition 2: Another kernel partition with nv-u-boot on it at higher priority
# Partition 3: Ext2 filesystem for /boot (important, should be ext2)
# Partition 4: rootfs for Linux, ext4 or whatever you prefer
# plus:
# Partition 5: home partition, encrypted
# cgpt expects 512-byte blocks:
let "max=`blockdev --getsz ${DEV}`"
let "offs=512"
let "size=1280"
cgpt add -b ${offs} -s ${size} -t kernel -P 2 -S 1 -l CHROMEOS ${DEV}
let "offs = offs + size"
let "size=128000"
cgpt add -b ${offs} -s ${size} -t kernel -P 3 -S 0 -T 10 -l NVUBOOT ${DEV}
let "offs = offs + size"
let "size=1280000"
# size = 625 MiB
# >>> 1280000.0 * 512 / 1024 / 1024
# 625.0
cgpt add -b ${offs} -s ${size} -t rootfs -P 1 -S 1 -l BOOT ${DEV}
let "offs = offs + size"
let "size = (max - offs) / 4"
cgpt add -b ${offs} -s ${size} -t rootfs -P 1 -S 1 -l ROOT ${DEV}
let "offs = offs + size"
let "size = max - offs - 1024"
cgpt add -b ${offs} -s ${size} -t rootfs -P 1 -S 1 -l HOME ${DEV}
cgpt show ${DEV}
exit 0
Be sure to edit this script by changing the following lines:
DEV=/dev/sdb
As root, plug in your SD card and run 00-partition-sd-card.sh
.
A tool like gparted
will display the partition layout of the SD card visually.
The reason for this partition layout is that the nv-uboot (non-verifying UBoot) that Google supplies expects this partition layout, according to https://plus.google.com/+OlofJohansson/posts/bQpzEGG15G8
Download nv_uboot-snow-simplefb.kpart
from http://www.chromium.org/chromium-os/u-boot-porting-guide/using-nv-u-boot-on-the-samsung-arm-chromebook :
wget -O - http://commondatastorage.googleapis.com/chromeos-localmirror/distfiles/nv_uboot-snow-simplefb.kpart.bz2 | bunzip2 > nv_uboot-snow-simplefb.kpart
The bootloader should be written to partition 2. From your Linux workstation, run 01-install-bootloader.sh
as root:
#!/bin/bash
# On a Linux workstation
DEV=/dev/sdb
set -x
dd if=nv_uboot-snow-simplefb.kpart of=${DEV}2
exit 0
Remember again to modify the DEV variable!
Three of the other partitions hold target filesystems:
The reason that /home is separated is that I encrypted this partition. I did this because being a travel workstation, the probability of loss or theft is higher than a normal workstation. I want to have the peace of mind that comes with not worrying what a theft could mean for my data.
Running 02-create-filesystems.sh
as root creates the filesystems:
#!/bin/bash
# On a Linux workstation
DEV=/dev/sdb
set -x
blockdev --rereadpt ${DEV}
# BOOT: /boot
mkfs.ext2 ${DEV}3
# ROOT: /, where debian root goes
mkfs.ext4 ${DEV}4
# HOME: luks encrypt
cryptsetup luksFormat ${DEV}5
exit 0
Remember again to modify the DEV variable!
If the following is seen:
+ mkfs.ext2 /dev/sdb3
mke2fs 1.42.10 (18-May-2014)
mkfs.ext2: Device size reported to be zero. Invalid partition specified, or
partition table wasn't reread after running fdisk, due to
a modified partition being busy and in use. You may need to reboot
to re-read your partition table.
then remove and insert your SD card and try again. If that doesn’t work, you might need to write zeroes to the whole SD card and try again. This happened to me when I used my USB SD card reader and writing zeroes to the whole SD card worked for me.
This is a two stage Debian bootstrap:
The commands are simple. First, as root, run 03-debootstrap-stage-1.sh
to start the first debootstrap stage targeting wheezy
:
#!/bin/bash
# On a Linux workstation
DEV=/dev/sdb
MNT=/mnt
set -x
# mount ROOT (/), and debootstrap into it
mount ${DEV}4 ${MNT}
debootstrap --arch=armhf --foreign wheezy ${MNT} ftp://ftp.ca.debian.org/debian
umount ${MNT}
exit 0
Next, on the Chromebook, boot into ChromeOS and switch over to the root shell with Ctrl-Alt-F2 (right arrow on esc
row). This will only be possible if the Chromebook was switched into developer mode. Connect the Chromebook to the internet, change into /usr/local/, and then save and run 04-debootstrap-stage-2.sh
:
#!/bin/bash
# On the Chromebook
DEV=/dev/mmcblk1
MNT=/mnt
# Chroot into the SD card, and perform the second debootstrap stage
umount ${DEV}p[1-5]
mount ${DEV}p4 ${MNT}
[[ ${?} -eq 0 ]] || exit 1;
# Bind the following special filesystems for use in the chroot
mount --bind /dev/ ${MNT}/dev/
[[ ${?} -eq 0 ]] || exit 1;
mount --bind /proc/ ${MNT}/proc/
[[ ${?} -eq 0 ]] || exit 1;
mount --bind /sys/ ${MNT}/sys/
[[ ${?} -eq 0 ]] || exit 1;
chroot ${MNT} /debootstrap/debootstrap --second-stage
[[ ${?} -eq 0 ]] || exit 1;
# Setup /etc/fstab for the two unencrypted partitions
cat > ${MNT}/etc/fstab <<EOF
${DEV}p4 / ext4 errors=remount-ro 0 1
${DEV}p3 /boot/ ext2 errors=remount-ro 0 1
EOF
# Populate the /etc/apt/sources.list with target = wheezy
cat > ${MNT}/etc/apt/sources.list <<EOF
deb http://ftp.ca.debian.org/debian wheezy main non-free contrib
deb-src http://ftp.ca.debian.org/debian wheezy main non-free contrib
EOF
# Update the packages
chroot ${MNT} apt-get update
[[ ${?} -eq 0 ]] || exit 1;
chroot ${MNT} apt-get upgrade
[[ ${?} -eq 0 ]] || exit 1;
# Install useful packages
chroot ${MNT} apt-get install wpasupplicant iw upower cryptsetup
echo "yourhostname" > ${MNT}/etc/hostname
# Set the root password
passwd
# Copy over built-in wireless firmware
mkdir ${MNT}/lib/firmware/mrvl/
cp /lib/firmware/mrvl/* ${MNT}/lib/firmware/mrvl/
umount ${MNT}/dev/
umount ${MNT}/proc/
umount ${MNT}/sys/
umount ${MNT}
exit 0
I chose tal as a hostname.
The Debian distribution is now installed onto the SD card - test with
chroot ${MNT}
to get into the bash shell.
I used crosstool-ng with the following configuration:
Paths and misc options --->
(${HOME}/dl) Local tarballs directory
Target options --->
Target Architecture (arm) --->
(v7) Suffix to the arch-part
Endianness: (Little endian) --->
Bitness: (32-bit) --->
Floating point: (hardware (FPU)) --->
Operating System --->
Target OS (linux) --->
Linux kernel version (3.12) --->
Get the Linux kernel source from The Linux Kernel Archives. Linux 3.15-rc7
is available at the time of this article’s writing.
$ gpg --verify linux-3.15-rc7.tar.sign
gpg: Signature made Sun 25 May 2014 07:08:30 PM EDT using RSA key ID 00411886
gpg: Good signature from "Linus Torvalds <torvalds@linux-foundation.org>"
gpg: WARNING: This key is not certified with a trusted signature!
gpg: There is no indication that the signature belongs to the owner.
Primary key fingerprint: ABAF 11C6 5A29 70B1 30AB E3C4 79BE 3E43 0041 1886
From within the linux-3.15-rc7 directory created from un-tarring linux-3.15-rc7.tar.xz, call 05-make-kernel-config.sh
.
#!/bin/bash
export PATH="${HOME}/x-tools/armv7-unknown-linux-gnueabi/bin/:${PATH}"
# from http://www.gentoo.org/proj/en/base/embedded/handbook/?part=1&chap=4
#
# ARCH ?= arm
# CROSS_COMPILE ?= arm-unknown-linux-gnu-
export ARCH=arm
export CROSS_COMPILE=armv7-unknown-linux-gnueabi-
makeopts="ARCH=${ARCH} CROSS_COMPILE=${CROSS_COMPILE}"
set -x
make ${makeopts} clean distclean mrproper
rc=${?}
set +x
if [[ ${rc} -ne 0 ]];
then
echo "make clean distclean returned ${rc}!";
exit ${rc};
fi;
set -x
make ${makeopts} exynos_defconfig
rc=${?}
set +x
if [[ ${rc} -ne 0 ]];
then
echo "make exynos_defconfig returned ${rc}!";
exit ${rc};
fi;
exit 0;
I needed to apply the following patches from https://github.com/virtualopensystems/linux-kvm-arm/ for the built-in wireless networking to work:
a modified version of 4d0ca38eaf77faaab99567ab26566001a3f60c54.patch, which seems to be based off of 9d0094739f89ff8cb0a6f801c15ddafd4eb687ab.patch
a modified version of 11d75fb158817d87bc64bc8d1079587665dbc91d.patch
Grab 06-built-in-wireless.patch
and :
patch -p0 < 06-built-in-wireless.patch
This was the tricky part for me. Until the Chromebook worked, I had never booted successfully from a Linux kernel I compiled myself.
My process for figuring out which configuration options to choose involved every modification to the kernel configuration being saved into config_good
, which was versioned with git. I used the iFixit teardown of the Chromebook as a reference for the components on the motherboard, and found those in the kernel configuration file.
This seemed strange - why wasn’t there a program that would look at my system and select the appropriate kernel configuration options? Turns out that there is:
`make localmodconfig`
Before making this blog post, I had a working Chromebook with Debian on it. So, I scp’ed the linux-3.15-rc7.tar.xz
over to the Chromebook, and performed the localmodconfig
to get a .config file for the Chromebook automatically! But it didn’t work.
Here are the menuconfig options I changed:
General setup --->
[*] Control Group support --->
CPU Power Management --->
CPU Frequency scaling --->
[*] CPU Frequency scaling
Default CPUFreq governor (ondemand) --->
<*> 'powersave' governor
CPU Idle --->
[*] CPU idle PM support
[*] Networking support --->
-*- Wireless --->
<*> cfg80211 - wireless configuration API
[*] cfg80211 wireless extensions compatibility
<*> Generic IEEE 802.11 Networking Stack (mac80211)
Device Drivers --->
[*] Network device support --->
[*] Wireless LAN --->
<*> Marvell WiFi-Ex Driver
<*> Marvell WiFi-Ex Driver for SD8786/SD8787/SD8797/SD8897
Character devices --->
<*> EXYNOS HW random number generator support
[*] Power supply class support --->
<*> SBS Compliant gas gauge
<*> TPS65090 battery charger driver
[*] Board level reset or power off --->
[*] GPIO power-off driver
[*] Restart power-off driver
Graphics support --->
[*] Backlight & LCD device support --->
<M> Backlight Driver for ADP8860/ADP8861/ADP8863 using WLED
<M> Backlight Driver for ADP8870 using WLED
<M> Backlight Driver for LM3630A
<M> Backlight Driver for LM3639
<M> Backlight driver for TI LP855X
<M> Generic GPIO based Backlight Driver
<M> Sanyo LV5207LP Backlight
<M> Rohm BD6107 Backlight
[*] Real Time Clock --->
<*> Maxim MAX77686
[*] Pulse-Width Modulation (PWM) Support --->
<*> Samsung PWM support
-*- Cryptographic API --->
<*> XTS support
<*> AES cipher algorithms (ARM-asm)
Using 07-chromebook-kernel-config.patch
:
cp .config config_good
patch -p0 < 07-chromebook-kernel-config.patch
cp config_good .config
Then, run 08-build-kernel-and-modules.sh
:
#!/bin/bash
export PATH="${HOME}/x-tools/armv7-unknown-linux-gnueabi/bin/:${PATH}"
# from http://www.gentoo.org/proj/en/base/embedded/handbook/?part=1&chap=4
#
# ARCH ?= arm
# CROSS_COMPILE ?= arm-unknown-linux-gnu-
export ARCH=arm
export CROSS_COMPILE=armv7-unknown-linux-gnueabi-
makeopts="ARCH=${ARCH} CROSS_COMPILE=${CROSS_COMPILE}"
set -x
make ${makeopts} uImage dtbs -j5
rc=${?}
set +x
if [[ ${rc} -ne 0 ]];
then
echo "make returned ${rc}!";
exit ${rc};
fi;
# make modules
make -j5
mkdir dist/
make modules_install INSTALL_MOD_PATH=${PWD}/dist/
exit 0;
As root, install the new kernel uImage
and dtb exynos5250-snow.dtb
to your SD card with 09-install-kernel-and-modules.sh
:
#!/bin/bash
# On a Linux workstation
DEV=/dev/sdb
MNT=/mnt
set -x
# Mount / and /boot/, then copy over compiled kernel and modules
mount ${DEV}4 ${MNT}
mount ${DEV}3 ${MNT}/boot/
cp arch/arm/boot/uImage arch/arm/boot/dts/exynos5250-snow.dtb ${MNT}/boot/
rm -rf ${MNT}/lib/modules/*
rsync -avAX dist/ ${MNT}/
umount ${MNT}/boot/
umount ${MNT}
exit 0
If the cross-compilation toolchain is correct, then the Chromebook should boot into Linux 3.15-rc7
without trouble:
Ctrl-U
If not,
cgpt
: cgpt show /dev/mmcblk1
. make sure it looks sane.cgpt add -i 2 -P 3 -S 0 -T 10 /dev/mmcblk1
where -i 2
refers to the UBOOT partitionIf all that doesn’t help, then I recommend either asking questions in #debian-arm on irc.oftc.net, or email me at jwm @ this domain and I will try to help.
If Linux will not boot, check the UBoot environment variables.
After pressing Ctrl-u
, hold space to stop UBoot’s booting process. This drops you into a shell, where you can use commands like printenv
and setenv
to view and modify environment variables.
nv_uboot-snow-simplefb ships with different environment variables than the non-simplefb version also available from Google. Before using the simplefb version, I used the non-simplefb version. At some point, I must have modified the non-simplefb version’s environment variables and then executed saveenv
, saving these to some internal flash.
I had problems until I installed nv_uboot-snow-simplefb and reset the saved environment to what ships with nv_uboot-snow-simplefb:
env default -f
saveenv
No regular user has been created yet. Before creating your day-to-day user id, set up the encrypted home partition.
As root:
cryptsetup luksOpen /dev/mmcblk1p5 sdenc
mkfs.ext4 /dev/mapper/sdenc
mount /dev/mapper/sdenc /home/
After this you can add a regular user with the adduser
command.
To close the encrypted home partition:
umount /home/
cryptsetup luksClose sdenc
I wrapped the creation and closing of the home partition into two bash scripts.
apt-get install openbox tint2 iceweasel
apt-get install xterm
apt-get install xcompmgr
apt-get install vim
apt-get install ntp
apt-get install build-essential
I modified 10-synaptics-chromebook.conf
from the ChromeOS partition.
USB keyboards or mice
There are probably another kernel configuration options that will make these things work.
Power switch
To power off, I have to type reboot
, then tap the power button when back at the Chromebook’s first boot screen.
Backlight dimming
It’s on 100% all the time.
booting an initrd for full root encryption
It works for http://archlinuxarm.org/forum/viewtopic.php?f=47&t=7071 but not for me. I tried for a long time to get this working. I’m settling for /home encryption for now.
xmodmap keyboard remapping
The lack of page up / page down / insert has been noticeable. I tried using xmodmap
to remap those keys but this doesn’t work for me. I am most certainly missing something. For now, I’m using xvkbd
instead.
hardware accelerated graphics
There are directions at the debian.org ARMChromebook page for hardware accelerated graphics, but I don’t need the Chromebook to output anything more than text most of the time. Even without the hardware acceleration, video performance is passable for non-HD video.
Thanks to Aardvark and hrw on #debian-arm for answering my questions and helping me debug problems.