Skip to content

sh0rez

Linux on the M1, with GPU acceleration

M12 min read

Apple recently released new MacBook Pros using their incredible M1 (Pro|Max) chips.

Those chips are really impressive. However, MacBooks ship with macOS, an operating system several of us consider much less impressive.

Let's run Linux on them!

Table of Contents

MacBook Pro M1 showing glxgears and neofetch applications running under
Linux

MacBook Pro 2021 showing glxgears and neofetch running in virtualized ArchLinux.

Options

Natively

The Asahi Linux project is working really hard on bringing a first class Linux experience to the M1 hardware.

Looking on their most recent progress report reveals it has come a lot of way already.

The lack of accelerated graphics and overall driver situation, especially on the laptop models, sadly disqualify it as a daily driver for the time being.

Virtualized

QEMU to the rescue! More specifically, UTM provides a version of QEMU specifically tailored towards M1 Macs (along with fancy GUI).

Most importantly, it ships a virtual graphics mode virtio-ramfb-gl, enabling accelerated graphics on most modern Linux out of the box.

Setup

We are going to use the following:

DeviceMacBook Pro 2021
CPUM1 Pro
RAM16GB
Disk1TB
OS (Host)macOS 12.0
OS (VM)ArchLinux ARM
HypervisorUTM v2.4.0

Preparing ArchLinux ARM

Unlike the official ArchLinux project, ArchLinux ARM does not come with a USB installer. Instead, we are going to create a bootable disk ourselves.

Requirements: fdisk, kpartx, qemu-img, bsdtar. Suggested to use any other Linux machine for the following.

Installation procedure based on this Gist. The following is very concise and intended for experienced Arch users.

$: normal user, #: root user

0. Download

In some suitable folder run:

1$ curl -LO http://os.archlinuxarm.org/os/ArchLinuxARM-aarch64-latest.tar.gz
1. Disk

First, partition and mount a new image:

1$ qemu-img create alarm.img 32G
2
3$ fdisk alarm.img
4 then create the following:
5 - gpt partition table
6 - 200MB+ EFI partition
7 - root partition (rest of disk)
8
9# kpartx -av alarm.img
10# mkfs.vfat /dev/mapper/loopXp1
11# mkfs.ext4 /dev/mapper/loopXp2
12
13# mount /dev/mapper/loopXp2 /mnt
14# mkdir /mnt/boot
15# mount /dev/mapper/loopXp1 /mnt/boot
2. OS

Next, extract ArchLinux ARM onto it:

1# bsdtar -xpf ArchLinuxARM-aarch64-latest.tar.gz -C /mnt

Correctly set disk UUIDs in /etc/fstab (use blkid to get yours):

/etc/fstab
1/dev/disk/by-uuid/XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX / ext4 defaults 0 0
2/dev/disk/by-uuid/XXXX-XXXX /boot vfat defaults 0 0

Because we cannot set EFIVARS in advance, we need to use a fallback /boot/startup.nsh (fill in your ext4 UUID):

/boot/startup.nsh
1Image root=UUID=XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX rw initrd=\initramfs-linux.img
3. Finish

First, unmount:

1# umount -R /mnt
2# kpartx -d alarm.img

Then, convert to .qcow2 (much smaller):

1$ qemu-img convert -O qcow2 alarm.img alarm.qcow2

Transfer that .qcow2 to your M1 Mac then.

VM creation

Continue now on your M1 Mac.

Open UTM, create a new VM (from scratch) and set the following:

System
Architectureaarch64
CPUCortex A72
Cores8 (# performance cores)
UEFIyes
Drives

Import Drive, then select your alarm.qcow2

Display
TypeFull Graphics
Hardwarevirtio-ramfb-gl
Fit-to-Screenyes
Retina Modeyes

UTM supports other hardware accelerated GPU types apart from virtio-ramfb-gl, but in my testing they either did not boot at all, or had worse performance.

Installation

Power up the VM, it should drop into the UEFI shell, but boot Linux shortly after.

Login as root:root. Linux is basically installed by this point, but some setup is still left:

1# pacman-key --init
2# pacman-key --populate archlinuxarm
3# pacman -Syu

To avoid relying on the the UEFI startup.nsh fallback, setup a bootloader using EFIVARS:

1# efibootmgr --disk /dev/vda --part 1 --create --label "Arch Linux ARM" --loader /Image --unicode 'root=UUID=XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX rw initrd=\initramfs-linux.img' --verbose

Above uses the EFISTUB functionality of the Linux kernel. Alternatives include systemd-boot, grub2, etc. and work perfectly fine as well.

By this point, ArchLinux ARM is installed. As always, consider following the General Recommendations and setup the system to your liking.

i3 desktop

Obviously we need a desktop enviroment to actually use this. I prefer i3, but choose whatever you like:

1# pacman -S i3 xorg-server xorg-init
2$ startx

There are no special drivers required, X11 works out of the box.

A bit of .xinitrc is needed though:

~/.xinitrc
1# load system wide xinit
2if [ -d /etc/X11/xinit/xinitrc.d ] ; then
3 for f in /etc/X11/xinit/xinitrc.d/?*.sh ; do
4 [ -x "$f" ] && . "$f"
5 done
6 unset f
7fi
8
9# screen layout
10modename="3024x1910_60.00"
11xrandr --newmode $modename 493.75 3024 3264 3592 4160 1910 1913 1923 1979 -hsync +vsync
12xrandr --addmode Virtual-1 $modename
13xrandr --output Virtual-1 --mode $modename
14
15# HiDPI
16xrdb -merge ~/.Xresources
17
18# start i3
19exec i3

HiDPI

The MacBook Pro 2021 uses a display with a whopping 3024x1964 pixels and 254 dpi. To avoid everything being incredibly small, the following appears to work well:

~/.Xresources
1Xft.dpi = 192

1export GDK_SCALE=2
2export GDK_DPI_SCALE=0.5
3export QT_AUTO_SCREEN_SCALE_FACTOR=1

Performance

It's fast! Like, really. Consider these Geekbench scores:

Native (macOS)LinuxCost %
Single-Core17711652-6.7%
Multi-Core124559064-27.2%

But what about the GPU? I did not find any benchmarking tool that runs on both operating systems, but I compared the fps of the WebGL Aquarium:

# FishNative (macOS)Linux
10006060
50006049
100006026
150005617
200004214
250003011
300002610

As expected for a virtualized GPU, performance decreases quite a lot.

However, we did not come for 3D intensive tasks, but for some OpenGL to smoothly browse the web and watch the one or another YouTube video.

And it does that perfectly fine at 60fps, without any noticeable slowdown!