One of the really cool features of QEMU (Quick Emulator) is that it can emulate CPU architectures other than x86-64, such as PowerPC, AArch64, and SPARC. In my experimentation with Solaris, I’ve really wanted to try the SPARC and SPARC64 emulators with Solaris, and do something similar to this article: Build your own SPARC workstation with QEMU and Solaris. However, I really wanted to do this with Solaris 8/9/10, as 2.6 is more limited in what you can do with it. In particular, I wanted to run Solaris 8 in this, as installing 8 in VirtualBox is a hassle, with barely-functional graphics. In the end, I was only able to run Solaris in the 32-bit emulator, which emulates a SPARCstation 5 by default. The SPARC64 emulator can only run BSD and Linux variants, and not Solaris. In the future, I intend on writing another post on the SPARC64 emulator.
Setting up QEMU
I performed all of this experimentation in Linux Mint 22 on an Intel NUC 13 I7. QEMU can also run on macOS (or what I still refer to as OS X) and Windows. Initially I used the version of qemu-system-sparc that can be installed with DNF or APT, and it worked fine with Solaris 9, but Solaris 8 would go into a crashing boot loop after installation. In an attempt to resolve this, I compiled the latest version of QEMU from source and used this instead, and Solaris 8 seemed to be more stable, though I did run into other issues. More on those later. I will also do a separate article on compiling QEMU from source on Debian-like and EL-like systems.
Configuring bridged networking (optional)
By default QEMU uses a NATted network called SLIRP that doesn’t allow ICMP and requires you to forward ports to it, similar to Docker. This seemed like a hassle to me and I wanted to use static IPs on my lab network anyway, so I choose to use the bridged network type instead. This requires that you already have your NIC configured as a bridge, such as for KVM. There are a few articles out there on how to do this; I’ve used this one in the past. To allow QEMU to use this bridge while running it as a non-root user, perform the following steps (thank you to this post for providing these):
- Create the file /etc/qemu/bridge.conf (or /usr/local/etc/qemu/bridge.conf if QEMU was compiled from source) with just the line: allow br0
- Set the setUID bit on the bridge helper with: sudo chmod u+s /usr/lib/qemu/qemu-bridge-helper (or sudo chmod u+s /usr/local/libexec/qemu-bridge-helper)
These steps are optional and the option -nic bridge can be left off if you just want to use the default SLIRP network.
Create and format the disk
First, create the QCOW2 disk that with the qemu-img command: qemu-img create -f qcow2 name.qcow2 number-of-gigsG or qemu-img create -f qcow2 name.qcow2 bytes. You will need to know the size of the image in bytes for the next exercise. If you created the image with gigabytes, you can get the byte size with qemu-img info image.qcow2. Note: qemu-img info will not work when the system is running, as it puts a lock on the image file.
Before you can install the OS, you will need to boot into single user mode and format the disk. First, start the machine with:
qemu-system-sparc -nic bridge -m 256M -drive file=solaris.qcow2,if=scsi,bus=0,unit=0,media=disk -drive file=solaris_8_sparc_d1.iso,format=raw,if=scsi,bus=0,unit=1,media=cdrom,readonly=on
Next, at the prompt, type boot cdrom:d -vs and hit enter. At the # prompt, issue the format command. Select disk 0 and specify 18 (other) for the disk type.
OK, so what should you enter for the number of data cylinders? The AdaFruit tells you what to put for a 9GB disk, but doesn’t mention how they calculated the value. Other articles make this process sound really complicated when it isn’t. I really wanted to know how to calculate this for myself and after some digging, was able to figure it out. Below is the formula:
- Disk size in bytes / (16 * 64 * 512)
- Round down to the nearest integer
- Subtract that by 2
So my disk is 10737418240 bytes (10GB) in size. I used the below calculations and values:
- I multiplied 16 heads by 63 sectors by 512 bytes per sector. This is going off the PATA standard, and I’m not sure it’s correct for SCSI. This comes to 516096.
- I divided 10737418240 bytes by 516096 = rounded down to 20805
- 20805 – 2 = 20803. This is the number of data cylinders.
- Enter 16 for the number of heads.
- Enter 63 for the number of data sectors/track.
- Enter whatever value you want for the disk type name. I chose Qemu.
- For the other values, press enter to accept the default value.
After the format completes, enter label at the format> prompt and hit enter. Below is a screenshot of this process:
Enter quit at the format> prompt and reboot at the # prompt.
Start the installation
At the prompt, enter type boot cdrom:d and hit enter. Follow the prompts to install Solaris 8 or 9 to your preferences.
With Solaris 9, I only ran into one issue. I wanted to partition the drive manually so that the root partition had more space. However, I kept getting the below error stating that I had exceeded the allowable cylinder range, even when the range was below 8323:
Furthermore, this error completely bombs the installation and you have to reboot and start over again. After a while this got tedious, and I gave up and choose auto-layout. Whatever, it’s fine. Sigh.
With Solaris 8, the stage of the installation that installs packages from the second CD just crashes completely (it also fails on the x86 installation in VirtualBox, but this is due to the tiny display resolution). In this case, it appears to be an architecture issue. I tried starting the installer manually by booting the machine with the second CD, changing directories to /cdrom/sol_8_hw703_sparc_2 and running ./installer, but I got the error below:
Just like in Solaris 8 x86, I had to install any additional packages I wanted by cd’ing into /cdrom/sol_8_hw703_sparc_2/Solaris_8/Product and running pkgadd -d . package. For me, the first thing I always install is SUNWbash, because as a millennial I find the Bourne or C shells unusable. I also install gzip and less.
Start the machine without the install media
To start the machine without the install media, run:
qemu-system-sparc -nic bridge -m 256M -drive file=solaris.qcow2,if=scsi,bus=0,unit=0,media=disk
Patching the machines and running Ansible
Using the steps provided in my previous post, I was able to patch the system using the Solaris 9 Recommended Patch Cluster (9_Recommended.zip). I then installed the prerequisites for Ansible using my Bash script (installing Python was really slow in particular). Finally, I was able to run the setup module against the machine successfully. Below is the output from this:
matthew@death-star:/solhome/matthew$ ansible all -l sol9vm2 -m setup sol9vm2 | SUCCESS => { "ansible_facts": { "ansible_all_ipv4_addresses": [ "192.168.87.54" ], "ansible_all_ipv6_addresses": [], "ansible_apparmor": { "status": "disabled" }, "ansible_architecture": "sun4m", "ansible_date_time": { "date": "2025-03-08", "day": "08", "epoch": "1741442546", "hour": "09", "iso8601": "2025-03-08T14:02:26Z", "iso8601_basic": "20250308T090226564080", "iso8601_basic_short": "20250308T090226", "iso8601_micro": "2025-03-08T14:02:26.564080Z", "minute": "02", "month": "03", "second": "26", "time": "09:02:26", "tz": "EST", "tz_offset": "%z", "weekday": "Saturday", "weekday_number": "6", "weeknumber": "09", "year": "2025" }, "ansible_default_ipv4": { "address": "192.168.87.54", "broadcast": "192.168.87.255", "device": "le0", "flags": [ "UP", "BROADCAST", "RUNNING", "MULTICAST", "IPv4" ], "gateway": "192.168.87.1", "interface": "le0", "macaddress": "52:54:00:12:34:56", "mtu": "1500", "netmask": "255.255.255.0", "network": "192.168.87.0", "type": "unknown" }, "ansible_default_ipv6": {}, "ansible_devices": { "sd0": { "hard_errors": "0", "illegal_request": "20", "media_errors": "0", "predictive_failure_analysis": "0", "product": "QEMU HARDDISK", "revision": "2.5+\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000", "serial": "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000", "size": "10.00 GB", "soft_errors": "20", "transport_errors": "0", "vendor": "QEMU \u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000" }, "sd2": { "hard_errors": "0", "illegal_request": "0", "media_errors": "0", "predictive_failure_analysis": "0", "product": "QEMU CD-ROM", "revision": "2.5+\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000", "serial": "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000", "size": "0.00 Bytes", "soft_errors": "0", "transport_errors": "0", "vendor": "QEMU \u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000" } }, "ansible_distribution": "Solaris", "ansible_distribution_major_version": "9", "ansible_distribution_release": "Solaris 9 s9_58shwpl3 SPARC", "ansible_distribution_version": "9", "ansible_dns": { "domain": "ridpath.mbr", "nameservers": [ "192.168.87.10" ] }, "ansible_domain": "", "ansible_effective_group_id": 1, "ansible_effective_user_id": 0, "ansible_env": { "HOME": "/", "HOSTNAME": "sol9vm2", "LOGNAME": "root", "MAIL": "/var/mail/root", "PATH": "/usr/bin:/bin:/usr/sbin:/sbin", "SHELL": "/bin/bash", "SUDO_COMMAND": "/bin/sh -c echo BECOME-SUCCESS-ncxmhlgtfhjiafqvzxvpuaytpdfjjkgv ; /opt/csw/bin/python /export/home/ansible/.ansible/tmp/ansible-tmp-1741442534.5230982-7836-272446156466758/AnsiballZ_setup.py", "SUDO_GID": "1010", "SUDO_UID": "1010", "SUDO_USER": "ansible", "TERM": "xterm-256color", "TZ": "US/Eastern", "USER": "root", "USERNAME": "root" }, "ansible_fips": false, "ansible_fqdn": "sol9vm2", "ansible_hostname": "sol9vm2", "ansible_hostnqn": "", "ansible_interfaces": [ "le0", "lo0" ], "ansible_is_chroot": false, "ansible_iscsi_iqn": "", "ansible_kernel": "5.9", "ansible_kernel_version": "Generic_117171-12", "ansible_le0": { "device": "le0", "ipv4": [ { "address": "192.168.87.54", "broadcast": "192.168.87.255", "flags": [ "UP", "BROADCAST", "RUNNING", "MULTICAST", "IPv4" ], "mtu": "1500", "netmask": "255.255.255.0", "network": "192.168.87.0" } ], "ipv6": [], "macaddress": "52:54:00:12:34:56", "type": "unknown" }, "ansible_lo0": { "device": "lo0", "ipv4": [ { "address": "127.0.0.1", "broadcast": "127.255.255.255", "flags": [ "UP", "LOOPBACK", "RUNNING", "MULTICAST", "IPv4" ], "mtu": "8232", "netmask": "255.0.0.0", "network": "127.0.0.0" } ], "ipv6": [], "macaddress": "unknown", "type": "loopback" }, "ansible_local": {}, "ansible_lsb": {}, "ansible_machine": "sun4m", "ansible_memtotal_mb": 256, "ansible_mounts": [ { "block_available": 491880, "block_size": 8192, "block_total": 1668222, "block_used": 1176342, "device": "/dev/dsk/c0t0d0s0", "fstype": "ufs", "inode_available": 361109, "inode_total": 417728, "inode_used": 56619, "mount": "/", "options": "rw,intr,largefiles,xattr,onerror=panic,suid,dev=800000", "size_available": 503685120, "size_total": 1708259328, "time": "1741442493" }, { "block_available": 0, "block_size": 512, "block_total": 0, "block_used": 0, "device": "/proc", "fstype": "proc", "inode_available": 1537, "inode_total": 1596, "inode_used": 59, "mount": "/proc", "options": "dev=3a40000", "size_available": 0, "size_total": 0, "time": "1741442493" }, { "block_available": 0, "block_size": 512, "block_total": 0, "block_used": 0, "device": "mnttab", "fstype": "mntfs", "inode_available": 0, "inode_total": 1, "inode_used": 1, "mount": "/etc/mnttab", "options": "dev=3b00000", "size_available": 0, "size_total": 0, "time": "1741442493" }, { "block_available": 0, "block_size": 1024, "block_total": 0, "block_used": 0, "device": "fd", "fstype": "fd", "inode_available": 0, "inode_total": 31, "inode_used": 31, "mount": "/dev/fd", "options": "rw,suid,dev=3b40000", "size_available": 0, "size_total": 0, "time": "1741442493" }, { "block_available": 168070, "block_size": 4096, "block_total": 168075, "block_used": 5, "device": "swap", "fstype": "tmpfs", "inode_available": 15544, "inode_total": 15571, "inode_used": 27, "mount": "/var/run", "options": "xattr,dev=1", "size_available": 688414720, "size_total": 688435200, "time": "1741442494" }, { "block_available": 168070, "block_size": 4096, "block_total": 168195, "block_used": 125, "device": "swap", "fstype": "tmpfs", "inode_available": 15544, "inode_total": 15571, "inode_used": 27, "mount": "/tmp", "options": "xattr,dev=2", "size_available": 688414720, "size_total": 688926720, "time": "1741442495" }, { "block_available": 7498484, "block_size": 8192, "block_total": 8107421, "block_used": 608937, "device": "/dev/dsk/c0t0d0s7", "fstype": "ufs", "inode_available": 1004371, "inode_total": 1023744, "inode_used": 19373, "mount": "/export/home", "options": "rw,intr,largefiles,xattr,onerror=panic,suid,dev=800007", "size_available": 7678447616, "size_total": 8301999104, "time": "1741442495" }, { "block_available": 0, "block_size": 1024, "block_total": 0, "block_used": 0, "device": "-hosts", "fstype": "autofs", "inode_available": 0, "inode_total": 0, "inode_used": 0, "mount": "/net", "options": "indirect,nosuid,ignore,nobrowse,dev=3cc0001", "size_available": 0, "size_total": 0, "time": "1741442497" }, { "block_available": 0, "block_size": 1024, "block_total": 0, "block_used": 0, "device": "auto_home", "fstype": "autofs", "inode_available": 0, "inode_total": 0, "inode_used": 0, "mount": "/home", "options": "indirect,ignore,nobrowse,dev=3cc0002", "size_available": 0, "size_total": 0, "time": "1741442497" }, { "block_available": 0, "block_size": 1024, "block_total": 0, "block_used": 0, "device": "-xfn", "fstype": "autofs", "inode_available": 0, "inode_total": 0, "inode_used": 0, "mount": "/xfn", "options": "indirect,ignore,dev=3cc0003", "size_available": 0, "size_total": 0, "time": "1741442497" }, { "block_available": 0, "block_size": 512, "block_total": 0, "block_used": 0, "device": "sol9vm2:vold(pid219)", "fstype": "nfs", "inode_available": -1, "inode_total": -1, "inode_used": 0, "mount": "/vol", "options": "ignore,dev=3c80001", "size_available": 0, "size_total": 0, "time": "1741442501" } ], "ansible_nodename": "sol9vm2", "ansible_os_family": "Solaris", "ansible_pkg_mgr": "svr4pkg", "ansible_processor": [ "FMI,MB86904 @ 170MHz" ], "ansible_processor_cores": "NA", "ansible_processor_count": 1, "ansible_python": { "executable": "/opt/csw/bin/python", "has_sslcontext": false, "type": "CPython", "version": { "major": 2, "micro": 9, "minor": 6, "releaselevel": "final", "serial": 0 }, "version_info": [ 2, 6, 9, "final", 0 ] }, "ansible_python_version": "2.6.9", "ansible_real_group_id": 1, "ansible_real_user_id": 0, "ansible_selinux": { "status": "Missing selinux Python library" }, "ansible_selinux_python_present": false, "ansible_service_mgr": "service", "ansible_ssh_host_key_dsa_public": "AAAAB3NzaC1kc3MAAACBAOmBvs3p98gu8GRbCbXz27mFT1WfoetmwFmmpfv8RWcDIWA1sdpz3c6tdCOQZfGCGRgqu0k3NPYsSNjW2nOjZcmEmQJPc0DAkjGrWupIpRGwAx/IKYXIQ6Q8AlG+uRgz+Qmwwvnnuy7/WCM4E9MFuLBVaG2su9FAxmGevVUtoGW9AAAAFQCtNkquSgtyXKqvjdsJKcF519mZoQAAAIA+NfGuAZUeCTjE1x5GABhz18+N3xb270plkmQzvdhcAmvBUAZ/2b3gCk5zUa+yZQX1UV4cseUsdGL4O84vC7UWavB80eYyxunbGQF7riioepT1Q8+dCzTSHWTQesdMmsZlYBmjCG6Aj1Cz7fUDC7TEN2aJKKHZLBx4LOX8ps7XsAAAAIBXXe5h5zdmfGV5onf+ssDdE1LxoIdUknAmM8A+Ukvfa9k9rWaSC8Oi0f2Unav5gwrQ8Uy8kIfh8PsnljmvCZA6Pw6P9f/Oratxv1v4xwIKyqPNy3SuewTLEYfWFWx8O4NVornDtCckviPBf5VB8wIceaaiEHlLS/5KEa8JAOA1jA==", "ansible_ssh_host_key_dsa_public_keytype": "ssh-dss", "ansible_ssh_host_key_rsa_public": "AAAAB3NzaC1yc2EAAAABIwAAAIEAy5KeYx7McZgz0Sgf2dBh4iU0lSAa2KNvX70e9Lzl/LVjYTk+flKkUWX54EdOL4rGhX0DP9av20uExIOuhsjq9q9EoSn9SICLz00yTuMNzXEvj60XFj69HU8+eGYLOSjZxCISgf0T8p4YwIWb8EAIBhEqDvEt85R+oTDYaMXChJc=", "ansible_ssh_host_key_rsa_public_keytype": "ssh-rsa", "ansible_swap_allocated_mb": 33, "ansible_swap_reserved_mb": 8, "ansible_swapfree_mb": 658, "ansible_swaptotal_mb": 699, "ansible_system": "SunOS", "ansible_uptime_seconds": 61, "ansible_user_dir": "/", "ansible_user_gecos": "Super-User", "ansible_user_gid": 1, "ansible_user_id": "root", "ansible_user_shell": "/sbin/sh", "ansible_user_uid": 0, "ansible_userspace_bits": "32", "gather_subset": [ "all" ], "module_setup": true }, "changed": false }
Final thoughts on Solaris 9 in QEMU
- It works.
- It’s quicker to set up than running the x86 version in VirtualBox, as you don’t have to select a video card, etc.
- Compared to VirtualBox, it’s a lot slower. That’s because it’s emulating a different CPU architecture, while not making use of the virtualization instruction set on the CPU. It’s also emulating a computer from 1994. I’m amazed Solaris 9 runs on here at all.
- VirtualBox has less of a learning curve and is more familiar to most.
- In sum, if you’re like me and are just tinkering with Solaris, then the x86 version is a better option.
- SPARC64 would be a lot more useful for me, as I could try stuff on here before deploying it to my SunBlade 100 workstation.
Final thoughts on Solaris 8 in QEMU
- It works, but it’s less stable. The installation crashes on the final stage.
- Compared to the x86 Solaris 8, at least you get full graphics. I’ve only been able to get 640×480 with 8-bit color to work with VirtualBox.
- Maybe this is the better route if you want to try Solaris 8, especially if you want usable graphics. But once again, the performance it really slow and it is less stable than running the 32-bit version.
I hope that this was of use to someone. As always, thanks for reading!