Table of Contents

What is here?

Some notes regarding running Linux on the Nintendo Switch. First things first, thanks fail0verflow. I did just play a bit on the software side, but fail0verflow was one of the 2 groups who found the way to run Linux, and established all the chain to make this possible, including providing the patched Linux kernel. After the initial release, most contributions/enhancements come from the switchroot project. You guys rock!

20190707_124709_nintendo_switchc.jpg

Nintendo Switch Hardware Specs

Graphical Demos/Games

What are people actually using the Nintendo switch/Linux setups for? Some use it like a workstation on HDMI output, with the option of undocking and using it on the road. Of course, databases, libreoffice and so on work. Some more graphical ideas to try out:

20190616_184612_switch2s.jpg

What Linux flavour works (not)?

distro wlan xorg plainxorg acceleratedaudiocuda
Lakka (retro game focused) yes yes yes yes no
L4T Ubuntu yes yes yes yes yes
Fedora 28/29/30/31/32yes yes yes yes no
Android 8.1/LineageOS 15.1 yes n/a n/a yes no

Boot order for Linux@switch

  1. As first step, the switch needs to be booted into RCM, an early debug mode. For doing this for the first time, pins on the switch need to be crossed, for example using this RCM jig device. You can create a Jig yourself, or buy them online. When booting to RCM succeeded, you can use Hekate to set 'autorcm', the switch will then after resets automatically enter RCM instead of booting Nintendos Horizon.
  2. With the switch being in RCM, it will do nothing, the screen will be black. At this state, payload code to be run needs to be supplied via USB. The code can be supplied from a Linux box via Fusee launcher.
  3. What to run as first level payload?
    • Hekate could be used, offered via Fusee launcher. Hekate can then
      • configure 'autorcm'
      • it can verify the switches battery charge state
      • it can run Linux kernels from microsd-cards in the switch (using coreboot)
      • it can poweroff the switch
    • Shovel2 can be used to upload Linux kernels via USB, coreboot is also here used. Userland to be booted could be on the microsd card, or supplied via NFS.
    • ArgonNX has more eyecandy than hekate.
  4. Which kernel to run? There are basically 2 levels of kernel patches.
    • mid-2018 kernel, using the initial patches from Fail0verflow: no audio, problems charging the switch, resets when load is high (compile things only with 'make -j1'), wlan only works after one reset of the system. Yet, on this kernel one can see Linux console output directly on the screen after booting. This kernel is great for initially running for example Fedora30, Opensuse or Debian userlands: because you can use the screen to get debugging output, until you get the userland appropriately configured to log into wlan after booting. From that state on you can work via SSH.
    • mid-2019 kernel from the L4T distro with nvidia patches: audio, charging and wlan work, no resets under high load. But no initial console output. Lakka and the L4T distro use this kernel.
  5. Which userland to run? L4T and Lakka come with kernel/userland together. The kernels with switch patches can be used on other userlands though, like Fedora30, SuSE, Debian.

L4T Ubuntu 3.2 installation

This is also the first step for installing Fedora, L4T will be used to bootstrap. The L4T instructions, for reference, are here. My notes for installing L4T version 3.0 are now here.

# Insert your microsd-card, 32+ GB
# Find the microsd-card device, i.e. /dev/mmcblk0p1

parted
# create make a single partition, 16GB size. No GPT, partition table.
mkfs.vfat /dev/mmcblk0p1

# Now extract hekate, copy contents
cd ~/Downloads
mkdir hekate_ctcaer_5.5.7_Nyx_1.0.4 && cd hekate_ctcaer_5.5.7_Nyx_1.0.4
unzip ../hekate_ctcaer_5.5.7_Nyx_1.0.4.zip
mount /dev/mmcblk0p1 /mnt/tmp
tar cf - bootloader|(cd /mnt/tmp && tar xfv -)

# now insert the card into the Nintendo switch.
# We will extract the archive into the newly created partition
mount /dev/mmcblk0p1 /mpt/tmp
cd /mnt/tmp
7z x /<path-to>/switchroot-ubuntu-3.2.0-2020-10-05.7z
cd ..; umount tmp
# We will extract the update
mount /dev/mmcblk0p1 /mpt/tmp
cd /mnt/tmp
rm -rf bootloader/ini/L4T-bionic.ini switchroot/ubuntu
7z x /<path-to>/switchroot-ubuntu-3.3.0-update_only-2021-04-08.7z
cd ..; umount tmp

Installing Fedora 32

These steps need an already installed L4T on the microsd card: partitions 1 (vfat) and 2 (ext4, L4T) have been set up. After increasing the second partition to 16GB, I create a third partition and install Fedora 32 there. I use a Fedora31/x86_64 system here as helper.

For installing the Fedora32 aarch64 userland for the switch, I use Fedora-Server-32_Beta-1.2.aarch64.raw.xz. The images for Fedora31/32 do not come with wpa_supplicant, we need to install it manually. Networkmanager-wifi is part of the images.

### The following steps are done on the Fedora31/x86_64 system,
### with the microsd card with L4T installed.
fdisk /dev/mmcblk0
# n / p / 3 / <return> / <return>
mkfs.ext4 /dev/mmcblk0p3
mount /dev/mmcblk0p3 /mnt/tmp3
cd /mnt/tmp3

xz -d Fedora-Server-32_Beta-1.2.aarch64.raw.xz
kpartx -av Fedora-Server-32_Beta-1.2.aarch64.raw
vgscan 
vgchange -ay
mount /dev/fedora/root /mnt/tmp
cd /mnt/tmp
tar cfp - *|pv|(cd /mnt/tmp3 && tar xf -)

# Now, as the copy finished, preparations
cd /mnt/tmp3
echo '/dev/mmcblk0p2 / ext4 defaults 0 0' >etc/fstab

# set a root password, for example one from your /etc/shadow
vi etc/shadow
# Now either add your ssh-pubkey to root/.ssh/authorized_keys,
# or modify etc/ssh/sshd_config to accept password root logins.

echo switch.local >etc/hostname
echo '127.0.0.1   switch.local switch' >>etc/hosts

# disable console on tty1.  We have no input anyway, and can
# see debugmessages that way - via HDMI
mv etc/systemd/system/getty.target.wants/getty@tty1.service root/

### prepare wlan
# I have my wlan already setup on my Fedora30 host system,
# so I can simply copy over the files
cp /etc/sysconfig/network-scripts/keys-mynetwork \
   /etc/sysconfig/network-scripts/ifcfg-mynetwork \
    etc/sysconfig/network-scripts/
    
# Our Fedora-server image lacks package wpa_supplicant.
# The package has to be fetched, and installed.
# Either
# - chroot into the Fedora partition, and "rpm -i <file>"
#   (needs to be done from an aarch64, for example the L4T)
# - or use
#     cd /mnt/tmp2
# rpm2cpio <rpmfile> | cpio --extract --verbose --make-directories --preserve

# We have 2 options:
# a) create an own inifile for Fedora on the boot partition,
#    then we have full control over kernel parameters, but
#    we need to write a new boot.scr file
# b) Or, we just exchange partitions 2 and 3 in the
#    partition table.  Doing that for now.

# twist partitions 2 and 3 with sfdisk
sfdisk /dev/mmcblk0 -l >/root/ptable_normal
cp /root/ptable_normal /root/ptable_twisted
vi /root/ptable_twisted
# change mmcblk0p2 -> mmcblk0p3, and 
# the original mmcblk0p3 -> mmcblk0p2
sfdisk /dev/mmcblk0 --force </root/ptable_twisted

# mount the L4T partition
mount /dev/mmcblk0p2 /mnt/tmp2

echo brcmfmac >etc/modules-load.d/brcmfmac.conf
cp /mnt/tmp2/lib/firmware/brcm/brcmfmac4356-pcie.txt lib/firmware/brcm/
cp -r /mnt/tmp2/lib/modules/4.9.140+/ lib/modules

# Now we can boot the system.  After the reboot, wlan should
# work.  Login via ssh into root, run dnf upgrade,
# remove and install packages
dnf remove iscsi-initiator-utils-iscsiuio iscsi-initiator-utils \
  clevis-luks atmel-firmware
dnf install -y langpacks-ja upower screen
dnf update -y

for i in auditd smartd pcscd ModemManager multipathd mdmonitor \
         dmraid-activation initial-setup lvm2-monitor zram-swap \
         plymouth-start lm_sensors udisks2 ; do
    systemctl disable $i
done

### tuning, seen in the L4T scripts
cat >/etc/rc.local<<EOT
#!/usr/bin/bash
echo 2048 > /sys/block/mmcblk0/queue/read_ahead_kb
echo 0 > "/proc/sys/vm/lazy_vfree_pages"
EOT
chmod +x /etc/rc.local
/etc/rc.local
ln -s /etc/rc.local /etc/rc.d/rc.local

### if you want rpmfusion
dnf install \
  https://download1.rpmfusion.org/free/fedora/rpmfusion-free-release-$(rpm -E %fedora).noarch.rpm \
  https://download1.rpmfusion.org/nonfree/fedora/rpmfusion-nonfree-release-$(rpm -E %fedora).noarch.rpm

Fedora Xorg/LXDM autologin

ssh root@switch

# Install the basic environment.
dnf -y groupinstall 'Basic Desktop' 'LXDE Desktop'

# Install
# - xvkbd, a virtual keyboard.  'florence' is a further option.
# - synergy, so I can use mouse/keyboard of my linux box
# - f29-backgrounds, because they are awesome :)
# - mplayer: media player, blueman: connect bt headsets etc.
dnf install -y xvkbd synergy f29-backgrounds-base mplayer blueman

# Now copying drivers from an L4T installation on partition2:
mount /dev/mmcblk0p3 /mnt/tmp3/
cd /mnt/tmp3

cp usr/lib/xorg/modules/drivers/nvidia_drv.so \
  /usr/lib64/xorg/modules/drivers/
cp usr/lib/xorg/modules/extensions/libglxserver_nvidia.so \
  /usr/lib64/xorg/modules/extensions/
cp usr/bin/dock-hotplug /usr/bin

mkdir -p /usr/lib/aarch64-linux-gnu
cp -r usr/lib/aarch64-linux-gnu/tegra-egl \
  /usr/lib/aarch64-linux-gnu
ln -s /usr/lib/aarch64-linux-gnu/tegra-egl/ld.so.conf \
  /etc/ld.so.conf.d/aarch64-linux-gnu_EGL.conf
cp -r usr/lib/aarch64-linux-gnu/tegra \
  /usr/lib/aarch64-linux-gnu
ln -s /usr/lib/aarch64-linux-gnu/tegra/ld.so.conf \
  /etc/ld.so.conf.d/aarch64-linux-gnu_GL.conf

ldconfig

cp -r lib/firmware/tegra21x lib/firmware/gm20b /lib/firmware/
cp lib/firmware/bcm4354.hcd /lib/firmware/

# special xorg config
cat >/etc/X11/xorg.conf<<EOT
Section "Module"
    Disable     "dri"
    SubSection  "extmod"
        Option  "omit xfree86-dga"
    EndSubSection
EndSection

Section "Device"
    Identifier  "Tegra0"
    Driver      "nvidia"
    # Allow X server to be started even if no display devices are connected.
    Option      "AllowEmptyInitialConfiguration" "true"
    Option      "Rotate" "CW"
EndSection

Section "InputClass"
    Identifier "evdev touchscreen catchall"
    MatchIsTouchscreen "on"
    MatchDevicePath "/dev/input/event*"
    Driver "evdev"
    Option "InvertX" "no"
    Option "InvertY" "no"
    Option "SwapAxes" "no"
    Option "Calibration" "0 1279 0 719"
EndSection

Section "Monitor"
    Identifier "DFP-0"
    Option "Rotate" "left"
EndSection 
EOT

# MYUSER="lxde"
MYUSER="chris"
useradd -m $MYUSER

# We create a default autostart, including running synergy
# client, trying to connect to a synergy-server on 192.168.0.2
mkdir -p /home/$MYUSER/.config/lxsession/LXDE
cat >/home/$MYUSER/.config/lxsession/LXDE/autostart<<EOT
@lxpanel --profile LXDE
@pcmanfm --desktop --profile LXDE
@synergyc 192.168.0.2
EOT
chown -R $MYUSER /home/$MYUSER/.config

# configure autologin
vi /etc/lxdm/lxdm.conf
# autologin=username  # <- insert the username which you use
# session=/usr/bin/startlxde

# We need to ensure that our user later appears in the 'who' output.
# That is important for the /usr/bin/dock-hotplug script, for
# switching screens.
echo 'sessreg -a -l $DISPLAY -x /etc/X11/xdm/Xservers $USER &' >> \
  /etc/lxdm/PostLogin
echo 'sessreg -d -l $DISPLAY -x /etc/X11/xdm/Xservers $USER &' >> \
  /etc/lxdm/PostLogout

rm -f /etc/systemd/system/display-manager.service
systemctl enable --now lxdm
systemctl set-default graphical.target

Fedora sound

# Now copying drivers from an L4T installation on partition2.
mount /dev/mmcblk0p2 /mnt/tmp2/
cd /mnt/tmp2

dnf install -y alsa-ucm alsa-plugins-pulseaudio alsa-utils \
  pulseaudio pulseaudio-module-x11 pulseaudio-utils pavucontrol
cp -r usr/share/alsa/ucm/tegra-snd-t210ref-mobile-rt565x/ /usr/share/alsa/ucm/
cp usr/share/alsa/cards/tegra-hda.conf /usr/share/alsa/cards

cp -r opt/nvidia/ /opt/
cp usr/sbin/nv* usr/sbin/brcm* /usr/sbin/
cp -r etc/nv* /etc/
cp etc/systemd/nv* /etc/systemd
cp etc/systemd/system/nv*service /etc/systemd/system/

cp etc/asound.conf.* /etc/

# ensure that /etc/asound.conf is linked to
# the appropriate file, depending on whether we are docked or not
cat >/etc/udev/rules.d/92-dp-switch.rules<<EOT
SUBSYSTEM!="switch", GOTO="dp_end"
KERNEL!="dp", GOTO="dp_end"
ATTRS{state}=="1", TEST=="/proc/asound/tegrahda", RUN+="/bin/ln -sf /etc/asound.conf.tegrahda /etc/asound.conf"
ATTRS{state}=="1", TEST=="/usr/bin/dock-hotplug", RUN+="/usr/bin/dock-hotplug"
ATTRS{state}=="0", TEST=="/usr/bin/dock-hotplug", RUN+="/usr/bin/dock-hotplug"
ATTRS{state}=="0", TEST=="/proc/asound/tegrasndt210ref", RUN+="/bin/ln -sf /etc/asound.conf.tegrasndt210ref /etc/asound.conf"
LABEL="dp_end"
EOT

# reboot

# for playing via alsa, this has to be active:
alsamixer # I2S1 Mux -> ADMAIF1

# we should now be able to start X, and have sound output:
mplayer <something> 

# When the switch is docked, the screen should switch to HDMI
# output automatically, via profile switch in /usr/bin/dock-hotplug .
# The profile can also be selected via 'pavucontrol' from X.

# also for output testing: 
# speaker-test –channels 2 –rate 48000 –device hw:0,3​

Fedora/switch FAQ

RHEL8/aarch64

# I used the cloud image as base, mounted and copied
# the contents to the second partition of a microsdcard
guestmount -a /tmp/image.qcow2 -m /dev/sda2 /mnt/tmpimage
mount /dev/mmcblk0p2 /mnt/tmp2/
cd /mnt/tmpimage
tar cfp - *|pv|(cd /mnt/tmp2 && tar xfp -)
guestunmount /mnt

cd /mnt/tmp2
# have to add contents wpa_supplicant and
# NetworkManager-wifi packages
rpm2cpio ..

# move cloudinit
mkdir etc/systemd/system/tmp
mv etc/systemd/system/multi-user.target.wants/cl* etc/systemd/system/tmp

Generic hints

Touchscreen detection not in sync

The touchscreen driver will work in horizontal mode by default, but the xorg screen might come up in vertical mode. Above xorgs monitor definition should rotate the monitor for all Xorg windowmanagers, but the rotation could instead also be done at the windowmanager level. Rotating the screen in LXDE:

mkdir -p ~/.config/autostart
cat <<EOT >~/.config/autostart/.desktop 
[Desktop Entry] 
Type=rotater
Exec=xrandr --output DSI-0 --rotate left
EOT

Booting is slow

Use systemd to illustrate what takes time at boot, and consider to disable services. Also comparing the graph from Fedora30 with the one from L4T helps. This helped me tremendously getting boottime down, after turning the switch on, it takes now 29sec until the usable LXDE screen of Fedora30. I supply the kernel via usb instead of reading from disk, that is further potential for optimization, takes 5.2sec until kernel/initrd etc. are transferred and the kernel is booted.

console debugging

With newer kernels, there is no console output any more. After booting the kernel, this switch screen just flashes up. Possible alternate debugging with console over usb, the L4T initrd has usb-debug libraries:

How to run the synergy server?

You are running the synergy client for mouse/keyboard sharing, in the LXDE autostart. But what to run to get the server part? I use this:

[chris@電脳 ~]$ cat .synergy.conf 
section: screens
        dennou.local:
        switch.local:
end
section: links
        dennou.local:
                up = switch.local
        switch.local:
                down = dennou.local

end
[chris@電脳 ~]$ synergys
[chris@電脳 ~]$ 

microsd card benchmark

Do this in the switch, as the reader is quite good, better than the one in my thinkpad.

pv /dev/mmcblk0 >/dev/null
Toshiba 32GB 100MB/sec : 80MB/sec
LAZOS 32GB             : 65MB/sec

dealing with filesystem corruptions

I saw ext4 corruptions on a 32GB card, on both partitions. Using the RPM checksums to verify:

for i in $(rpm -qa|sort); do echo "### $i"; rpm -V $i; done  >>logg
egrep -B1 -v '^#|\.uuid' logg2|grep -c '^#'
egrep -B1 -v '^#|\.uuid' logg2|grep '^#'

overclocking

# enable overclocking
echo 1 >/sys/kernel/tegra_cpufreq/overclock
# set maximum frequency
echo 2091000 >/sys/devices/system/cpu/cpufreq/policy0/scaling_max_freq
# cet current frequency
cat /sys/devices/system/cpu/cpufreq/policy0/cpuinfo_cur_freq 
# set governor
echo performance >/sys/devices/system/cpu/cpufreq/policy0/scaling_governor