Installing Debian on a MacBook Air Early 2014

22nd June 2023 from jc's blog

My main personal computer is a MacBook Air, specifically the “Early 2014” model. It’s a great device: slim, quiet, and most importantly, working on it feels good. In 9 years of heavy usage I have never had a single issue with its keyboard. On a recent MacBook Pro that I received for work, I regularly ran into issues with the keyboard, not to mention that the new keyboards just don’t have the same good haptic feel to me.

screenshot of the i3 window manager running vim editing this article, withi3bar in the bottom

I wanted to install a new battery for the laptop but understandbly Apple does not really supply this laptop with regular updates anymore, outside of critical security fixes. Therefore, to justify buying a new battery, I set out to install Linux on it.

Tinkering note

This setup involves a fair bit of tinkering. Whether you enjoy that or not is something you need to decide. I post this here in the hopes that it is useful for somebody doing the same.

Hardware

Let’s look at what we have:

First off: why bother with this “slow” gear? Well, I do not think this gear is slow at all. The CPU is snappy enough to make anything I do in my terminals snappy, even when clocked into powersave mode. Where you notice the slowness is when viewing any modern website. I will refrain from making a comment on that. But the rough idea is that I believe as a developer I have a certain responsibility for making sure what I build runs well even on computers that aren’t the latest and greatest developer toy.

In either case, with my first computer it was normal to open a program (back then usually games) and wait ~ two minutes for it to load. I believe that the instant on-click availability of everything these days keeps us glued to the screen more and I don’t like that.

Anyways: because I still wanted to have MacOS, I installed Debian on the SD card.

Liftoff

I flashed the Debian minimal installation on a USB and went off: BTRFS root filesystem, reboot, and I was dropped into the trusty Debian terminal. Very good.

Well, until it wasn’t. Drivers. More specifically, network drivers. Without those the installation was not too useful. I manually downloaded the .deb packages for the broadcom-sta-* packages and their dependencies, transferred it onto a USB drive, and installed it from there. And - surprise - it did not work. Huh?

Getting wireless to work

The driver was one part of the issue. I had to add the following settings to my /etc/wpa_supplicant.conf to allow me to connect to WLANs that weren’t my hotspot:

wps_cred_add_sae = 1
ieee80211w = 1

I do not remember what these mean at all, but I believe one of them came up when I searched for WPA2, and these work for me. Afterwards, I added the SSID and password to a new file in /etc/network/interfaces.d/ and it worked.

Going graphical

Now that we can install more packages, it was time to install i3 and lightdm. Installation completed, I ran startx, was greeted with the login screen, entered my credentials, and there was i3! Except it wasn’t launching my terminal. And the trackpad - well, it was sort of working, but I did not find any way to start an app without the keyboard.

First reboot

When booting I was greeted with grub. From grub, I could boot into macOS - by quitting grub. If I did not quit it, it would boot into Linux, so Linux would seemingly be the default. Back to startx.

The main issue: MacBooks seem to use some exotic keymap. So, after way too many more reboots I realized that I could install any terminal emulator and I would not be able to get it to start.

Configuring the keyboard

The first step to put an end to the useless rebooting was to run startx only as timeout 30 startx. If something worked I could just restart it later.

The solution to getting the keyboard to work properly was to mess around with xmodmap long enough that I could run xev, and then configure the rest. Those two tools work greatly! I ended up with the following ~/.Xmodmap, and I should note that this is built for a German keyboard layout:

! See also gist.github.com/bonndan/9629550 !

clear control
clear mod4
clear mod1

keycode 49 = less greater less greater bar brokenbar bar
keycode 94 = dead_circumflex degree dead_circumflex degree U2023 U2033 U2032

! Keys for writing normal text !

! 5 = '5' '%' '['
keycode 14 = U0035 U0025 U005B
! 6 = '6' '&' ']'
keycode 15 = U0036 U0026 U005D
! 7 = '7' '/' '|' '\'
keycode 16 = U0037 U002F U007C U005C
! 8 = '8' '(' '{'
keycode 17 = U0038 U0028 U007B
! 9 = '9' ')' '}'
keycode 18 = U0039 U0029 U007D
! e = 'e' 'E' '€'
keycode 26 = U0065 U0045 U20AC
! l = 'l' 'L' '@'
keycode 46 = U006C U004C U0040
! n = 'n' 'N' '~'
keycode 57 = U006E U004E U007E

! Command and control keys !

! Left alt !
keycode 64 = Mode_switch
! Left command key !
keycode 133 = Alt_L Meta_L

! Right command key!
keycode 134 = ISO_Level3_Shift Multi_key
keycode 105 = Control_R
! Right alt !
keycode 108 = Menu

add mod4 = Alt_L
! add mod1 = Alt_L Meta_L !
add mod1 = Alt_R Meta_R
add control = Control_L
add control = Control_R
! add control = Alt_L Meta_L !

This gets me all the keys where I expect them. Except, well, copy & paste is done with left control, not left command - left command is used as the modifier key in i3.

The next step was configuring i3 to make the function keys work as I expect. The following dependencies (apt names) are used for that:

The wpctl (“WirePlumber Control”) command should be installed automatically, because Debian Bookworm uses Pipewire by default. If not, pactl will probably also work.

# ~/.config/i3/config
# Set up keyboard maps, monitor settings (see below) and black background
exec --no-startup-id xmodmap ~/.Xmodmap
exec --no-startup-id xrdb -merge ~/.Xresources
exec --no-startup-id xsetroot -solid "#000000"

set $mod Mod4

# [...]

# The ~/.config/rofi/rofi.rasi file just sets up the gruvbox color scheme
bindsym XF86LaunchA exec --no-startup-id "rofi -config ~/.config/rofi/rofi.rasi -modi Workspaces:/usr/share/doc/rofi/examples/i3_switch_workspaces.sh -show Workspaces"
bindsym XF86LaunchB exec --no-startup-id "rofi -config ~/.config/rofi/rofi.rasi -modi drun,run -show drun"
bindsym XF86KbdBrightnessDown exec --no-startup-id light -s sysfs/leds/smc::kbd_backlight -U 5
bindsym XF86KbdBrightnessUp exec --no-startup-id light -s sysfs/leds/smc::kbd_backlight -A 5
bindsym Mode_switch+Shift+XF86KbdBrightnessDown exec --no-startup-id light -s sysfs/leds/smc::kbd_backlight -U 1
bindsym Mode_switch+Shift+XF86KbdBrightnessUp exec --no-startup-id light -s sysfs/leds/smc::kbd_backlight -A 1
bindsym XF86MonBrightnessDown exec --no-startup-id light -U 5
bindsym XF86MonBrightnessUp exec --no-startup-id light -A 5
bindsym Mode_switch+Shift+XF86MonBrightnessDown exec --no-startup-id light -U 1
bindsym Mode_switch+Shift+XF86MonBrightnessUp exec --no-startup-id light -A 1
bindsym XF86AudioLowerVolume exec --no-startup-id wpctl set-volume @DEFAULT_AUDIO_SINK@ 10%- --limit 1.0 && $refresh_i3status
bindsym XF86AudioRaiseVolume exec --no-startup-id wpctl set-volume @DEFAULT_AUDIO_SINK@ 10%+ --limit 1.0 && $refresh_i3status
bindsym Mode_switch+Shift+XF86AudioLowerVolume exec --no-startup-id wpctl set-volume @DEFAULT_AUDIO_SINK@ 1%- --limit 1.0 && $refresh_i3status
bindsym Mode_switch+Shift+XF86AudioRaiseVolume exec --no-startup-id wpctl set-volume @DEFAULT_AUDIO_SINK@ 1%+ --limit 1.0 && $refresh_i3status
bindsym XF86AudioMute exec --no-startup-id wpctl set-mute @DEFAULT_AUDIO_SINK@ toggle && $refresh_i3status
bindsym $mod+Shift+minus move scratchpad
bindsym $mod+minus scratchpad show
# Thanks to Joe for suggesting `maim` to me.
bindsym Mod4+Control+Shift+3 exec --no-startup-id maim | xclip -selection clipboard -t image/png
bindsym Mod4+Control+Shift+4 exec --no-startup-id maim --select | xclip -selection clipboard -t image/png
bindsym $mod+Tab workspace back_and_forth
bindsym Mod4+Control+q exec --no-startup-id loginctl lock-session

My ~/.Xresources - I don’t fully remember why I have this:

Xft.dpi: 96

! These might also be useful depending on your monitor and personal preference:
Xft.autohint: 0
Xft.lcdfilter: lcddefault
Xft.hintstyle: hintfull
Xft.hinting: 1
Xft.antialias: 1
Xft.rgba: rgb

Finally, to fix some other strange interactions with the option keys, I also had to set the following in /etc/X11/xorg.conf.d/90-apple-badmap.conf:

Section "InputClass"
    Identifier "keyboard defaults"
    MatchIsKeyboard "on"

    Option "XKbOptions" "apple:badmap"
EndSection

A reboot to properly apply these (an X server or just display manager restart would also do the trick) and onwards.

Configuring graphics

Add the following to /etc/X11/xorg.conf.d/20-intel-graphics.conf:

Section "Module"
    Load "dri3"
EndSection

Section "Device"
    Identifier  "Intel Graphics"
    Driver      "intel"
    Option      "DRI"   "3"
EndSection

Configuring the touchpad

Well, I knew I was not going to get quite the native macOS experience with the touchpad. But palm detection turned out to be pretty essential feature. I added the following to /etc/X11/xorg.conf.d/90-touchpad.conf:

Section "InputClass"
    Identifier "touchpad"
    Driver "synaptics"
    MatchIsTouchpad "on"
    # MatchIsPointer "on"
    # MatchDevicePath "/dev/input/event*"
    Option "PalmDetect" "true"
    # Inverted scrolling
    Option "VertScrollDelta" "-10"
    # Tap to click
    Option "TapButton1" "1"
    # Double tap to right click
    Option "TapButton2" "2"
    Option "LockedDrags" "true"
    Option "LockedDragTimeout" "1000"
    # Option "Device" "/dev/input/mice"
    # Option "Protocol" "Auto"
    # Option "ZAxisMapping" "5 4"
EndSection

You may need to install xserver-xorg-input-synaptics.

I could not quite get triple-click drag to work. Instead, the LockedDrags and LockedDragTimeout options allow you to do similar things with leftclick. This is also useful for taking screenshots that you resize easily. Outside of that, vertical scrolling may not work like you expect it from MacOS.

Making it feel like a laptop

At this point I realize that my laptop seems oddly warm. It’s normal the laptop heats up after a while, but this was unusual. What’s going on?

Turns out you need to install some fan drivers. I installed macfanctld and was met with jet engine sounds. Great!

I went through a couple more iterations of fan control scripts and whatnot. I ended up using thermald with the following configuration:

<?xml version="1.0"?>
<ThermalConfiguration>
	<Platform>
		<Name>MacBook Air</Name>
		<ProductName>*</ProductName>
		<!-- <Preference>QUIET</Preference> -->
		<Preference>PERFORMANCE</Preference>
		<ThermalSensors>
			<ThermalSensor>
			  <SensorType>x86_pkg_temp</SensorType>
				<AsyncCapable>1</AsyncCapable>
			</ThermalSensor>
			<ThermalSensor>
				<SensorType>BAT0</SensorType>
				<AsyncCapable>0</AsyncCapable>
			</ThermalSensor>
		</ThermalSensors>
		<ThermalZones>
			<ThermalZone>
				<Type>CPU</Type>
				<TripPoints>
					<TripPoint>
						<SensorType>x86_pkg_temp</SensorType>
						<Temperature>70000</Temperature>
						<type>passive</type>
						<ControlType>SEQUENTIAL</ControlType>
						<CoolingDevice>
							<influence>50</influence>
							<type>rapl_controller</type>
							<samplingPeriod>10</samplingPeriod>
						</CoolingDevice>
						<CoolingDevice>
							<influence>45</influence>
							<type>intel_powerclamp</type>
							<samplingPeriod>5</samplingPeriod>
						</CoolingDevice>
						<CoolingDevice>
							<influence>40</influence>
							<type>intel_pstate</type>
							<samplingPeriod>10</samplingPeriod>
						</CoolingDevice>
						<CoolingDevice>
							<influence>30</influence>
							<type>cpufreq</type>
							<samplingPeriod>5</samplingPeriod>
						</CoolingDevice>
						<CoolingDevice>
							<influence>20</influence>
							<type>Processor</type>
							<samplingPeriod>5</samplingPeriod>
						</CoolingDevice>
					</TripPoint>
					<TripPoint>
						<SensorType>x86_pkg_temp</SensorType>
						<Temperature>70000</Temperature>
						<type>active</type>
						<ControlType>SEQUENTIAL</ControlType>
						<CoolingDevice>
							<type>_MacFan</type>
						</CoolingDevice>
					</TripPoint>
					<!-- Final effort - we should not reach >= 98°C, regardless of config -->
					<TripPoint>
						<SensorType>x86_pkg_temp</SensorType>
						<Temperature>98000</Temperature>
						<type>max</type>
						<ControlType>SEQUENTIAL</ControlType>
						<CoolingDevice>
							<influence>50</influence>
							<type>rapl_controller</type>
							<samplingPeriod>10</samplingPeriod>
						</CoolingDevice>
						<CoolingDevice>
							<influence>45</influence>
							<type>intel_powerclamp</type>
							<samplingPeriod>5</samplingPeriod>
						</CoolingDevice>
						<CoolingDevice>
							<influence>40</influence>
							<type>intel_pstate</type>
							<samplingPeriod>10</samplingPeriod>
						</CoolingDevice>
						<CoolingDevice>
							<influence>30</influence>
							<type>cpufreq</type>
							<samplingPeriod>5</samplingPeriod>
						</CoolingDevice>
						<CoolingDevice>
							<influence>20</influence>
							<type>Processor</type>
							<samplingPeriod>5</samplingPeriod>
						</CoolingDevice>
					</TripPoint>
				</TripPoints>
			</ThermalZone>
		</ThermalZones>
		<CoolingDevices>
			<CoolingDevice>
				<Type>_MacFan</Type>
				<Path>/sys/devices/platform/applesmc.768/fan1_min</Path>
				<MinState><!-- Please find this yourself, I don't want to heat-death
				   your machine :-) --></MinState>
				<IncDecStep>250</IncDecStep>
				<ReadBack>0</ReadBack>
				<MaxState>6500</MaxState>
				<DebouncePeriod>100</DebouncePeriod>
			</CoolingDevice>
			<CoolingDevice>
				<Type>intel_powerclamp</Type>
				<MinState>0</MinState>
				<IncDecStep>10</IncDecStep>
				<ReadBack>0</ReadBack>
				<MaxState>50</MaxState>
				<DebouncePeriod>5</DebouncePeriod>
			</CoolingDevice>
		</CoolingDevices>
	</Platform>
</ThermalConfiguration>

From 70°C this will start throttling your CPU when you run it in QUIET mode. In PERFORMANCE mode, it will spin up the fans from 70°C. In either case, unless you put your Mac into your oven, thermald will make sure you never reach 98°C by throttling you into nirvana.

Please note that it still appears my laptop seems to build up more case heat (noticeable on the palms) than on macOS, despite the presumed CPU sensor reporting way lower values than on macOS. The laptop will get just as hot as it gets when running macOS, that is to say, it doubles as a hand warmer. I would be happy if anybody has input on what your thermald configuration (or similar tools) is.

I also installed laptop-mode-tools for some battery savings and higher filesystem commit intervals strongly raising the risk of data loss on power loss backup strategy validation, and cpufrequtils, which I configured as follows:

ENABLE=true
GOVERNOR="conservative"
MAX_SPEED="25000000"

With the ondemand governor and the default MAX_SPEED of 2.8 GHz, the laptop would heat up way too fast.

Finally, time for a break! I eagerly closed the lid, opened it back up - lockscreen was there, great - entered my password aaaand nothing happened. Well, that would have been too easy, apparently the filesystem is unmounted and I need to configure that to not do it.

Turns out to make that work it seemed that I needed a swapfile, so I created one swapfile with btrfs fi mkswapfile, added it to the Kernel command line, updated grub, rebooted, and … you guessed it, rescue shell!

Anger

I like to think that I roughly know my way with the command line. The grub rescue shell is not one of those parts. My system does not boot up and the only message I got was something like “Basic command-line editing supported.”, in a window that is roughly a quarter of the screen, with the built-in pager apparently disabled by default. I tried help here, help there, why did it show 7 different hd devices, well, following the standard procedure I remembered and found online did not help me boot into anything (it only found the macOS disk, not mine).

I read over the commands and found the hello command! Packed with enthusiasm about finding something that seemed a little more helpful, I figured:

I hope whoever packaged that into the rescue shell is having a good day. No, seriously: within the dark caves of despair and pain that is the rescue shell, this managed to lighten the mood a bit. I guess I did get the friendly greeting after all.

Regrets

Why did I configure a swapfile? Why couldn’t I use the other sleeping methods the (super helpful!) Arch Wiki suggested? Why did I think that having sleep on a laptop was a useful feature? Why does my laptop need 20 seconds to start in the first place? Why do I still use computers? Why do I work in IT?

Pain

My entire system, even with the Mac hotkeys and even cooling was working fine, and now I could not even figure out why I’m getting a grub rescue, let alone boot from it. No matter which online resource I followed, it just wouldn’t boot. My beloved new installation, with painful hours spent configuring basic keyboard keys to work. Why would it do this to me? What have I done to deserve this?

Guiding Voices

At this point I delved more into the Arch Wiki, longing for alternatives to grub. Launched into a deep mental state somewhere between West Germany and Angela Merkel’s Doctor Thesis - this is mandatory reading over here - my number one thought was to get rid of it at all costs, because if I was ever going to launch into a grub rescue shell again, I would not be very happy about it.

It turns out that my Mac supports something called “UEFI”. I read about it and I still don’t really know what UEFI is. The gist seems to be that UEFI seems to be a simpler way to do whatever I was doing.

Talking to the higher beings

I can understand why people don’t like systemd, personally I like it a lot. It works well, it does almost everything I need it to do, and a huge plus for me is also: it has great command-line tools. I installed systemd-boot and asked it to install itself. It took some tweaking back and forth - disk space on the UEFI partition is limited - but after a while I got it to install itself. I also had to remove grub from there.

And the best part - it has a nice command line tool, bootctl, which cleanly outputs information on the boot configuration. I was ready.

Liberation

It took me roughly 20 hours from adding the swapfile before I had a working system again. The nice part is that I got the behaviour I wanted for seemingly free as well: when booting without doing anything, the Mac will boot into macOS by default, and when holding down the option key, it will enter boot drive selection and select Linux by default.

While having the opportunity I also moved my BTRFS root directory to a subdirectory to allow for cleaner snapshots later. Without hesitation I figured I should boot into whatever systemd-boot’s equivalent “rescue shell” is and see what happens. I was taken into initramfs which felt a lot like a small Linux installation.

Please note that I still do not have a swapfile. Something an old colleague once told me was “do not wake sleeping dogs”.

The reverse mirror

I don’t want to talk badly about grub. I run it on another dual-booted box and it runs perfectly fine there. For literally any case that is not “entered the rescue shell”, grub has always worked perfectly fine for me. And that aside, I am also relatively sure that grub supports an absolute beast of hardware configurations. That on its own is a huge effort, and I know I can rely on it elsewhere. Just the rescue shell is always a big, big source of head scratching…

Final setup

The rest of setting up the laptop was really just setting up standard Linux stuff. I went with btrbk for daily filesystem snapshots and borg via borgmatic for weekly backups, and would highly recommend those. Also, I discovered btop, and, wow, that is one beautiful application. The rest is up to you!

Remaining wishes

The following things are things that are still missing. If you have managed to read until here (thank you!) and have an idea on them, I would be happy for your input :-)

reply via email