├── .gitignore ├── .gitmodules ├── 00_build.sh ├── README.md └── boot_linux.sh /.gitignore: -------------------------------------------------------------------------------- 1 | build.log 2 | exploit.log 3 | product/* 4 | rootfs 5 | sd.img 6 | sd.img.gz 7 | vendor/* 8 | -------------------------------------------------------------------------------- /.gitmodules: -------------------------------------------------------------------------------- 1 | [submodule "coreboot"] 2 | path = coreboot 3 | url = https://github.com/fail0verflow/switch-coreboot.git 4 | [submodule "shofel2"] 5 | path = shofel2 6 | url = https://github.com/cmsj/shofel2.git 7 | [submodule "u-boot"] 8 | path = u-boot 9 | url = https://github.com/cmsj/switch-u-boot.git 10 | branch = switch 11 | [submodule "linux"] 12 | path = linux 13 | url = https://github.com/tardyp/switch-linux.git 14 | branch = rebase_4.17 15 | [submodule "imx_usb_loader"] 16 | path = imx_usb_loader 17 | url = https://github.com/boundarydevices/imx_usb_loader.git 18 | [submodule "aarch64_toolchain"] 19 | path = aarch64_toolchain 20 | url = https://github.com/cmsj/aarch64_toolchain_docker 21 | [submodule "ubuntu_builder"] 22 | path = ubuntu_builder 23 | url = https://github.com/cmsj/nintendo-switch-ubuntu-builder.git 24 | -------------------------------------------------------------------------------- /00_build.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | #set -x 3 | set -eE 4 | set -u 5 | 6 | # Global variables 7 | CROSS_COMPILE=aarch64-linux-gnu- 8 | export CROSS_COMPILE 9 | 10 | ROOTDIR=/source 11 | BUILDLOG="${ROOTDIR}/build.log" 12 | echo "" > "${BUILDLOG}" 13 | 14 | function build_failed() { 15 | echo "ERROR: Build failed. Check ${BUILDLOG} for full details." 16 | } 17 | 18 | trap build_failed ERR 19 | 20 | NPROC=$(grep -c ^processor /proc/cpuinfo) 21 | export NPROC 22 | 23 | # Pixel C factory image metadata 24 | SHA256_RYU_OPM=8f7df21829368e87123f55f8954f8b8edb52c0f77cb4a504c783dad7637dd8f4 25 | SHA256_SMAUG=ed121ba1f5dbbf756f2b0b559fef97b2def88afa9217916686aa88c8c2760ce9 26 | URL_RYU_OPM=https://dl.google.com/dl/android/aosp/ryu-opm1.171019.026-factory-8f7df218.zip 27 | ZIPNAME_RYU_OPM=ryu-opm1.171019.026-factory-8f7df218.zip 28 | DIRNAME_RYU_OPM=ryu-opm1.171019.026 29 | IMGNAME_SMAUG=bootloader-dragon-google_smaug.7900.97.0.img 30 | 31 | TSFMT='[%Y-%m-%d %H:%M:%S]' 32 | 33 | mkdir -p "${ROOTDIR}/vendor" 34 | mkdir -p "${ROOTDIR}/product" 35 | 36 | # Helper functions 37 | make() { 38 | /usr/bin/make -j"${NPROC}" "$@" | ts "${TSFMT}" >> "${BUILDLOG}" 2>&1 39 | } 40 | 41 | sha256() { 42 | sha256sum "$1" | awk '{ print $1 }' 43 | } 44 | 45 | copy_products() { 46 | targetdir="${ROOTDIR}/product/${1}" ; shift 47 | mkdir -p "${targetdir}" 48 | for product in "$@" ; do 49 | if ! [ -e "${product}" ]; then 50 | build_failed 51 | exit 1 52 | fi 53 | cp -v "${product}" "${targetdir}" | ts "${TSFMT}" >> "${BUILDLOG}" 54 | done 55 | } 56 | 57 | mypushd() { 58 | pushd "$1" >> "${BUILDLOG}" 2>&1 59 | } 60 | 61 | mypopd() { 62 | popd >> "${BUILDLOG}" 2>&1 63 | } 64 | 65 | myecho() { 66 | echo "$@" | ts "${TSFMT}" | tee -a "${BUILDLOG}" 67 | } 68 | 69 | # Get ourselves in the right place 70 | cd "${ROOTDIR}" 71 | 72 | # Remove everything except the vendor folder, since that is pretty well guarded, and pointless to re-download every time 73 | reset_build() { 74 | echo "Resetting build environment..." 75 | git submodule deinit . 76 | rm -rf product/* 77 | rm -rf rootfs/* 78 | git submodule update --init --recursive 79 | } 80 | 81 | fetch_tegra_ram_trainer() { 82 | myecho "Checking Tegra RAM trainer blob..." 83 | mypushd "${ROOTDIR}/vendor" 84 | if ! [ -f tegra_mtc.bin ]; then 85 | if ! [ -f "${ZIPNAME_RYU_OPM}" ] || [ "$(sha256 "${ZIPNAME_RYU_OPM}")" != "${SHA256_RYU_OPM}" ]; then 86 | myecho "Fetching Tegra RAM trainer blob..." 87 | rm -rf "${DIRNAME_RYU_OPM}" "${ZIPNAME_RYU_OPM}" 88 | wget "${URL_RYU_OPM}" -a "${BUILDLOG}" 2>&1 | ts "${TSFMT}" >> "${BUILDLOG}" 89 | fi 90 | if ! [ -f "${DIRNAME_RYU_OPM}/${IMGNAME_SMAUG}" ] || [ "$(sha256 "${DIRNAME_RYU_OPM}/${IMGNAME_SMAUG}")" != "${SHA256_SMAUG}" ]; then 91 | myecho "Unpacking Tegra RAM trainer blob..." 92 | rm -rf "${DIRNAME_RYU_OPM}" 93 | unzip "${ZIPNAME_RYU_OPM}" | ts "${TSFMT}" >> "${BUILDLOG}" 94 | fi 95 | fi 96 | mypopd 97 | } 98 | 99 | build_exploit() { 100 | myecho "Building shofel2 exploit..." 101 | mypushd "${ROOTDIR}/shofel2/exploit" 102 | make 103 | copy_products exploit shofel2.py cbfs.bin 104 | mypopd 105 | mypushd "${ROOTDIR}/shofel2/usb_loader" 106 | copy_products exploit switch.scr switch.conf imx_usb.conf 107 | mypopd 108 | copy_products exploit boot_linux.sh 109 | tar cvzf "${ROOTDIR}/product/exploit.tar.gz" -C "${ROOTDIR}/product/" exploit >> "${BUILDLOG}" 2>&1 110 | } 111 | 112 | build_uboot() { 113 | myecho "Building u-boot..." 114 | mypushd "${ROOTDIR}/u-boot" 115 | make nintendo-switch_defconfig 116 | make 117 | copy_products exploit tools/mkimage 118 | mypopd 119 | } 120 | 121 | build_coreboot() { 122 | myecho "Building coreboot..." 123 | mypushd "${ROOTDIR}/coreboot" 124 | make distclean # coreboot doesn't seem to take kindly to being rebuilt without a good clean first 125 | make nintendo_switch_defconfig 126 | make iasl 127 | mypushd util/cbfstool 128 | make cbfstool 129 | mypopd 130 | 131 | if ! [ -f ../vendor/tegra_mtc.bin ]; then 132 | myecho " Extracting Tegra RAM trainer blob from Pixel C factory restore image..." 133 | ./util/cbfstool/cbfstool "../vendor/${DIRNAME_RYU_OPM}/${IMGNAME_SMAUG}" extract -n fallback/tegra_mtc -f tegra_mtc.bin | ts "${TSFMT}" >> "${BUILDLOG}" 134 | cp tegra_mtc.bin ../vendor 135 | fi 136 | 137 | if ! [ -f tegra_mtc.bin ]; then 138 | cp ../vendor/tegra_mtc.bin . 139 | fi 140 | 141 | if [ "$(sha256 tegra_mtc.bin)" != "edb32e3f9ed15b55e780e8a01ef927a3b8a1f25b34a6f95467041d8953777d21" ]; then 142 | myecho "ERROR: tegra_mtc.bin does not match stored SHA256 sum" 143 | exit 1 144 | fi 145 | 146 | make 147 | copy_products exploit build/coreboot.rom 148 | mypopd 149 | } 150 | 151 | build_imx_loader() { 152 | myecho "Building imx loader..." 153 | mypushd "${ROOTDIR}/imx_usb_loader" 154 | make 155 | copy_products exploit imx_usb 156 | mypopd 157 | } 158 | 159 | build_linux() { 160 | myecho "Building Linux..." 161 | mypushd "${ROOTDIR}/linux" 162 | export ARCH=arm64 163 | make nintendo-switch_defconfig 164 | make 165 | copy_products boot arch/arm64/boot/Image.gz arch/arm64/boot/dts/nvidia/tegra210-nintendo-switch.dtb 166 | mypopd 167 | } 168 | 169 | build_rootfs() { 170 | grep -q binfmt_misc /proc/modules 171 | if [ $? != 0 ]; then 172 | myecho "ERROR: You need to modprobe binfmt_misc before running this build." 173 | exit 1 174 | fi 175 | myecho "Building Ubuntu filesystem (this will take 20+ minutes)..." 176 | mkdir -p "${ROOTDIR}/rootfs" 177 | mypushd "${ROOTDIR}/rootfs" 178 | # Build the ubuntu rootfs 179 | ../ubuntu_builder/build-image.sh "${ROOTDIR}/rootfs/chroot" rootfs.tar 2>&1 | ts "${TSFMT}" >> "${BUILDLOG}" 180 | # Inject our /boot (kernel and DTB) 181 | myecho "Injecting kernel/dtb into Ubuntu filesystem..." 182 | tar -rvf rootfs.tar -C "${ROOTDIR}/product/" ./boot/ 2>&1 | ts "${TSFMT}" >> "${BUILDLOG}" 183 | myecho "Compressing Ubuntu filesystem..." 184 | gzip rootfs.tar 185 | # Not using copy_products here just because this is a ~1GB tarball that's rebuilt on every run, no point having two around 186 | mv -v "rootfs.tar.gz" "${ROOTDIR}/product/" | ts "${TSFMT}" >> "${BUILDLOG}" 187 | mypopd 188 | } 189 | 190 | build_sd_image() { 191 | myecho "Building SD card image..." 192 | # 3125MB is taken from the Raspbian image builder 193 | dd if=/dev/zero of=sd.img bs=1M count=3125 >> "${BUILDLOG}" 2>&1 194 | # Partition the blank image 195 | FDISK_SCRIPT='o 196 | n 197 | p 198 | 1 199 | 200 | +100M 201 | t 202 | b 203 | n 204 | p 205 | 2 206 | 207 | 208 | w 209 | ' 210 | echo "${FDISK_SCRIPT}" | fdisk sd.img >> "${BUILDLOG}" 211 | 212 | # Make the partitions available via loopback, format them, unpack the rootfs 213 | DEVNODE="$(losetup --partscan --show --find sd.img)" 214 | mkfs.vfat "${DEVNODE}p1" 2>&1 | ts "${TSFMT}" >> "${BUILDLOG}" 215 | mkfs.ext4 "${DEVNODE}p2" 2>&1 | ts "${TSFMT}" >> "${BUILDLOG}" 216 | mkdir -p /mnt/rootfs 217 | mount "${DEVNODE}p2" /mnt/rootfs 218 | tar xvf "${ROOTDIR}/product/rootfs.tar.gz" -C /mnt/rootfs 2>&1 | ts "${TSFMT}" >> "${BUILDLOG}" 219 | umount /mnt/rootfs 220 | losetup -d "${DEVNODE}" 221 | gzip -v -9 sd.img 2>&1 | ts "${TSFMT}" >> "${BUILDLOG}" 222 | mv -v sd.img.gz "${ROOTDIR}/product/" | ts "${TSFMT}" >> "${BUILDLOG}" 223 | } 224 | 225 | build_all() { 226 | reset_build 227 | fetch_tegra_ram_trainer 228 | build_exploit 229 | build_uboot 230 | build_coreboot 231 | build_imx_loader 232 | build_linux 233 | build_rootfs 234 | build_sd_image 235 | } 236 | 237 | build_all 238 | myecho "Finished." 239 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | ### Unofficial Ubuntu builder for Nintendo Switch 2 | 3 | This repo allows you to build the exploit, bootloaders, kernel and root filesystem for running Ubuntu on a Nintendo Switch. 4 | 5 | Note that this is an unofficial project and is not connected to, or endorsed by, Ubuntu. All trademarks are deeply loved. 6 | 7 | Also note that the [build toolchain](https://hub.docker.com/r/cmsj/aarch64_toolchain/) is based around Docker, so you should have Docker installed. 8 | 9 | ### Preparation 10 | 11 | ``` 12 | docker pull cmsj/aarch64_toolchain 13 | git clone https://github.com/cmsj/switch_linux_kit 14 | cd switch_linux_kit 15 | ``` 16 | 17 | ### Compiling 18 | 19 | ``` 20 | modprobe binfmt_misc 21 | docker run --privileged -ti --rm -v/dev:/dev -v$(pwd):/source cmsj/aarch64_toolchain bash 00_build.sh 22 | ``` 23 | 24 | The build script is pretty modular, so with some simple edits you could choose to build just the exploit chain, bootloader, kernel, rootfs, etc. if you so desire. 25 | 26 | So far this has only been tested on a CentOS 7 host, please report issues/success with other distros. 27 | 28 | ### Micro SD Card Preparation 29 | 30 | You need a microSD card for Linux, which you can prepare with these steps: 31 | 32 | * Use a tool that can write disk images (e.g. dd or Etch) 33 | * Unzip `sd.img.gz` and write it to the SD card 34 | 35 | Notes: 36 | 37 | * The Linux partition contains both the kernel and device tree (the `.dtb` file) in `/boot/` and the exploit chain produced by this builder expects to find them there (ie you can't use a f0f exploit chain obtained elsewhere that is expecting to send `Image.gz` and the `.dtb` file over USB) 38 | 39 | ### Running 40 | 41 | Attach your Switch to USB, trigger the hardware exploit (ie short pin 10 of the right joycon slot, to ground), and run: 42 | 43 | ``` 44 | cd products/exploit/ 45 | sudo ./boot_linux.sh 46 | ``` 47 | 48 | (If you want to run the exploit from a separate host to your build host, you'll find `exploit.tar.gz` in `products/` with all of the scripts, executables and data that are needed) 49 | -------------------------------------------------------------------------------- /boot_linux.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | set -e 3 | set -u 4 | 5 | LOGFILE="exploit.log" 6 | echo "" > "${LOGFILE}" 7 | 8 | echo "Exploiting Switch recovery mode and loading coreboot..." 9 | sudo ./shofel2.py cbfs.bin coreboot.rom >> "${LOGFILE}" 2>&1 10 | #echo "Waiting for Switch to run coreboot..." 11 | #sleep 5 # Give the Switch a few seconds to execute coreboot 12 | #if ! [ -f switch.scr.img ] ; then 13 | # echo "Creating u-boot script image..." 14 | # ./mkimage -A arm64 -T script -C none -n "boot.scr" -d switch.scr switch.scr.img >> "${LOGFILE}" 2>&1 15 | #fi 16 | #echo "Sending u-boot script..." 17 | #sudo ./imx_usb -c . >> "${LOGFILE}" 2>&1 18 | echo "Switch should boot Linux momentarily." 19 | --------------------------------------------------------------------------------