Installing Endeavour OS Cassini 22-12 (Arch Linux) on a 1st generation Microsoft Surface Book

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:

  1. Start the Installer
  2. Select the Online option.
  3. Select the relevant localizations. (In my case, all “US”.)
  4. Select Plasma KDE for my desktop environment (given the very high DPI of the Surface Book’s display, I need good scaling capabilities.)
  5. Package selection:
    • “LTS kernel in addition” (because if anything goes wrong with the latest kernel, I can switch to the LTS one)
    • “Printing Support” in case I want to print anything.
  6. The default boot manager is Systemd-boot. (I have no reason to choose something else.)
  7. Select “Erase Disk” (be careful here, make sure you have the correct disk selected!)
    • “Swap (with hibernate)”
    • “btrfs” file system (in my opinion, this file system is better for SSDs than ext4, and I will accept the risks of using a less mature file system)
  8. Create the primary user account and password.
    • I select “use the same password for the administrator account” because I am lazy.
  9. Check that the summary is accurate, and click “Install” and then “Install Now”
  10. Wait for the install process to complete. (I read a bit of “What If? 2” by Randall Munroe, a book I highly recommend, both for its curiosity and its humor.)
  11. Check “restart now” and then click “done”
  12. Don’t forget to remove the USB stick after the shutdown so that the system boots to the internal SSD.

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:
https://forum.endeavouros.com/t/endeavouros-cassini-with-dracut-does-not-configure-hibernation/35120/5
https://forum.endeavouros.com/t/resume-hook-missing-even-if-installing-selecting-hibernate-to-swap/36257/6

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

https://forum.endeavouros.com/t/endeavouros-cassini-with-dracut-does-not-configure-hibernation/35120/7

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 functional (including unmarked ones such as “Fn + Del” and “Fn + Backspace” for screen brightness up and down, respectively)

Keyboard backlight: functional

Audio: functional (both input and output).

Touchscreen: functional

Stylus: not yet working

References (not in order):

  1. https://learn.microsoft.com/en-us/windows-hardware/design/minimum/supported/windows-11-supported-intel-processors
  2. https://support.microsoft.com/en-us/surface/surface-book-1st-gen-specs-and-features-b2c7cf05-1144-da66-c4c8-dba0be246040
  3. https://github.com/linux-surface/linux-surface
  4. https://github.com/linux-surface/linux-surface/wiki/Installation-and-Setup
  5. https://www.lorenzobettini.it/2022/12/linux-endeavouros-cassini-review/
  6. https://forum.endeavouros.com/t/no-wifi-after-fresh-install/27346
  7. https://archlinux.org/packages/core/any/linux-firmware-marvell/
  8. https://github.com/linux-surface/linux-surface/wiki/Installation-and-Setup
  9. https://wiki.archlinux.org/title/offline_installation_of_packages
  10. https://github.com/linux-surface/linux-surface/issues/93
  11. https://github.com/linux-surface/linux-surface/wiki/Surface-Book
  12. https://www.kernel.org/doc/html/latest/power/basic-pm-debugging.html
  13. https://forum.endeavouros.com/t/endeavouros-cassini-with-dracut-does-not-configure-hibernation/35120/5
  14. https://forum.endeavouros.com/t/resume-hook-missing-even-if-installing-selecting-hibernate-to-swap/36257/6
  15. https://github.com/linux-surface/linux-surface/issues/91
  16. https://discovery.endeavouros.com/installation/systemd-boot/2022/12/
  17. https://discovery.endeavouros.com/nvidia/new-nvidia-driver-installer-nvidia-inst/2022/03/
  18. https://askubuntu.com/questions/1052658/how-can-i-run-glmark2-on-the-dedicated-gpu
  19. https://wiki.archlinux.org/title/benchmarking


Cross posted at: https://www.linkedin.com/pulse/installing-endeavour-os-cassini-22-12-1st-generation-microsoft-smith/?trackingId=AF2YzWV2RFO9FFLv1aI3Kw%3D%3D