Skip to content

Instantly share code, notes, and snippets.

@rbeucher
Forked from elerch/arch-usb-uefi.md
Created August 5, 2023 23:01
Show Gist options
  • Save rbeucher/5bda0216e181db088ebc64ff9c2c79a5 to your computer and use it in GitHub Desktop.
Save rbeucher/5bda0216e181db088ebc64ff9c2c79a5 to your computer and use it in GitHub Desktop.
Installation of Arch Linux on a USB stick with UEFI and legacy BIOS Support

Our goal here is to have one USB stick to rule them all. Objectives:

  • We want a full system - not a live CD
  • We want to boot this system on a Macbook Pro (requires UEFI)
  • We want to boot this system on a Acer C720 Chromebook (requires Legacy BIOS support)
  • We want the system "functional"

The last bullet is subject to interpretation, but I'm defining functional as:

  • X Windows works (with LXDE)
  • Wireless works
  • Audio works
  • Non-root user is configured with sudo rights
  • We have a browser installed
  • We have vlc installed
  • Trackpad operates (with natural scrolling)

Initial configuration

Much of this was inspired by the following video, however, the dual UEFI/Legacy BIOS support is not covered in the video:

https://www.youtube.com/watch?v=sDq1oaYRgeg

To boot UEFI on Mac, we must have a GPT partition scheme. An EFI system partition must also be somewhere on the drive. Note that the UEFI system partition scan is limited, so for very large drives, you may want to put the system partition first. I put mine at the end as my drive was relatively small and I'd rather not have the EFI System Partition (ESP) show up first when I plug it in to a booted system.

NOTE: COMMANDS BELOW USE /dev/sdX. BE CAREFUL and replace with actual device letter

First, it's probably wise to zero out the disk:

dd if=/dev/zero of=/dev/sdX bs=1k count=2048 

Using sgdisk, I setup the following:

Partition scheme: GPT

sgdisk -z /dev/sdX # zap anything existing (should be nothing after the dd command above)
sgdisk -o /dev/sdX # write a new GPT partition with protective MBR

Parition 1: 0- (-200MB). Linux System Partition

sgdisk -n 1:0:-200M /dev/sdX # Partition 1 - everything but the last 200MB
sgdisk -t 1:8300 /dev/sdX # Set partition type to Linux

Partition 2: 200MB. EFI System Partition with Legacy Boot ON

sgdisk -n 2:-200M:-0 /dev/sdX # create partition 2 - last 200MB
sgdisk -t 2:ef00 /dev/sdX # Set partition type to ESP
sgdisk -A 2:set:2 /dev/sdX # Turn legacy boot attribute on

Before proceeding, it's worth a reboot. sgdisk has probably been complaining along the way that the linux kernel is caching partition information, so just to be safe we should reboot.

Once coming back from the reboot we need to make the appropriate filesystems. For the root filesystem, we'll use ^has_journal. This option will reduce wear on the drive, but adds risk of data failure should the device be removed without unmounting or a reset/power failure occurs.

mkfs.ext4 -O "^has_journal" /dev/sdX1 # Primary Linux partition

The EFI System Partition is more straightforward - just a FAT32:

mkfs.fat -F32 /dev/sdX2 # ESP

Next, we'll mount the partitions in this order:

mount /dev/sdX1 /mnt
mkdir /mnt/boot
mount /dev/sdX2 /mnt/boot

And now we're ready to bootstrap. We'll add vi and wpa_supplicant to make later configuration a bit easier. Also, adding dialog allows us to use wifi-menu later.

pacstrap /mnt base vi wpa_supplicant dialog

Now we need an fstab that will allow us to mount the root partition on boot. UUIDs are necessary since we'll move this from machine to machine:

genfstab -U -p /mnt >> /mnt/etc/fstab

IMPORTANT I've found genfstab -U to be extremely hit and miss. At this point it's best to up the file and verify that UUIDs are actually in there. If not, do an 'ls -l /dev/disk/by-uuid' and replace the appropriate /dev/sdX1 with "UUID=uuid-from-listing".

At this point we can chroot into the system itself to configure the remaining items:

arch-chroot /mnt

Now we have a ton of standard configuration that is well documented both in the video and in the Arch documentation. Much of the following commands will be specific to the installation. These were my commands, so replace with your own values where they make sense.

echo loboarch > /etc/hostname
passwd # Set password for root user
ln -sf /usr/share/zoneinfo/America/Los_Angeles /etc/localtime
cd /etc
vi locale.gen # uncomment locale (en_US UTF8)
vi mkinitcpio.conf # HOOKS=... move block before autodetect
mkinitcpio -p linux
systemctl enable dhcpcd

# Enable EFI boot
pacman -S syslinux
mkdir -p /boot/EFI/syslinux
cp -r /usr/lib/syslinux/efi64/* /boot/EFI/syslinux/
cd /boot/EFI
mv syslinux boot
cd boot
mv syslinux.efi bootx64.efi
vi /boot/syslinux/syslinux.conf # update root partition with UUID=...
                                # also check the paths for the images
                                # they will be relative from the config file

At this point, the system should be able to boot under EFI. This is handy, because if you're like me, you've inceptioned into a arch chroot in an arch live VM (possibly under some other unixy mechanism) and your head may be hurting.

Legacy BIOS Support

Legacy BIOS works by the system looking at the Master Boot Record (MBR), then the Volume Boot Record (VBR) of the partition marked as the boot partition. This is the legacy boot partition attribute we assigned above. We'll have the legacy BIOS also boot into the ESP, which allows us to keep our booting configuration and files centralized.

First, we'll make sure the MBR is "standard". Since it's a GPT-partitioned disk, we'll use that binary from syslinux to represent our mbr:

dd bs=440 count=1 conv=notrunc if=/usr/lib/syslinux/bios/gptmbr.bin of=/dev/sdX

Next, we need to install syslinux into the VBR of the ESP partition:

extlinux --install /boot

At this point if you booted off a legacy BIOS onto the stick, you should see an error message coming from SysLinux. We're installed after all, but not configured. The configuration, however, is all in the filesystem, as syslinux knows what FAT32 is.

mkdir /syslinux
cp -r /usr/lib/syslinux/bios/* /syslinux/
vi /syslinux/syslinux.cfg # See discussion below

The syslinux.cfg will need to be modified to reference the correct paths for the images, but more importantly, syslinux is pretty particular about where the .c32 files get loaded from.

We should now have a USB stick with Arch that can boot both UEFI and BIOS. Test it out on both types of machines, do whatever debugging necessary, and then we'll configure in the OS. I have had issues on UEFI with certain USB sticks! The tails project has had done some good research on this topic, so I encourage you to stay away from all Sandisk products (I used a Sandisk not on the list and still had problems) and read the following web page: https://tails.boum.org/support/known_issues/#index1h2.

Now, whether UEFI or Legacy, we should be able to boot. Let's exit/unmount.

exit # Get out of chroot
umount -R /mnt # Then test in another machine or reboot this one

Configuration on live OS

Now that we have a bootable system, let's boot it and start getting things ready for real usage.

First, Internet access. For me, neither my Macbook nor my Chromebook have hard wired connections, so I need to configure wireless. First up, find the wireless device:

# The hard way: dmesg |fgrep wlan #wlp1s0
ip link # show network interfaces

Then make sure it's offline. wifi-menu likes it better that way.

ip link set wlp3s0 up # replace wlp3s0 with whatever wireless ip link shows you. This was from a mac

Next we should be able to just fire up wifi menu and get connected.

wifi-menu

Now we should have network connectivity, though it may take a bit to connect to wifi. Be patient, then try pinging google by IP:

ping 8.8.8.8

Assuming your connected, we can start pulling some packages and get a more sane system that doesn't require root and gets some UI running. Note my user is lobo, so change as appropriate:

pacman -S sudo
visudo
groupadd sudo
useradd -G sudo -m lobo # Your username will vary!
passwd lobo
```sh

Now we have a user and can stop here or get a desktop, X, vlc, a browser, ssh, and some fonts installed:

```sh
pacman -S exfat-utils lxde xorg-xinit vlc qt4 chromium openssh ttf-dejavu ttf-hack zip unzip obconf dosfstools
# For lxde, I chose all members of the group
# For libgl, we want a portable install, so go with mesa-libgl
# For xf86-input-driver, you'll likely want xf86-input-libinput
# Fonts we'll come back to - for now ttf-ubuntu-font-family
# For libx264.so=148-64, you'll likely want libx264 (better hardware support)

This one is going to cook for a while. Even with a minimal system, this is downloading (on my system) 870MB. Once we're done, we'll modify the xinitrc with a call to startlxde

vi /etc/X11/xinit/xinitrc # comment stuff from twm& and below, replace with startxlde

At this point we can use our normal user account to log in with "startx". the first time will be slow. We will have vlc and chromium and can do some basic stuff. We might want our trackpad to use taps rather than require clicks (and use "Natural Scrolling").

To do so, create a new file /etc/X11/xorg.conf.d/30-touchpad.conf and include the following:

Section "InputClass"
     Identifier "touchpad"
     Driver "libinput"
     MatchIsTouchpad "on"
     Option "Tapping" "on"
     Option "NaturalScrolling" "true"
 EndSection

I also immediately changed the lxterminal font to Hack 10. I did this through the UI, although you can also set the line 'fontname=Hack 10' in ~/.config/lxterminal/lxterminal.conf.

I like my task manager on the left with autohide. I've configured it thusly:

  • Geometry
    • Position/Edge: Left
    • Size/Width: 50px
  • Appearance
    • Background/Solid color (with opacity): Opacity 110
  • Advanced
    • Minimize panel when not in use (checked)

The full file ~/.config/lxpanel/LXDE/panels/panel has been added to the gist

Any self-respecting linux install has some development capabilities. This is also necessary for AUR packages. However, at this point you might not want to push it. I originally did this on a 4GB stick and started to run low on space after all the X stuff, so your mileage may vary.

sudo pacman -S --needed base-devel

I like the idea of Ctrl-Space to open up an application launcher, and the lxde run command is good enough for my purposes. I also wanted Ctrl-Q to close windows. I added the following to ~/.config/openbox/lxde-rc.xml

   <keybind key="C-Q">
     <action name="Close"/>
   </keybind>
   <keybind key="C-space">
       <action name="Execute">
         <command>lxpanelctl run</command>
       </action>
   </keybind>

Also, caps->ctrl, but adding 'xmodmap .Xmodmap' in ~/.config/openbox/autostart. My ~/.Xmodmap looks like this (although I'm on a chromebook and the "Caps Lock" is a search key, which maps to the Windows key keycode, so I can't test ATM):

keycode 66 = Control_L
clear Lock
add Control = Control_L
keycode 117 = Caps_Lock
add Lock = Caps_Lock

Audio

Audio was not working properly, so I added my user to the audio group and installed the alsa-utils.

usermod -a -G audio lobo
pacman -S alsa-utils

If everything is working properly, running 'speaker-test -c 2' should be enough to hear sound. I used alsamixer to unmute everything through an interface, although I think vlc would have done this part automatically. The real trick was to list the cards (cat /proc/asound/cards), then use the device name in the defaults added to ~/.asoundrc, like this:

pcm.!default {
  type hw
  card PCH
}

ctl.!default {
  type hw
  card PCH
}

This does not seem portable between machines but it does seem to work between my C720 and MBP. I've now run through this several times and audio has always been something that has needed a bit of fiddling the first time, but then it seems to be stable.

Wireless

I recommend wpa_actiond for automatic management of wireless networks, which is a simple pacman -S wpa_actiond away. It's useful to read this wiki on this: https://wiki.archlinux.org/index.php/Netctl#Automatic_switching_of_profiles Also, since I'm moving my stick between machines I had an annoying 90 second timeout on boot while systemd looked for hardware that didn't exist. I tried to fix this by adding a 'TimeoutStartSec=10' line to the [Service] section of /usr/lib/systemd/system/[email protected], but that was ineffective so I dropped the global timeout in /etc/systemd/system.conf (to 20 seconds - I had another service that wouldn't start fast enough for 10 seconds).

Suspend/Resume

I had an issue on my C720 with suspend/resume that was corrected with the following instructions: https://bbs.archlinux.org/viewtopic.php?pid=1364521#p1364521. Note that the only part of this fix that was needed for me was the file /usr/lib/systemd/system-sleep/cros-sound-suspend.sh. And make sure not to copy/paste the first line. Like another user on that forum I accidentally copied that line in and because it's before the shebang nothing works. I haven't fully tested this fix for sound/HDMI issues, but I can suspend/resume and continue operating in a terminal. Also, I've yet to test this on my MBP.

Other packages

  • Clipboard manager: parcellite (my config attached to gist. also put parcellite & in ~/.config/openbox/autostart)
  • Battery status: lxde has this on the panel
  • Prevent screen tearing: compton (and put 'compton -b' in ~/.config/openbox/autostart)
  • Powertop: Install per https://wiki.archlinux.org/index.php/powertop. Make sure to run powertop --calibrate after install

UI Configuration

I like things dark, so I install:

Other Stuff/Random notes

# lxpanel <profile> config file. Manually editing is not recommended.
# Use preference dialog in lxpanel to adjust config when you can.
Global {
edge=left
allign=left
margin=0
widthtype=percent
width=100
height=50
transparent=1
tintcolor=#000000
alpha=110
setdocktype=1
setpartialstrut=1
usefontcolor=1
fontcolor=#ffffff
background=0
backgroundfile=/usr/share/lxpanel/images/background.png
autohide=1
}
Plugin {
type=space
Config {
Size=2
}
}
Plugin {
type=menu
Config {
image=/usr/share/lxde/images/lxde-icon.png
system {
}
separator {
}
item {
command=run
}
separator {
}
item {
image=gnome-logout
command=logout
}
}
}
Plugin {
type=launchbar
Config {
Button {
id=pcmanfm.desktop
}
Button {
id=firefox.desktop
}
}
}
Plugin {
type=space
Config {
Size=4
}
}
Plugin {
type=wincmd
Config {
Button1=iconify
Button2=shade
}
}
Plugin {
type=space
Config {
Size=4
}
}
Plugin {
type=pager
Config {
}
}
Plugin {
type=space
Config {
Size=4
}
}
Plugin {
type=taskbar
expand=1
Config {
tooltips=1
IconsOnly=0
AcceptSkipPager=1
ShowIconified=1
ShowMapped=1
ShowAllDesks=0
UseMouseWheel=1
UseUrgencyHint=1
FlatButton=0
MaxTaskWidth=150
spacing=1
}
}
Plugin {
type=tray
Config {
}
}
Plugin {
type=volumealsa
Config {
}
}
Plugin {
type=batt
Config {
}
}
Plugin {
type=dclock
Config {
ClockFmt=%R
TooltipFmt=%A %x
BoldFont=0
IconOnly=0
CenterText=0
}
}
Plugin {
type=launchbar
Config {
Button {
id=lxde-screenlock.desktop
}
Button {
id=lxde-logout.desktop
}
}
}
imary=true
synchronize=true
save_history=true
history_pos=false
history_x=1
history_y=1
history_limit=25
data_size=0
item_size=5
automatic_paste=false
auto_key=true
auto_mouse=true
key_input=false
restore_empty=true
rc_edit=false
type_search=false
case_search=false
ignore_whiteonly=false
trim_wspace_begend=false
trim_newline=false
hyperlinks_only=false
confirm_clear=true
current_on_top=true
single_line=true
reverse_history=false
item_length=50
persistent_history=false
persistent_separate=false
persistent_on_top=false
persistent_delim=\\n
nonprint_disp=false
ellipsize=2
multi_user=true
icon_name=parcellite
menu_key=<Ctrl><Alt>P
history_key=<Ctrl><Alt>Z
phistory_key=<Ctrl><Alt>X
actions_key=<Ctrl><Alt>A
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment