Installing Endeavour OS Cassini 22-12 (Arch Linux) on a 1st generation Microsoft Surface Book
Zoey Smith
Mindful Leadership in Information Technology | I build teams of people that uplift each other and brighten your day.
Overview
I recently dug up an old Microsoft Surface Book (1st generation) that I purchased when it first came out. It served me well for a good few years until I replaced it with a beast of a laptop from?Eluktronics.
Knowing that it does not meet the minimum hardware requirements for Windows 11 (it does not have a supported processor), I debated whether to donate it or recycle it. Then I realized I could address my recent craving for novelty by attempting to install Linux on a Microsoft Surface device!
This post is a document of my efforts and the results.
Surface Book 1st Gen (not with performance base):
– Intel Core i7-6600U at 2.6 GHz
– 16GB RAM
– NVIDIA GeForce dGPU with 1GB GDDR5 RAM
– 512GB SSD
– Windows 10 Pro 22H2
Preparation
First, I download the?Endeavour OS installer ISO, “EndeavourOS_Cassini_22_12.iso”, the release from 2022-12-17.
NOTE: There is a new version – “Endeavouros_Cassini_neo_22_12.iso” – that fixes the hibernation issue I deal with later in this post.
I download?Rufus?3.21 portable, and create a GPT/UEFI USB drive from the Endeavour OS ISO (stay tuned for a post on this process!)
The Surface Pro has a fresh install of Windows 10 Pro on it. I use “left shift + restart” to get into the advanced boot options and boot into the UEFI interface. I make sure that I have USB booting enabled and that the USB device is the first boot option. For now, I disable Secure Boot.
Wi-Fi
I boot to the Endeavour OS installer and select the “EndeavourOS x86_64 UEFI Default” option from the boot menu.
Oh dear, when I try to connect to my Wi-Fi, there are “no network devices available“.
lsmod | grep wifi
mwifiex_pcie 57344 0
mwifiex 339968 1 mwifiex_pcie
cfg80211 1118208 1 mwifiex
Well, there are Wi-Fi driver modules loaded. I wonder why they aren’t working?
sudo dmesg | grep wifi
[ 12.765820] mwifiex_pcie 0000:03:00.0: quirk reset_d3cold enabled
[ 12.765912] mwifiex_pcie 0000:03:00.0: enabling device (0000 -> 0002)
[ 12.766148] mwifiex_pcie: PCI memory map Virt0: 000000001377f2c1 PCI memory map Virt2: 0000000045d2b741
[ 12.893228] mwifiex_pcie 0000:03:00.0: Direct firmware load for mrvl/pcie8897_uapsta.bin failed with error -2
[ 12.893249] mwifiex_pcie 0000:03:00.0: Failed to get firmware mrvl/pcie8897_uapsta.bin
[ 12.893258] mwifiex_pcie 0000:03:00.0: info: _mwifiex_fw_dpc: unregister device
[ 12.893722] mwifiex_pcie 0000:03:00.0: performing cancel_work_sync()...
[ 12.893731] mwifiex_pcie 0000:03:00.0: cancel_work_sync() done
It looks like there is some missing firmware. A quick Google search turned up?https://forum.endeavouros.com/t/no-wifi-after-fresh-install/27346?which led to?https://archlinux.org/packages/core/any/linux-firmware-marvell/.
It is also mentioned here:?https://github.com/linux-surface/linux-surface/wiki/Installation-and-Setup
Note to self: read the documentation!
But how do I install a missing package with no network access? My only available USB network adapter has a USB-C connection, and the Surface Book only has USB-A ports.
Well, Linux package managers usually have an option to install a pre-downloaded package – in this case, “sudo pacman -S /path/to/file“.
However, how do I pre-download the package? I know, let’s check the Arch Linux wiki for?a relevant article?(a.k.a. read the documentation!)
Luckily for this project, I have another laptop already running Endeavour OS Cassini 22-12. I grab it, create a new directory, “/home/username/surface-book“, and within it, run “pacman -Sp linux-firmware-marvell > linux-firmware-marvell.txt” and then “wget -P ./ -i linux-firmware-marvell.txt“
I copy the resulting zst file to a USB drive and plug it into the Surface Book. I run “sudo pacman -U /run/media/liveuser/untitled/linux-firmware-marvell20230117.7e4f0ed-1-any.pkg.tar.zst“
OK, that worked. Time for me to reload the kernel modules.
sudo modprobe -r --remove-holders mwifiex_pcie
lsmod | grep wifi
Yay! All gone!
sudo modprobe mwifiex_pcie
lsmod | grep wifi
mwifiex_pcie 57344 0
mwifiex 339968 1 mwifiex_pcie
cfg80211 1118208 1 mwifiex
Yay! All back, with no errors! And the Wi-Fi manager in the system tray shows available networks! Time to connect to my Wi-Fi, and on to the next steps.
Installation
I connect to my Wi-Fi network and step through the graphical installer’s prompts:
OS Initial Configuration
First things first, I go to “Display Configuration“, and set “Global Scale” to 200%, and double the height of the system panel from 44 to 88. Then I reboot so the scaling changes take effect.
Great, now I can clearly see what is on the screen without a magnifying glass.
From the Welcome Menu, I complete the following options:
1. Update Mirrors (Arch)
2. Update Mirrors (Endeavour OS)
Now I add the linux-surface Arch repository and install the necessary packages, following these instructions:?https://github.com/linux-surface/linux-surface/wiki/Installation-and-Setup
I want to make the new linux-surface kernel the default one, so I do the following:
sudo nano -w /efi/loader/loader.conf
Then I edit the first line, which starts with “default“, to append “surface.conf” so that the first line is “default 59f43974206a420689993bf7896abada*surface.conf” (where “59f43974206a420689993bf7896abada” is the OS instance ID).
You will only have multiple OS instance IDs if you are multi-booting OSs.
Time to restart and see if it all worked!
The restart is successful, and everything is looking pretty good so far! I do a quick run of “yay” just to see if there are any further updates pending, but nothing pops up.
I also run “Package cleanup configuration” from the “Welcome” app, and then hit the “Don't show me anymore” button so that the app no longer launches every time I log in.
Enabling the NVIDIA dGPU
The next step is to get the NVIDIA GeForce dGPU functioning. I have seen, anecdotally, that it can be tricky. From reading the comments in?linux-surface issue #93?on Github, it seems that the original Surface Book’s NVIDIA dGPU was controlled by a custom embedded controller, which presents some difficulties. Thankfully, the community surrounding that project discovered a workaround and entered it into?the appropriate wiki article.
Following that article, I append the following commands to “/etc/kernel/cmdline“:
nouveau.modeset=0 pci=realloc nvidia-drm.modeset=1 pcie_port_pm=off pcie_aspm=off
Then I run “sudo reinstall-kernels” and reboot.
The reboot succeeds, and I create a file on my desktop, “nvidia.sh“, with the following contents:
#!/bin/sh
echo 1 | tee /sys/bus/platform/devices/MSHW0041:00/dgpu_power
echo 1 > /sys/bus/pci/rescan
setpci -H1 -s 01:00.0 6a.b=81
setpci -H1 -s 01:00.0 4.w=0407
echo 1 > /sys/bus/pci/rescan
setpci -s 01:00.0 4.w
setpci -s 01:00.0 6a.b
After a quick “chmod +x?~/Desktop/nvidia.sh” I execute the file “sudo?~/Desktop/nvidia.sh” and then check lspci and lsmod. They show that the system detects “NVIDIA Corporation GM108M [GeForce 940MX]” and has loaded the nouveau kernel module.
I want to use the drivers provided by NVIDIA, so I run “sudo pacman -S nvidia-inst” and then “nvidia-inst“. And then, a while later, after it finishes, I reboot.
Time to check for success. I log in and run “lspci” – there is no sign of the NVIDIA dGPU. I run “lsmod” – there is no sign of the nouveau or the nvidia drivers. This is the expected behavior.
Then I run “sudo ~/Desktop/nvidia.sh“. After that, running “lspci” and “lsmod” shows the NVIDIA dGPU the nvidia_modset driver, respectively.
Alas, when I execute glmark2 it runs on the Intel iGPU. I get glmark2 to run on the NVIDIA dGPU with the following command, referenced in?a surface-linux wiki article?on Github:
__NV_PRIME_RENDER_OFFLOAD=1 __GLX_VENDOR_LIBRARY_NAME=nvidia glmark2
However, sometimes the performance is less than that of the Intel 520 iGPU. Specifically, the iGPU performs better in glmark2 and the dGPU performs better in unigine-superposition.
NVIDIA dGPU glmark2 score: 619
GL_VENDOR: NVIDIA Corporation
GL_RENDERER: NVIDIA GeForce GPU/PCIe/SSE2
GL_VERSION: 4.6 NVIDIA 525.89.02
Surface Config: buf=32 r=8 g=8 b=8 a=8 depth=24 stencil=0 samples=0
Surface Size: 800x600 windowed
NVIDIA dGPU unigine-superposition score: 2588
领英推荐
Version: 1.1
Graphics API: OpenGL
Resolution: 1280x720
Fullscreen: enabled
Shaders: Low
Textures: Low
DOF: Disabled
Motion Blur: Disabled
CPU: Intel Core i7-6600U @ 2600MHz (Stock) / 3400 MHz (Actual)
RAM: 16 GB
GPU: Intel HD Graphics 520 16 GB (Skylake GT2)
Driver: i915 OpenGL 4.6
OS: EndeavourOS kernel 6.1.11-arch1-1-surface
Intel HD 520 iGPU glmark2 score: 1523
GL_VENDOR: Intel
GL_RENDERER: Mesa Intel(R) HD Graphics 520 (SKL GT2)
GL_VERSION: 4.6 (Compatibility Profile) Mesa 22.3.4
Surface Config: buf=32 r=8 g=8 b=8 a=8 depth=24 stencil=0 samples=0
Surface Size: 800x600 windowed
Intel HD 520 iGPU unigine-superposition score: 1865
Version: 1.1
Graphics API: OpenGL
Resolution: 1280x720
Fullscreen: enabled
Shaders: Low
Textures: Low
DOF: Disabled
Motion Blur: Disabled
CPU: Intel Core i7-6600U @ 2600MHz (Stock) / 3400 MHz (Actual)
RAM: 16 GB
GPU: Intel HD Graphics 520 16 GB (Skylake GT2)
GPU: NVIDIA GeForce GPU 1 GB (GM108M)
Driver: i915 OpenGL 4.6
OS: EndeavourOS kernel 6.1.11-arch1-1-surface
Now that the NVIDIA dGPU seems to work the way I want it to, on to the next step!
Enabling System Hibernation
When I installed the OS, I chose “Swap (with hibernate)”, so I expect S4 hibernate-to-disk to work out of the box.
I test hibernation using the following guide:?https://www.kernel.org/doc/html/latest/power/basic-pm-debugging.html
# echo reboot > /sys/power/disk
# echo disk > /sys/power/state
Alas, the system boots back to a login screen instead of resuming to my existing session with the command prompt that issued the above commands.
Time to try another test:
# echo platform > /sys/power/disk
# echo disk > /sys/power/state
Alas, I see the same behavior as before.
Time to try one more test:
# echo shutdown > /sys/power/disk
# echo disk > /sys/power/state
Alas, I see the same behavior as before.
A quick bit of Google searching turns up the following:
I edit “/etc/dracut.conf.d/eos-defaults.conf” and append the line:
add_dracutmodules+=" resume "
Then I run:
sudo dracut-rebuild
However, I get the following error when the linux-surface kernel was being rebuilt:
cp: error writing '/efi/<OS instance ID>/...' no space left on device
This means that having three kernels and initramfs files (the LTS kernel, the standard Arch kernel, and the suface-linux kernel) is taking up almost 1000 MB of space (the size of the “efi” partition created by the installer). So I need to enlarge the “efi” partition.
I edit “/efi/loader/loader.conf” so that the LTS kernel (the only one that dracut-rebuild successfully completed) is the default, and reboot back into the EndeavourOS installer. Then I run “sudo gparted” and resize the EFI partition from 1000MB to 2000MB. (Why not? It’s on a 512GB drive.) I get a warning about the fact that moving the system’s kernel files around might render the system unbootable. However, I am not using a boot method that references a specific section of the disk, so this should not be an issue.
The system successfully boots with the LTS kernel. I re-run “sudo dracut-rebuild” to finish rebuilding all the kernels and initramfs files, and then edit “/efi/loader/loader.conf” so that the linux-surface kernel is once again the default. I reboot again, and the system successfully boots with the linux-surface kernel.
Back to hibernate testing!
# echo reboot > /sys/power/disk
# echo disk > /sys/power/state
It does not work. Now to try:
# echo platform > /sys/power/disk
# echo disk > /sys/power/state
It does not work. Now to try:
# echo shutdown > /sys/power/disk
# echo disk > /sys/power/state
It does not work. Hmmm.
I remember seeing this mentioned:
For any who uses?dracut?and systemd-boot
if you want to edit boot entry options (add resume uuid, resume-offset), then edit this file: /etc/kernel/cmdline
Then run: sudo reinstall-kernels
I don’t think that dracut changed “/etc/kernel/cmdline” (especially given that the last modified date is yesterday, from when I was enabling the NVIDIA dGPU).
I’m not sure what “reinstall-kernels” would do that “dracut-rebuild” does not (even after some cursory research). Regardless, I run “sudo reinstall-kernels” and then reboot.
And now to test hibernation again:
# echo reboot > /sys/power/disk
# echo disk > /sys/power/state
Huzzah! It works! If anyone can explain why “sudo reinstall-kernels” was required, please feel free to send me a message.
Now to test what happens when the system hibernates and resumes,?after?the NVIDIA dGPU is enabled.
lspci | grep NVIDIA
lsmod | grep nvidia
Good, nothing detected.
sudo ~/Desktop/nvidia.sh
lspci | grep NVIDIA
01:00.0 3D controller: NVIDIA Corporation GM108M [GeForce 940MX] (rev a2)
lsmod | grep nvidia
nvidia_drm 73728 1
nvidia_modeset 1515520 1 nvidia_drm
nvidia 61444096 1 nvidia_modeset
video 65536 2 i915,nvidia_modeset
Good, the script ran and now the dGPU is active and the drivers are loaded.
su
echo reboot > /sys/power/disk
echo disk > /sys/power/state
Good, the system hibernated and resumed back to the lock screen of my existing session.
lspci | grep NVIDIA
01:00.0 3D controller: NVIDIA Corporation GM108M [GeForce 940MX] (rev a2)
lsmod | grep nvidia
nvidia_drm 73728 1
nvidia_modeset 1515520 1 nvidia_drm
nvidia 61444096 1 nvidia_modeset
video 65536 2 i915,nvidia_modeset
The card and the modules are still there, but when I try to run glmark2, it cannot find the dGPU:
__NV_PRIME_RENDER_OFFLOAD=1 __GLX_VENDOR_LIBRARY_NAME=nvidia glmark2
X Error of failed request: BadValue (integer parameter out of range for operation)
Major opcode of failed request: 152 (GLX)
Minor opcode of failed request: 24 (X_GLXCreateNewContext)
Value in failed request: 0x0
Serial number of failed request: 32
Current serial number in output stream: 33
I dig a little deeper:
sudo dmesg | grep nvidia
[ 38.594830] nvidia-nvlink: Nvlink Core is being initialized, major device number 508
[ 38.751285] nvidia-modeset: Loading NVIDIA Kernel Mode Setting Driver for UNIX platforms 525.89.02 Wed Feb 1 23:09:40 UTC 2023
[ 38.753925] [drm] [nvidia-drm] [GPU ID 0x00000100] Loading driver
[ 39.173829] [drm] Initialized nvidia-drm 0.0.0 20160202 for 0000:01:00.0 on minor 1
[ 105.225225] nvidia 0000:01:00.0: Unable to change power state from unknown to D0, device inaccessible
I try running “sudo ~/Desktop/nvidia.sh” again. The script hangs and proves to be very difficult to terminate (even “kill -9” doesn’t work, and I end up having to force a reboot). I think perhaps the NVIDIA dGPU is more trouble than it is worth, at least for my purposes.
The Webcam
Now to test the webcam!
sudo pacman -S kamoso
Womp womp, the webcam doesn’t appear to work; I get no image, just a black box.
Wow, this appears to be quite a tough issue –?https://github.com/linux-surface/linux-surface/issues/91?– so I’ll leave this alone for now.
The Rest
Secure Boot: not yet working.
Keyboard hotkeys: all seem functional.
Keyboard backlight: works.
Audio: functional (both input and output).
References (not in order):