Dual Booting Chrome OS and elementary OS
I recently decided to purchase a Chromebook. Considering that it’s pretty easy to find them for under $200, they seemed like a good deal, especially taking into account the new features that Chrome OS has been adding (namely, the ability to install Android apps and run Linux in an container via Crostini). What is more interesting, however, is that Chromebooks allow for disabling verified boot: while Chrome OS is itself Linux-based, with this validation disabled we can also install a full-blown, “standard” Linux distribution on the device. With a bit of effort, I’ve managed to get this to work pretty well; to save others (and myself, if I end up having to redo this later!) time I felt like it might be useful to document my installation process here.
Overview
To aid those from the internet who might stumble across this post, I’m going to lay out the goal of this process explicitly: this post goes through the process of installing elementary OS 5.0 “Juno” alongside Chrome OS on the internal storage of my Chromebook, a Samsung Chromebook 3 “Celes” XE501C13-K02US (with a “Braswell” Intel Dual-Core Celeron N3060 processor, 4 GB of DDR3 RAM, and 32 GB of eMMC storage). I can’t be certain that this will work on your device, especially if your configuration deviates from mine, but hopefully it can still be useful in some part. I’ll try to document places where I had to “experiment” or piece together things to work the way I wanted it to. If you follow this guide exactly, you should end up with a Chromebook that boots Chrome OS with a 5 GB “user state” partition (at the time of writing sufficient to fit Chrome OS, a very lightly used Android runtime and Linux container with a gigabyte or two left over) and an elementary OS install with a ~20 GB /
partition and a 500 MB /boot
partition (which ends up about a third full after the install). With some luck, and possibly some small changes, you might be to make this work with other Intel-based Chromebooks or Linux distributions. Finally, you’re going to have a flash drive that’s at least 2 GB large for elementary OS. If you mess up, you’re going to need an 8 GB flash drive on hand for the recovery image.
Partition Layout
Chromebooks require a very specific partition layout; any changes to this (and I mean any, as we’ll see) will cause Chrome OS to fail to boot with a “Chrome OS is missing or damaged” error. Since our goal is to have both elementary OS and Chrome OS side-by-side, this means that we are limited in the changes we can make–for example, we cannot add partitions, nor can we remove them. Thankfully, the disk format is well documented:
In logical order, the partitions are:
Partition | Label |
---|---|
1 | STATE |
2 | KERN-A |
3 | ROOT-A |
4 | KERN-B |
5 | ROOT-B |
6 | KERN-C |
7 | ROOT-C |
8 | OEM |
9 | reserved |
10 | reserved |
11 | RWFW |
12 | EFI-SYSTEM |
KERN-A
is the kernel for Chrome OS, while ROOT-A
is the root filesystem; KERN-B
and ROOT-B
fulfill a similar function and are used by the autoupdate system. These, along with most of the other partitions, don’t need to be touched; we will leave them as they are (except for RWFW
, which the firmware script will modify). The three partitions we care about are STATE
, KERN-C
, and ROOT-C
; the first stores user data and is typically the largest partition, while the other two are currently unused in Chrome OS but are theoretically supposed to serve a purpose similar to that of the other KERN
and ROOT
partitions.
With the current partition map, there is little space for our elementary OS installation. To make this work, we will shrink STATE
(which unfortunately also wipes it) and expand KERN-C
and ROOT-C
into the newly freed space, with KERN-C
being mapped to /boot
in our elementary OS installation and ROOT-C
becoming /
. With this out of the way, let’s get on with actually performing the installation.
Installation Steps
The installation consists of five main steps: basic setup, updating the firmware of the device, partitioning it, installing elementary OS, and fixing GRUB so we can boot the installation. The first and third steps will wipe your device. If you something goes wrong in these steps, you will likely lose data as well. Be careful, and keep backups! There’s a section at the end for recovering from mistakes (though, these generally involve data loss).
Setup
The first thing you will have to do is enable Developer mode on your Chromebook. On mine, the keyboard combination for this is Power+Esc+Refresh (the third function key) to get to recovery mode, then Ctrl+D (here’s a list of these shortcuts). This will wipe your device, as well as make it less secure.
Once you’ve done that, go ahead and boot the computer and connect it to Wi-Fi. It’s probably not worth going further than this in the setup process, since we will be wiping the computer again, but you are free to do so if you’d like.
Update your firmware
Depending on your Chromebook model, you may need to install an updated firmware to be able to boot Linux (the GalliumOS wiki is good place to check to see whether you need to do this); this is a shell script that will do the steps necessary for this process. Assuming you have enabled Developer mode and have just passed the Wi-Fi setup screen, you can access a shell by pressing Ctrl+Alt+Forward (the second function key): as the message will tell you, the username is chronos
. If you are already logged in to Chrome OS just press Ctrl+Alt+T and type shell
like you normally would. Then download and run the script:
$ curl -LO https://mrchromebox.tech/firmware-util.sh
$ sudo bash firmware-util.sh
At the prompt, type 1
and press enter to “Install/Update RW_LEGACY Firmware”; it’s your choice how you want the boot order to be. Pick one and let it finish, then type q
to exit. You don’t have to reboot; just go to the next step.
Partition the disk
Before we partition the disk, you will need to decide how much space you want to allocate to each partition. I gave STATE
5 GB, KERN-C
500 MB, and ROOT-C
the remainder (about 20 GB), and the following commands will use this layout; however, you may choose different sizes based on your needs. These steps will wipe your Chromebook’s data.
The command we will be using throughout this section is cgpt
, which handles partitions on Chrome OS. It is quite important that Chrome OS is the only thing that touches your partitions–failure to adhere to this so has caused a “Chrome OS is missing or damaged” error for me every time. cpgt
requires superuser privileges to run, and always takes as an argument the device it will work on. For eMMC backed-computers, this will likely be /dev/mmcblk0
; if you have an SSD this might be /dev/sda
. First, list the partitions on the disk:
$ sudo cgpt show /dev/mmcblk0
start size part contents
0 1 PMBR (Boot GUID: BB000A0D-0001-0EB4-CD10-AC3C0075F4C3)
1 1 Pri GPT header
2 32 Pri GPT table
8704000 52367312 1 Label: "STATE"
Type: Linux data
UUID: 56FACA9B-B561-5E4E-B225-67FF9101E64A
20480 32768 2 Label: "KERN-A"
Type: ChromeOS kernel
UUID: 0981A5B2-C79A-E145-802A-DEA62009F8DA
Attr: priority=1 tries=0 successful=1
4509696 4194304 3 Label: "ROOT-A"
Type: ChromeOS rootfs
UUID: BDBF49E7-B855-D04E-B09E-FDF2FE9E72A9
53248 32768 4 Label: "KERN-B"
Type: ChromeOS kernel
UUID: F7D51A74-2D18-5649-B352-B3C779269C71
Attr: priority=0 tries=15 successful=0
315392 4194304 5 Label: "ROOT-B"
Type: ChromeOS rootfs
UUID: D47975C2-3B96-F54A-AAA7-394802C5BED2
16648 1 6 Label: "KERN-C"
Type: ChromeOS kernel
UUID: 64A4302A-7FFD-0543-9ADD-004D20919615
Attr: priority=0 tries=15 successful=0
16649 1 7 Label: "ROOT-C"
Type: ChromeOS rootfs
UUID: 09C55DBA-67B7-A14A-BC05-C93124403597
86016 32768 8 Label: "OEM"
Type: Linux data
UUID: DB2D7C8A-7F32-1F46-BC47-313EAB282E35
16450 1 9 Label: "reserved"
Type: ChromeOS reserved
UUID: 531EB8EE-4240-2D44-A85F-27521C3934B0
16451 1 10 Label: "reserved"
Type: ChromeOS reserved
UUID: F2E20DFD-8967-DE4F-96A2-F780936B29A2
64 16384 11 Label: "RWFW"
Type: ChromeOS firmware
UUID: 9AC55AEB-1593-C247-BC54-9C9D9998990B
249856 65536 12 Label: "EFI-SYSTEM"
Type: EFI System Partition
UUID: CDCA82E4-547E-BE4E-AD55-C3B5EFD5DF79
Attr: legacy_boot=1
61071327 32 Sec GPT table
61071359 1 Sec GPT header
The partitions are listed with their labels, partition number, start block, and size. Blocks are 512 bytes in length; notice that while the table is listed in logical order, it is not in physical order–the blocks are actually in a different order on disk (this, by the way, seems to be what trips up the scripts above). Let’s put the partitions in physical order:
Number | Label | Start | Size |
---|---|---|---|
11 | RWFW | 64 | 16384 |
6 | KERN-C | 16648 | 1 |
7 | ROOT-C | 16649 | 1 |
9 | reserved | 16450 | 1 |
10 | reserved | 16451 | 1 |
2 | KERN-A | 20480 | 32768 |
4 | KERN-B | 53248 | 32768 |
8 | OEM | 86016 | 32768 |
12 | EFI-SYSTEM | 249856 | 65536 |
5 | ROOT-B | 315392 | 4194304 |
3 | ROOT-A | 4509696 | 4194304 |
1 | STATE | 8704000 | 52367312 |
Luckily, the STATE
partition (which is the first one, logically) is right at the very end physically, which means we can shrink it easily. To do this, we can use cgpt add
, which takes the partition number with -i
, starting block with -b
, and size in blocks with -s
. For this, we will keep its starting point, block 8704000, the same and shrink the partition down to 10485760 blocks (5 GB):
$ sudo cpgt add -i 1 -b 8704000 -s 10485760 /dev/mmcblk0
Unfortunately, KERN-C
and ROOT-C
are wedged in the middle of other partitions, making it impossible to increase their size without overwriting the subsequent ones. However, we can just change their base to fall right after STATE
and expand them in the new space we have created. Thus, KERN-C
(the sixth partition) will now start at block 8704000+10485760+1=19189761 and span 1048576 blocks, while ROOT_C
(the seventh) starts at 19189761+1048576+1=20238337. ROOT-C
’s size is the amount we shaved off of STATE
minus the size of KERN-C
, or (52367312-10485760)-1048576=40832976. In cgpt
, this translates to
$ sudo cgpt add -i 6 -b 19189761 -s 1048576 /dev/mmcblk0
$ sudo cgpt add -i 7 -b 20238337 -s 40832976 /dev/mmcblk0
With this done, you can reboot the system:
$ sudo /sbin/reboot
When Chrome OS boots up, it will repair itself, taking about five minutes (there’s a small, somewhat accurate timer in the top left that tracks progress). Once it’s done booting, drop back into a shell; we now need to format the new partitions as ext4 so they are usable from Linux.
Once you’re back in a shell, use mkfs.ext4
to initialize the partitions we will be using for elementary OS (KERN-C
and ROOT-C
, which are the sixth and seventh partitions in /dev/mmcblk0
for my Chromebook):
$ sudo /sbin/mkfs.ext4 /dev/mmcblk0p6
$ sudo /sbin/mkfs.ext4 /dev/mmcblk0p7
Installing elementary OS
You’re now going to have grab your bootable installer USB. Plug it into the computer and reboot:
$ sudo /sbin/reboot
On startup, press Ctrl+L this time; this will start legacy boot. Depending on the options you selected earlier when you installed the new firmware, the USB will either automatically boot or you will have to quickly press Esc and select your USB device from the boot menu.
Once you have booted, start the installer.
Go through the setup as usual, until you reach the bit where it asks you about your installation type. Make sure to select the option for “Something else” here:
This will take you to the partition editor. Here, we need to modify the KERN-C
and ROOT-C
partitions. Click on them and them change them to have a “Ext4 journaling file system” (do not format the partition–leave the checkbox unchecked); in addition, change the mount point for the first one to /boot
and the second to /
(this might cause the installer to prompt about performing a resize, or something like that; out of fear I just hit back on the dialog and it seemed to work out). Finally, change the device for boot loader installation to where KERN-C
is. In the end, it should look something like this:
Going to the next step will probably cause some warnings to pop up about not formatting; just skip through those. Continue with the rest of the installation as normal, however, hold off on rebooting when the installation completes.
Fixing GRUB
The installer exits in a state which is unfortunately still not bootable, so we will have to fix this ourselves. Launch a terminal, and create /mnt/boot
:
$ sudo mkdir /mnt/boot
Mount the boot partition, KERN-C
, there (not the whole device, just the partition):
$ sudo mount /dev/mmcblk0p6 /mnt/boot
We need to install GRUB to this partition now. We will need to pass it the --boot-directory
flag to tell it where our boot directory is, and possibly --force
if it complains about blocklists being unreliable. In all, the command should look something like this:
$ sudo grub-install --boot-directory=/mnt/boot /dev/mmcblk0 --force
Notice that we did not use the partition here; this is for the entire device. This will set up the correct MBR records and allow us to boot straight into elementary OS next time we perform a legacy boot. Reboot your computer, remove the Live USB, and press Ctrl+L at startup: the Chromebook should go directly into Linux. Pressing Ctrl+D should take you to Chrome OS instead (do check this: Linux is less picky about booting, and these steps can easily lead to a broken Chrome OS install even when elementary OS boots fine). If this doesn’t work, see below for troubleshooting.
Troubleshooting
You’ve done something wrong and now Chrome OS doesn’t work, or you’re being dropped into a GRUB>
prompt. Here’s some stuff that might help.
Chrome OS Recovery
If you are getting an error that Chrome OS is missing or damaged when trying to boot it, then you need to use Chrome OS Recovery. This will reset your Chromebook to its original out-of-box state (wiping your data), which also makes it useful as a way to go back to single-boot Chrome OS if you so desire.
Supposedly, there’s a Chrome Extension that does the work of flashing this to a flash drive for you: don’t bother with it; it didn’t work for me and it’s really not necessary, since you can just download and flash the image yourself. The list of recoveries that the tool uses is here; look for your device in this, grab the URL, and download it by hand. You can then unzip that firmware and directly image it (via dd
, if you have it) to a flash drive. I’d keep this around for a bit, until you’re sure that your installation works fine and you don’t want to go back to a factory install.
I messed up when installing Linux
Does your Chrome OS still boot? If so, just wipe your KERN-C
and ROOT-C
partitions with mkfs.ext4
and try again, assuming that your partitions are the correct size; if all goes well this should preserve your Chrome OS installation. If not, go back another step–this will unfortunately erase your Chrome OS data. If that doesn’t work, either, use the recovery to start from the beginning.
I can’t boot into Linux
If you’re getting a black screen after pressing Ctrl+L, getting stuck at “Booting from hard disk”, or a GRUB>
prompt, it’s likely that the GRUB fix described above didn’t work. Double check to make sure you installed the boot loader to the correct partition, as well as did grub-install
to the right place. Even if you have installed your Linux correctly, I have seen the system mess up sometimes after booting from a Live CD, or updating the Linux kernel–just run the GRUB install step again and this should take care of the issue.