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!
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:
distro | wlan | xorg plain | xorg accelerated | audio | cuda |
---|---|---|---|---|---|
Lakka (retro game focused) | yes | yes | yes | yes | no |
L4T Ubuntu | yes | yes | yes | yes | yes |
Fedora 28/29/30/31/32 | yes | yes | yes | yes | no |
Android 8.1/LineageOS 15.1 | yes | n/a | n/a | yes | no |
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
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
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
# 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
# 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
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
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.
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:
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@電脳 ~]$
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
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 '^#'
# 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