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.
(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.
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
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
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
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
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
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:
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,
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,
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
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
--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
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,
/usr/mipsr5900el-unknown-linux-musl. I have
dropbear installed. I have installed/updated
@system. I have also added the
custom gentoo repository from
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
dropbear, nothing else. Thinking that me using
musl would cause issue, I compiled another toolchain, this time using
That seems to have worked, I guess since everything is normally compiled
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
now, and revisit
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
First I need
app-eselect/eselect-repository so I can enable the
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.
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:
- 32bit host to avoid cross word-size issues, which means a dedicated machine and not a chroot on my desktop
- Taking an existing distribution and backporting a newer userland