« Back

Softmodding the Playstation 2 + Linux

25 Sep 2021 - faintshadows

I sure like to endure pain and suffering with computers, don’t I.


This is NOT a tutorial for installing Linux on your PlayStation 2 I did NOT succeed in getting a modern distribution running. This is just documenting my experience getting there.

I have had a fat Playstation 2 for a few years now, and never did much with it, as I only got it for a few games, and once I got bored of those, back onto the shelf it went.

That changed when I was linked to a GitHub repo of some dude who was… Porting modern (5.x) Linux kernels to the MIPS R5900 that’s found in the PS2??? Alright, count me in.

I didn’t want to buy a memory card that had FreeMCBoot on it already, and I needed a hard drive adapter anyways, good thing the PS2 Network Adapter is pretty cheap, at the time of purchase it was under 20$US shipped. I also ordered a SATA upgrade PCB, but the network adapter came early, and as of me writing this now, the SATA PCB hasn’t arrived.

Because the SATA PCB isn’t here, we’re using good ‘ol IDE. I don’t have a USB to IDE adapter, so I pulled out my Dell Precision 610 to do all my PS2 goodness, at least where the hard drive is concerned.

I grabbed the FreeHDBoot newbie kit from here. Thankfully, the included tools run on Windows XP. But the Dell runs Win2K… Well, no issue, I have plenty of XP CDs, I’ll just install XP and call it a day.

It’s never that easy.

I boot the XP CD, it gets past the first stage of the install fine, no issues. It reboots to continue the install, but it can’t find the CD drive! Odd, XP has drivers for this SCSI controller. No biggie, I’ll just boot a live Linux CD and copy I386 over to the hard drive and it’ll finish. Can’t boot anything. Great! The CD drive died! Luckily I had some other SCSI CD-ROM drive, swap that in and the install completes without a hitch.

Back to the PS2. I have selected two hard drives, a 20 and 40GB Samsung SpinPoint. I format the 40GB one, and restore the FHDB image to it. Plug it into the PS2 aaaaaand… Nothing. Well, ok maybe I messed something up. Format the drive again, restore the image, nothing. Fine, this is why I got the other hard drive. Rinse and repeat, formatted and imaged. Plug it into the PS2, the TV syncs to an output, and then poof, the PS2 loses power. Just completely. But after leaving it unplugged for a few minutes it comes back, but it’ll only boot without the hard drive installed. Odd, maybe the power supply is going out. I grab a 3rd hard drive to make sure, a 20GB Seagate from my iMac G3, and it boots again. Good sign. I format and image it, and wow! I’m greeted with FHDB!


I download some ISOs off the ‘net and after using that 40GB drive as a secondary storage drive for the Dell, started putting some images on there. And they work! Mostly, some act funny or don’t respond to the controller.

After playing with that for a few hours, I decided it’s time to move on to why I really got this adapter. Linux.

KernelLoader is a project that allows you to boot a Linux kernel without needing the special PS2 Linux discs Sony provided with the Linux Kit. They have a backup of what a fresh install of said Linux Kit would have, along with some other goodies, namely a live DVD. If a PS2 was able to be exploited with FreeDVDBoot, it would just boot up, but for those without, just boot the KernelLoader ELF and it’ll find the DVD automatically and boot up… eventually. Seriously, it took like 15 minutes.

Slowly booting the LiveDVD

Almost there

We have X11

Obligatory uname and cpuinfo (It’s as blurry as you think it is, composite video really sucks)

But I had Linux running! BlackRhino Linux to be specific, a Debian derivative. However, with no swap available, the memory was 100% full from the get-go. I needed a writable filesystem. Enter some random maxtor I have. Threw it in and it just happened to work fine, booted a kernel and initrd, formatted the hard drive, which was 4GB large, mounted the USB, and copied over the rootfs for BlackRhino. After that it was a reboot and I was booting off the hard drive, with help from the kernel on the USB.

With a writable filesystem and more importantly, swap space, I could start doing things! Namely installing GCC so I can compile Bash 4.0 and run neofetch. No issues, didn’t even take that long either. Though neofetch messed up the font or something, causing all text to be corrupted/have changed character sets. Screenfetch and pfetch didn’t do this. I also switched to a better TV using component cables, so the picture is much, much better.

BlackRhino off the hard drive



Now that the obligatory *fetch is out of the way, it’s time to do real things. I thought about working with BlackRhino, but it’s based on Debian 3.0, which, is a little old, coming out in 2002. Appropriate for the PS2, but not of any use for me in 2021.

Let’s go back to that first url I linked at the top there. Linux 5.4, for the PS2. Crazy. Unfortunately, the dev doesn’t provide any images to test, so I’m on my own for building and testing. At the time of writing, the wiki is pretty solid, though some things are missing, which makes coming into this with very little experience with crossdev, outside of setting it up for distcc, pretty daunting. Luckily, browsing through the issues revealed answers.

Considering the fact that there are a couple forked projects involved here, I decided to create a chroot on my desktop with a minimal enough Gentoo x86_64 install to handle crossdev for mipsel. The wiki helped for the most part, and told me how to fix musl so I can use that instead of glibc, along with Issue #33.

Some time later, we have a kernel and embedded busybox initrd.

(PS2chroot) yarideki ~/linux # file vmlinuz
vmlinuz: ELF 32-bit LSB executable, MIPS, MIPS-III version 1 (SYSV), statically linked, stripped

As this is an ELF executable, uLaunchELF/wLaunchELF on the PS2 should be able to directly boot it. And after renaming it from vmlinuz to vmlinuz.elf, it does! Using a cool 3MB of RAM, and running at 1080p(!!). Overscan was an issue on the specific TV I have the PS2 connected to, but I was able to nudge the picture over enough to see what I was typing. There’s nothing on the initrd as of now, just busybox.

Booted 5.4 on a PS2

I went to bed after that, and now it’s the next day. I’ve compiled the fork of QEMU that should let me run these MIPS binaries on my desktop for testing.

With the fork here, I had to run: ./configure --target-list=mipsel-linux-user,mipsn32el-linux-user to only build the MIPS userspace targets, and not everything. That repo doesn’t include build instructions, and I’ve never built QEMU on my own before, so if someone reading this would also be insane enough to do what I am doing, there you go! The compile only took 49 seconds, given 6 threads on my Ryzen 5 2600, very nice.

Well, until I read on how to actually set up QEMU for usermode emulation. Turns out I need to recompile it on my host to give me the binfmt daemons, as per this article on the Gentoo wiki. It talks about creating a chroot after the fact but I already kinda had one, the crossdev root thing that it created for the compilers. I will keep using that for now, as MIPS stage3’s are a little hard to come by.

Speaking of, the MIPS ABI is weird as hell in that there’s basically two ABIs for 32bit MIPS, o32 and n32. o32 is the “traditonal” ABI, but there’s apparently performance issues with it. I don’t know what the PS2 stuff uses, I’m assuming o32. With that in mind, there happens to be a o32 MIPS3 stage3 for Gentoo, dated 2020-03-24. Though, because the R5900 in the PS2 is only “mostly” MIPS3, perhaps a MIPS2 stage3 would be more appropriate, though no musl available. I don’t want to really deal with building from complete scratch either, so I may try the MIPS3 stage and just cross my fingers.

Back to the QEMU usermode, it works(tm) but only inside the 1st chroot. After downloading the musl stage3, I couldn’t chroot into it. I need a static build of QEMU, and I have a dynamically linked one. But running ./configure with --static tacked on, gives me

ERROR: sizeof(size_t) doesn't match GLIB_SIZEOF_SIZE_T.
       You probably need to set PKG_CONFIG_LIBDIR
       to point to the right pkg-config files for your
       build target

which the Internet says compile the dependencies also statically. I did glib and pixman, but it still complains, and the config.log isn’t of much help either. I could go through and compile things until it stops complaining, OR I can just set USE="static-libs" globally in this chroot and just let it rebuild everything! :)

IF this was the normal, in tree QEMU, I don’t think I would have issues. But because I’m using this dude’s fork, I don’t get the benefit of having Portage figure out all this for me.

Alternatively, I could just try to do the static QEMU build on my desktop and see what Portage wants to do, and that’s what I did instead. What my desktop came up with was this. Keep in mind, my USE flags/features are different on my desktop and I have more things enabled, so some of these are likely unneeded, but I put them there anyways.

dev-libs/glib static-libs
x11-libs/pixman static-libs
dev-libs/libpcre static-libs
sys-libs/ncurses static-libs
app-arch/bzip2 static-libs
dev-libs/libaio static-libs
media-libs/libpng static-libs
sys-libs/libseccomp static-libs
dev-libs/openssl static-libs
net-libs/libslirp static-libs
dev-libs/libxml2 static-libs
sys-libs/libcap-ng static-libs
net-misc/curl static-libs
sys-apps/dtc static-libs
media-libs/libjpeg-turbo static-libs
virtual/jpeg static-libs
media-libs/libsdl2 static-libs

That didn’t help. Back to setting static-libs globally and just sending it. Huzzah! Whatever got compiled statically made QEMU stop complaining.

chroot: failed to run command ‘/bin/bash’: Exec format error

Well… That’s not right it’s supposed to wor-

bin/bash: ELF 32-bit MSB executable, MIPS, MIPS-III version 1 (SYSV), dynamically linked, interpreter /lib/ld-musl-mips.so.1, stripped

[...]bin/busybox: ELF 32-bit LSB executable, MIPS, MIPS-III version 1 (SYSV), statically linked, stripped

Ah. Do you see it? bash in the chroot is a 32-bit MSB executable, and busybox is a 32-bit LSB executable. Endian issues.

It was there I decided to take a break and do some reading (and eat something).

Ok so I didn’t realize how long Issue 33 was for the kernel and it turns out, just about everything I was just doing was for nothing as it’s allllllllll there for me in that issue. Issue 33 is now the gospel of this project.

So where I’m at now is I have a mipsr5900el chroot using musl as the libc, located at /usr/mipsr5900el-unknown-linux-musl. I have busybox and dropbear installed. I have installed/updated @system. I have also added the custom gentoo repository from frno7 to give me a proper QEMU install.

Though, when I thought I had a full @system set, it turns out I didn’t, as I only had busybox and dropbear, nothing else. Thinking that me using musl would cause issue, I compiled another toolchain, this time using glibc.

That seems to have worked, I guess since everything is normally compiled against glibc in normal Gentoo, that things would depend on it. Whatever it is, I now have a more useful rootfs to start off with, and I will use glibc for now, and revisit musl later.

Well, I figured it out. I couldn’t compile ncurses, but after some digging, I found this bug on the Gentoo bugzilla. TL;DR, if the version of ncurses on the host differs from what you’re trying to emerge in a chroot, it fails. Fixed that, and we’re back in action under musl.

First I need app-eselect/eselect-repository so I can enable the musl repo and get any fixes for using musl system-wide. 28 packages are to be installed, and a lot of those are critical, these-should’ve-been-already-installed packages.

More issues: libxml2 doesn’t compile, this other bug has a fix. lxml doesn’t either, with the same error, but not the same solution. These issues aren’t related to musl, but rather how the compiler reacts to differing word sizes in the host and target architectures. Since I’m compiling 32bit MIPS on a 64bit desktop…

It was here that I decided that modern Linux just isn’t ready for the PS2. I want to revisit this and report back with something running on there, but as things are right now, it’s just not easily done. Especially not by someone like myself.

Things I would try next, when I revisit this: