Running PonyOS on real hardware for fun and profit

Written on .

Final exams are creeping closer day to day. And what's better use of my time than running PonyOS on real hardware?

If you don't know what PonyOS is, it's a hobby Unix-like operating system. And it is delightful! If you didn't read about it, go check it out. Like, right now. Come back when you read it. It's awesome thing.

my attempt to boot ponyos

My attempt to boot PonyOS

Apart from the stunning visuals, there is something that differs PonyOS from yet another ubuntu-based distribution. PonyOS is built from scratch and it has it's own kernel - it's not Linux!

It's pretty clear what we're going to do. We are going to take this operating system, which was built to run in virtualbox, and run it on real hardware. Because running this on virtual machine just doesn't have the feeling of ultimate hapiness you'll experience. Even if you map Ctrl+Alt to change keyboard layout in X so you can't ungrab your mouse from virtualbox window, so you're trapped inside and are forced to REISUB (actually just R, then you can switch to another terminal) and can't get out of PonyOS by normal means, running it on your real hardware just feels right. Even just for the respectful silence when you amaze your friends with your distro (it's not distro I know) choice.

So let's jump right into it. Download ponyos and we can begin. It's important which version will you download. In my testing, version 5 worked pretty much out of the box after I hacked grub a bit. Version 6-base would require different way to boot it so I didn't try (but it should work) and trying to boot version 6-grub enters infinite loop or something like that. So I recommend version 5 for hassle-free experience and later try to boot 6-base (you will have to dig into grub a bit). If you manage to do it, let me know! Version 6 has many features I would love to have on my computer.


Running it isn't that hard. Simply use qemu or virtualbox or any other virtual machine and throw .iso file at it. It will boot, it will run and it will be awesome. But we want to run it real hardware.

So how are we going to do this? Well, we could copy it to its own partition and then just run it somehow, right? So lets try it. This is what I've done:

% sudo dd if=~/Downloads/ponyos.iso of=/dev/disk/by-partlabel/PonyOS bs=4M conv=noerror,sync status=progress
NO YOU IDIOT. Shutting down.
^C

Wait what?

% alias dd
dd='echo '\''NO YOU IDIOT. Shutting down.'\''; sleep 2; poweroff'

Oh. Yeah, I forgot. I once tried to use dd directly to play with partitions and nuked my partition table. And lost 7 months of data. Because who needs backups, right?

% sudo \dd if=~/Downloads/ponyos.iso of=/dev/disk/by-partlabel/PonyOS bs=4M conv=noerror,sync status=progress
% qemu /dev/disk/by-partlabel/PonyOS

Yeah, this looks good. This actually boots! Note: the /by-partlabel thing is awesome feature that I didn't know existed until my dd disaster. It turns out you can specify disks with many different ways. So if you use this, you'll never accidentally mistype sda instead of sda2.See ls /dev/disk.

Mayyybeeee theoretically it's grub in there, right? So if we just run os-prober...

% sudo os-prober
/dev/nvme0n1p1@/EFI/Microsoft/Boot/bootmgfw.efi:Windows Boot Manager:Windows:efi
/dev/sda2:Manjaro Linux (20.0):ManjaroLinux:linux
/dev/sda3:Arch Linux:Arch:linux

No, nevermind. That would be too easy. If this worked, this would generate grub configuration on their own. It looks like we will have to write our own grub menu entry. So let's learn how grub works!

Open /boot/grub/grub.cfg (or whatever your config location is) and see what's in there. Section that boots Windows bootloader (it's probably the most simple section you'll have there) looks like this:

menuentry 'Windows Boot Manager (on /dev/nvme0n1p1)' --class windows --class os $menuentry_id_option 'osprober-efi-202C-5D55' {
insmod part_gpt
insmod fat
if [ x$feature_platform_search_hint = xy ]; then
search --no-floppy --fs-uuid --set=root 202C-5D55
else
search --no-floppy --fs-uuid --set=root 202C-5D55
fi
chainloader /EFI/Microsoft/Boot/bootmgfw.efi
}

Note: The best way to learn about grub is to play. Reboot, enter GRUB command line, and press tab to see commands. ls works. You can use things like /boot to specify path. This is the way to specify partitions: (hd0,gpt3) (may be without gpt, but pressing Tab helps). So for example ls (hd0,gpt6)/boot works exactly as you thing it works.

So what do we have there in the config file. This looks like the thing that executes when we select the 'Windows Boot Manager' option in grub menu when booting. Ok, so on the first line is a name, some classes, that doesn't interest us. Then in the menu block, it probably loads some modules, sets a variable or something like that, and then chainloads a file. The file is windows bootloader. This looks interesting. Maybe we could use it! The ponyos image has a bootloader, grub! So maybe if we chainload it... Let's try. If you read the whole config file, you know that we should write our own menu entries into /etc/grub.d/40_custom file (don't overwrite it, just append!) and then issue grub-mkconfig. So maybe something like this will work?

menuentry 'PonyOS' {
chainloader (hw0,gpt6)+1
}

The +1 specifies to start reading from first segment. And this won't work. Maybe if we do chainloader (hw0,gpt6)/? No, this doesn't work either. It just can't find anything to execute. Let's see how the .iso file actually looks like.

% mkdir pony
% sudo mount ~/Downloads/ponyos.iso pony/
% cd pony && ls
boot boot.catalog kernel.gz mod ramdisk.img.gz wallpaper.png
% cd boot/grub && ls
fonts grub.cfg i386-pc locale menus.cfg modules.cfg roms theme.txt

Interesting. So there is a kernel, some ramdisk and so on. I have no idea how to boot this. But there is grub.cfg! This is grub config file! If we somehow manage to copy this into our menu entry, it would magically work! Let's see what's actually in the config file.

There are loads of imports, modules, conditions and functions. But the last line looks interesting.

% tail -1 grub.cfg
configfile /boot/grub/menus.cfg

Oh. So this might load configuration file? This would solve our problem! Let's see. Grub documentation:

Command: configfile file

    Load file as a configuration file. If file defines any menu entries, then show a menu containing them immediately. Any environment variable changes made by the commands in file will not be preserved after configfile returns. 

This is interesting. So we could just add configfile to our menu entry. However, how do we find the file? And furthermore, how do we assure that it will work? If we look at PonyOs config file, there are loads of paths like /boot/something. The root here is clearly the root of the iso file, so it would not work if we just loaded it. One option is to write some crazy sed, then debug it, and generally waste a lot of time. The other option is to set the root to something else.

menuentry 'PonyOS' {
set root="some/path/to/iso/file/root"
configfile "/boot/grub/grub.cfg" # We are already using new root here
}

How do we set root? Well, first we need to tell grub to load the isofile. A bit of ducking later, I came up with this.

menuentry "PonyOs" {
set isofile="/path/to/ponyos.iso"
loopback loop $isofile
set root="(loop)"
configfile "/boot/grub/grub.cfg"
}

This looks pretty good. The isofile is just a variable. It's cleaner this way. We then load the isofile, set root to it and execute the config file. This looks pretty good. However, where is the iso file? Well, we could just move it to somewhere when grub can find it easily, right? If we look at our original grub config file, we notice that grub is loading loads of things from /boot. So we put our iso file there as well!

% sudo cp ~/Downloads/ponyos.iso /boot

So what we have now in /etc/grub.d/40_custom is:

#!/bin/sh
exec tail -n +3 $0
# This file provides an easy way to add custom menu entries. Simply type the
# menu entries you want to add after this comment. Be careful not to change
# the 'exec tail' line above.
menuentry "Pony OS" {
set isofile="/boot/ponyos.iso"
loopback loop $isofile
set root="(loop)"
configfile "/boot/grub/grub.cfg"
}

So let's add it to main config file # grub-mkconfig -o /boot/grub/grub.cfg and reboot! We are greeted with a miracle.

Oh. Crap.

Well, I guess I just won't spend hours trying to make a fucking not-even-linux I'll try my older computer. My bios doesn't allow me to switch to legacy boot mode (without formatting my disks for some reason), but maybe on my other computer...

And yes, this works!

ponyos homescreen

It turns out PonyOS requires legacy bios support to run, which makes sense.

Well, that's it. We learned a bit about GRUB and made our own entry in it, which loads PonyOS and actually runs it. Network still doesn't work (at least for me) and it isn't persistent which is something I wanted to do a lot, but that'll have to wait until the next time. Maybe I'll get version 6 to work after exams.

Previous Index Home Next
RSS feed