Porting OpenBSD to the Solbourne S4000

[ Index ] [ Prev: Getting Started ] [ Next: Blinking The LED ]

Playing With The hardware

There were two ways I could play with the hardware: either with Forth commands at the PROM prompt, or by getting it to download binary code.

I have no disk (yet) in this machine. But it is no surprise that the PROM can boot off the network, in the same way as the Sun workstations. There is a subtle difference, though: the PROM directly downloads its kernel from tftp. No need for intermediate boot code.

Moreover, if you try to have the prom netboot the genuine SunOS bootloader, you will be rewarded with an enigmatic message:

ok boot tftp.ei()netboot.solbourne
rarp: using IP address = A0001A4
rarp: server at IP address = A000165
tftp.ei()netboot.solbourne: conflicts with rom (entry must be greater than 0xfd044000)

No more luck, but different message, with OpenBSD's netboot:

ok boot tftp.ei()netboot.sparc
rarp: using IP address = A0001A4
rarp: server at IP address = A000165
tftp.ei()netboot.sparc: overlaps current image

Of course, the OS/MP kernel boots fine:

ok boot tftp.ei()vmunix
rarp: using IP address = A0001A4
rarp: server at IP address = A000165
Boot:  tftp.ei()vmunix
Entry: 0xfd080000
Size:  0x12e000+0x43710+0x8b3e0

OS/MP 4.1C Export(GENERIC/Root)#0: ...

The seasoned OpenBSD/sparc hacker will quickly notice that a few things feel wrong here. The OpenBSD/sparc code gets linked at a lower address, with the kernel image at (virtual) 0xf8000000 and the kernel entry at (virtual) 0xf8004000. But it is loaded by the Sun PROM low in memory, at physical address zero onwards.

Here, it seems that

So, to get the PROM to accept loading my code, all I need would be changing the link address. This would imply changing the constant KERNELBASE in <machine/param.h>, so the S4000 port would need to be a distinct architecture from OpenBSD/sparc.

Being bold, I decided I could as well start by getting the PROM to load a full-blown kernel; it took only a few minutes to create a new architecture in the source tree, OpenBSD/solbourne, which would be a compound architecture with MACHINE being solbourne but MACHINE_ARCH being sparc. I populated almost everything inside /sys/arch/solbourne/include/ with two-line wrappers to the sparc include files, like this:

/* $OpenBSD$ */
/* public domain */
#include <sparc/foo.h>
Except for /sys/arch/solbourne/include/param.h, which was almost identical to /sys/arch/sparc/include/param.h, minus this:
#define	KERNBASE	0xfd080000	/* start of kernel virtual space */
#define	KERNTEXTOFF	0xfd080000	/* start of kernel text */

Of course, I knew this kernel would die very early, if only because it would not drive the MMU properly, but what I wanted was to get something loaded. It could be made working later...

The astute reader will raise the concern that OpenBSD/sparc (and thus, OpenBSD/solbourne) uses the ELF binary file format, while SunOS 4 (and thus, OS/MP) uses the a.out binary file format, and it is unlikely that the Solbourne PROM can read ELF binaries.

Since the BSD kernel is a standalone, statically linked executable, it is very easy to convert the ELF binary to a.out. The steps are as follows:

  1. Run clean-elf (which can be found in /sys/arch/sparc/stand/common/clean-elf.c) on the bsd kernel to merge the .rodata section with the .text section, as a.out does not have the notion of a read-only data section:
    clean-elf bsd
  2. Now that the binary only has the .text, .data and .bss sections left, rewrite it in a.out format with objcopy:
    objcopy -j .text -j .data -j .bss -O a.out-sparc-netbsd bsd aoutbsd

So I had an a.out file (aoutbsd), and I was ready to boot it.

ok boot tftp.ei()aoutbsd
rarp: using IP address = A0001A4
rarp: server at IP address = A000165
Boot:  tftp.ei()aoutbsd
Entry: 0xfd080000
Size:  0xfbd20+0x147b0+0x32fd0
Error on word boot

The cursor would spin for a while, with data effectively transmitted across the wire, but would always end with this cryptic error message.

What could possibly cause loading my binary to fail? Let's have a look at the a.out headers for both kernels:

$ hexdump -C -n32 vmunix
00000000  01 03 01 07 00 12 e0 00  00 04 37 10 00 08 b3 e0  |.........7...|
00000010  00 02 01 24 fd 08 00 00  00 00 00 00 00 00 00 00  |...$...........|
$ hexdump -C -n32 aoutbsd
00000000  00 8a 01 07 00 0f bd 20  00 01 47 b0 00 03 2f d0  |...... ..G../|
00000010  00 00 b7 d8 fd 08 00 00  00 00 00 00 00 00 00 00  |...@.........|

Apart from the machine ID in the magic number (3 = M_SPARC for OS/MP, 8a = MID_SPARC for OpenBSD), nothing looks wrong (of course, sections size differ). Wait! The OS/MP kernel has a very round number for the text size... It looks like it is aligned to an 0x2000 boundary... which happens to be the MMU page size!

I had nothing to lose, and much to gain, trying to also round my text section to a page size. To save me time, I ended up doing it in the following way:

  1. Since I could easily control the size of the .text section, I added a simple prom_pad.o object file last in the kernel link line, which forces the end of the .text section to be page-aligned:
    $ cat /sys/arch/solbourne/solbourne/prom_pad.s
    /*	$OpenBSD$	*/
    /* public domain */
    #include <machine/asm.h>
    #include <machine/param.h>
    	.align	PROM_TEXT_PAD
    	.globl	_C_LABEL(prom_pad)
    	.type	_C_LABEL(prom_pad),@object
  2. I changed clean-elf to not merge .rodata in .text, but now merge .rodata in .data. This is not very clean, but it works and will be enough for now.

With a text section correctly padded, the PROM was finally happy and loaded my kernel entirely, then ran it, and... well, what did you expect? Of course it froze the machine! Then, I realized that this machine lacks a physical reset switch. I had to power-cycle it (countless times since then), and I am glad there are no hard drives in it, or they would not survive this on the long run...

[ Index ] [ Prev: Getting Started ] [ Next: Blinking The LED ]