Thursday, June 18, 2026

Building an Ada Cross-Compiler

Introduction

I previously described how to build a SPARC V9 cross-compiler for the C programming language in this article: SPARC V9 Boot Process – Part 2: sysboot. The resulting cross-compiler was used to compile a SPARC V9 bootloader written predominantly in C.

For complex software development, Ada is a far more suitable programming language than C. My next goal is to rewrite the bootloader in Ada, which requires building an Ada cross-compiler along with several additional development tools.

My build platform is Linux on ARM aarch64 and my target platform is bare metal sparc64. Building an Ada compiler requires an existing Ada compiler on the host system for bootstrapping. Ada compilers are available for many operating systems and can usually be installed through the operating system's package management tools.

$ /bin/gnat --version
GNAT 12.2.0
Copyright (C) 1996-2022, Free Software Foundation, Inc.
This is free software; see the source for copying conditions.
There is NO warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.

I have a native GNAT Ada compiler 12.2.0 that can be used to build a new Ada cross-compiler. The steps required to achieve this are described below.

1. Download Source Code

The first step is to download the required development tools. These consist of Binutils, GCC, XML Ada, and GPRbuild.

# Create various build directories
BUILD_ROOT="${HOME:?}/gcc_build" &&
TAR_DIR="${BUILD_ROOT:?}/tar" &&
SRC_DIR="${BUILD_ROOT:?}/src" &&
OBJ_DIR="${BUILD_ROOT:?}/obj"
mkdir -p ${TAR_DIR:?} ${SRC_DIR:?} ${OBJ_DIR:?}

# Download the latest Binutils, GCC, XML Ada, and GPRbuild
BINUTILS_VER="2.46.0" &&
GCC_VER="15.2.0" &&
GPRBUILD_VER="25.0.0" &&
GPRCONFIG_KB_VER="25.0.0" &&
XMLADA_VER="25.0.0" &&
cd ${TAR_DIR:?} &&
wget https://ftp.gnu.org/gnu/binutils/binutils-${BINUTILS_VER:?}.tar.gz &&
wget https://ftp.gnu.org/gnu/gcc/gcc-${GCC_VER:?}/gcc-${GCC_VER:?}.tar.gz &&
wget -O gprbuild-${GPRBUILD_VER:?}.tar.gz \
  https://github.com/AdaCore/gprbuild/archive/refs/tags/v${GPRBUILD_VER:?}.tar.gz &&
wget -O gprconfig_kb-${GPRCONFIG_KB_VER:?}.tar.gz \
  https://github.com/AdaCore/gprconfig_kb/archive/refs/tags/v${GPRCONFIG_KB_VER:?}.tar.gz &&
wget -O xmlada-${GPRCONFIG_KB_VER:?}.tar.gz \
  https://github.com/AdaCore/xmlada/archive/refs/tags/v${XMLADA_VER:?}.tar.gz

# Unpack all downloaded files
cd ${SRC_DIR:?} &&
for i in $(ls ${TAR_DIR:?}); do tar -xf ${TAR_DIR:?}/$i & done; wait

# Before building GCC we need to download a few prerequisites
cd ${SRC_DIR:?}/gcc-${GCC_VER:?} &&
/bin/sh contrib/download_prerequisites

2. Build Native Development Tools

The GCC documentation strongly recommends using a native Ada compiler that matches the version of the cross-compiler being built. Using a different version may lead to unexpected build failures. Therefore, in this step, I build a native Ada compiler that will later be used to bootstrap the corresponding Ada cross-compiler.

The native Ada compiler can also be used to build GPRbuild, a build system that integrates closely with Ada development tools while supporting multiple programming languages. Although the use of GPRbuild is optional and some developers may prefer traditional Makefiles, it can be particularly beneficial for managing large and complex projects. For this reason, instructions for building GPRbuild are included here.

# Set various build options
MAKE_JOBS=4 &&
PREFIX="${HOME:?}/gcc-${GCC_VER:?}_native" &&
PATH="${PREFIX:?}/bin:/bin:/usr/bin:/sbin:/usr/sbin" &&
CC="gcc" &&
CXX="g++" &&
unset LDFLAGS &&
export PATH CC CXX

# Build and install Binutils with static standard libs
pkg="binutils-${BINUTILS_VER:?}"; cd ${OBJ_DIR:?} &&
test ! -d ${pkg:?} && mkdir ${pkg:?}; cd ${pkg:?} &&
${SRC_DIR:?}/${pkg:?}/configure --prefix=${PREFIX:?} --libdir=${PREFIX:?}/lib \
  --enable-shared --enable-64-bit-bfd \
  --with-static-standard-libraries --disable-nls &&
gmake -j ${MAKE_JOBS:?} && gmake install &&
cd ${OBJ_DIR:?} && rm -rf ${pkg:?}

# Build and install GCC
pkg="gcc-${GCC_VER:?}"; cd ${OBJ_DIR:?} &&
test ! -d ${pkg:?} && mkdir ${pkg:?}; cd ${pkg:?} &&
mkdir -p ${PREFIX:?}/lib && ln -sf lib ${PREFIX:?}/lib64 &&
${SRC_DIR:?}/${pkg:?}/configure --prefix=${PREFIX:?} --libdir=${PREFIX:?}/lib \
  --enable-languages=c,ada --enable-shared \
  --disable-nls --disable-multilib &&
gmake --output-sync=target -j ${MAKE_JOBS:?} && gmake install &&
cd ${OBJ_DIR:?} && rm -rf ${pkg:?}

# Build and install temporary bootstrap gprbuild
# This will be used to build xmlada and final gprbuild
pkg="gprbuild-${GPRBUILD_VER:?}"; cd ${OBJ_DIR:?} &&
test ! -d ${pkg:?} && cp -a ${SRC_DIR:?}/${pkg:?} ./ && cd ${pkg:?} &&
GNATMAKEFLAGS="-j${MAKE_JOBS:?}" ${SRC_DIR:?}/${pkg:?}/bootstrap.sh \
  --prefix=${PREFIX:?}/gprbuild_bootstrap \
  --with-xmlada=${SRC_DIR:?}/xmlada-${GPRCONFIG_KB_VER:?} \
  --with-kb=${SRC_DIR:?}/gprconfig_kb-${GPRCONFIG_KB_VER:?} &&
cd ${OBJ_DIR:?} && rm -rf ${pkg:?}

# Build and install xmlada
pkg="xmlada-${XMLADA_VER:?}"; cd ${OBJ_DIR:?} &&
test ! -d ${pkg:?} && cp -a ${SRC_DIR:?}/${pkg:?} ./ && cd ${pkg:?} &&
PATH=${PREFIX:?}/gprbuild_bootstrap/bin:${PATH} \
  ${SRC_DIR:?}/${pkg:?}/configure --prefix=${PREFIX:?} --libdir=${PREFIX:?}/lib &&
PATH=${PREFIX:?}/gprbuild_bootstrap/bin:${PATH} \
  gmake -j ${MAKE_JOBS:?} && gmake install &&
cd ${OBJ_DIR:?} && rm -rf ${pkg:?}

# Build and install final gprbuild
pkg="gprbuild-${GPRBUILD_VER:?}"; cd ${OBJ_DIR:?} &&
test ! -d ${pkg:?} && mkdir ${pkg:?}; cd ${pkg:?} &&
PATH=${PREFIX:?}/gprbuild_bootstrap/bin:${PATH}
  gmake -f ${SRC_DIR:?}/${pkg:?}/Makefile \
  prefix=${PREFIX:?} SOURCE_DIR=${SRC_DIR:?}/${pkg:?} setup &&
PATH=${PREFIX:?}/gprbuild_bootstrap/bin:${PATH} \
  gmake -f ${SRC_DIR:?}/${pkg:?}/Makefile -j ${MAKE_JOBS:?} &&
PATH=${PREFIX:?}/gprbuild_bootstrap/bin:${PATH} \
  gmake -f ${SRC_DIR:?}/${pkg:?}/Makefile install &&
cd ${OBJ_DIR:?} && rm -rf ${pkg:?} &&
rm -rf ${PREFIX:?}/gprbuild_bootstrap

3. Build Target Development Cross Tools

In this step, the native Ada compiler built in the previous step, is used to build an Ada cross-compiler targeting the sparc64-unknown-elf architecture. Because this is a bare-metal target, the resulting compiler does not include an Ada runtime library. Without a runtime, the compiler is limited in the types of Ada programs it can build, as many language features depend on runtime support.

A future article will describe how to build a minimal Ada runtime and integrate it with the cross-compiler to support bare-metal Ada development projects.

# Set various build options
MAKE_JOBS=4 &&
BUILD="aarch64-unknown-linux-gnu" &&
HOST="${BUILD:?}" &&
TARGET="sparc64-unknown-elf" &&
PREFIX="${HOME:?}/gcc-${GCC_VER:?}_${TARGET:?}" &&
PATH="${PREFIX:?}/bin:${HOME:?}/gcc-${GCC_VER:?}_native/bin:/bin:/usr/bin:/sbin:/usr/sbin" &&
CC="gcc" &&
CXX="g++" &&
unset LDFLAGS &&
export PATH CC CXX

# Build and install Binutils with static standard libs
pkg="binutils-${BINUTILS_VER:?}"; cd ${OBJ_DIR:?} &&
test ! -d ${pkg:?} && mkdir ${pkg:?}; cd ${pkg:?} &&
${SRC_DIR:?}/${pkg:?}/configure --prefix=${PREFIX:?} --libdir=${PREFIX:?}/lib \
  --build="${BUILD:?}" --host="${HOST:?}" --target="${TARGET:?}" \
  --enable-shared --enable-64-bit-bfd \
  --with-static-standard-libraries --disable-nls &&
gmake -j ${MAKE_JOBS:?} && gmake install &&
cd ${OBJ_DIR:?} && rm -rf ${pkg:?}

# Build and install GCC
pkg="gcc-${GCC_VER:?}"; cd ${OBJ_DIR:?} &&
test ! -d ${pkg:?} && mkdir ${pkg:?}; cd ${pkg:?} &&
mkdir -p ${PREFIX:?}/lib && ln -sf lib ${PREFIX:?}/lib64 &&
${SRC_DIR:?}/${pkg:?}/configure --prefix=${PREFIX:?} --libdir=${PREFIX:?}/lib \
  --build="${BUILD:?}" --host="${HOST:?}" --target="${TARGET:?}" --with-cpu=v9 \
  --enable-languages=c,ada --enable-shared \
  --disable-nls --disable-multilib --disable-libssp --disable-libada &&
gmake --output-sync=target -j ${MAKE_JOBS:?} &&
gmake -C gcc --output-sync=target -j ${MAKE_JOBS:?} cross-gnattools &&
gmake -C gcc --output-sync=target -j ${MAKE_JOBS:?} ada.all.cross &&
gmake install &&
cd ${OBJ_DIR:?} && rm -rf ${pkg:?}

After completing the above steps, we obtain the following development tools:

${HOME}/gcc-15.2.0_native - This directory contains native C and Ada compilers and GPRbuild framework.

${HOME}/gcc-15.2.0_sparc64-unknown-elf - This directory contains sparc64 C and Ada cross-compilers.

If we attempt to compile a simple Ada program, the compiler will report that the runtime is missing:

$ cat > test_ada.adb << 'EOF'
procedure Test_Ada is
begin
  null;
end Test_Ada;
EOF

${HOME}/gcc-15.2.0_sparc64-unknown-elf/bin/sparc64-unknown-elf-gcc -c test_ada.adb
fatal error, run-time library not installed correctly
cannot locate file system.ads
compilation abandoned

We can simulate a minimal Ada runtime and test the compiler:

$ mkdir -p test_ada_runtime/adainclude
$ mkdir -p test_ada_runtime/adalib
$ cat > test_ada_runtime/adainclude/system.ads << 'EOF'
package System is
   pragma Pure;
   --  Note that we take advantage of the implementation permission to make
   --  this unit Pure instead of Preelaborable; see RM 13.7.1(15). In Ada
   --  2005, this is Pure in any case (AI-362).

   pragma No_Elaboration_Code_All;
   --  Allow the use of that restriction in units that WITH this unit

   type Name is (SYSTEM_NAME_GNAT);
   System_Name : constant Name := SYSTEM_NAME_GNAT;

   --  System-Dependent Named Numbers

   Min_Int             : constant := -2 ** (Standard'Max_Integer_Size - 1);
   Max_Int             : constant :=  2 ** (Standard'Max_Integer_Size - 1) - 1;

   Max_Binary_Modulus    : constant := 2 ** Standard'Max_Integer_Size;
   Max_Nonbinary_Modulus : constant := 2 ** Integer'Size - 1;

   Max_Base_Digits       : constant := Long_Long_Float'Digits;
   Max_Digits            : constant := Long_Long_Float'Digits;

   Max_Mantissa          : constant := Standard'Max_Integer_Size - 1;
   Fine_Delta            : constant := 2.0 ** (-Max_Mantissa);

   Tick                  : constant := 0.0;

   --  Storage-related Declarations

   type Address is private;
   pragma Preelaborable_Initialization (Address);
   Null_Address : constant Address;

   Storage_Unit : constant := 8;
   Word_Size    : constant := Standard'Word_Size;
   Memory_Size  : constant := 2 ** Word_Size;

   --  Address comparison

   function "<"  (Left, Right : Address) return Boolean;
   function "<=" (Left, Right : Address) return Boolean;
   function ">"  (Left, Right : Address) return Boolean;
   function ">=" (Left, Right : Address) return Boolean;
   function "="  (Left, Right : Address) return Boolean;

   pragma Import (Intrinsic, "<");
   pragma Import (Intrinsic, "<=");
   pragma Import (Intrinsic, ">");
   pragma Import (Intrinsic, ">=");
   pragma Import (Intrinsic, "=");

   --  Other System-Dependent Declarations

   type Bit_Order is (High_Order_First, Low_Order_First);
   Default_Bit_Order : constant Bit_Order :=
                         Bit_Order'Val (Standard'Default_Bit_Order);
   pragma Warnings (Off, Default_Bit_Order); -- kill constant condition warning

   --  Priority-related Declarations (RM D.1)

   Max_Priority           : constant Positive := 30;
   Max_Interrupt_Priority : constant Positive := 31;

   subtype Any_Priority       is Integer      range  0 .. 31;
   subtype Priority           is Any_Priority range  0 .. 30;
   subtype Interrupt_Priority is Any_Priority range 31 .. 31;

   Default_Priority : constant Priority := 15;

private

   type Address is mod Memory_Size;
   for Address'Size use Standard'Address_Size;

   Null_Address : constant Address := 0;

   --------------------------------------
   -- System Implementation Parameters --
   --------------------------------------

   --  These parameters provide information about the target that is used
   --  by the compiler. They are in the private part of System, where they
   --  can be accessed using the special circuitry in the Targparm unit
   --  whose source should be consulted for more detailed descriptions
   --  of the individual switch values.

   Atomic_Sync_Default       : constant Boolean := False;
   Backend_Divide_Checks     : constant Boolean := False;
   Backend_Overflow_Checks   : constant Boolean := True;
   Command_Line_Args         : constant Boolean := False;
   Configurable_Run_Time     : constant Boolean := True;
   Denorm                    : constant Boolean := True;
   Duration_32_Bits          : constant Boolean := True;
   Exit_Status_Supported     : constant Boolean := False;
   Fractional_Fixed_Ops      : constant Boolean := False;
   Frontend_Layout           : constant Boolean := False;
   Machine_Overflows         : constant Boolean := False;
   Machine_Rounds            : constant Boolean := True;
   Preallocated_Stacks       : constant Boolean := False;
   Signed_Zeros              : constant Boolean := True;
   Stack_Check_Default       : constant Boolean := False;
   Stack_Check_Probes        : constant Boolean := False;
   Stack_Check_Limits        : constant Boolean := False;
   Support_Aggregates        : constant Boolean := True;
   Support_Composite_Assign  : constant Boolean := True;
   Support_Composite_Compare : constant Boolean := True;
   Support_Long_Shifts       : constant Boolean := True;
   Always_Compatible_Rep     : constant Boolean := True;
   Suppress_Standard_Library : constant Boolean := True;
   Use_Ada_Main_Program_Name : constant Boolean := False;
   Frontend_Exceptions       : constant Boolean := False;
   ZCX_By_Default            : constant Boolean := True;

end System;
EOF

${HOME}/gcc-15.2.0_sparc64-unknown-elf/bin/sparc64-unknown-elf-gcc \
  -c test_ada.adb --RTS=test_ada_runtime

$ file test_ada.o
test_ada.o: ELF 64-bit MSB relocatable, SPARC V9, relaxed memory ordering, version 1 (SYSV), not stripped

Monday, March 30, 2026

SPARC V9 Boot Process – Part 2: sysboot

Introduction

This article is a follow-up to the earlier SPARC V9 Boot Process – Part 1: bootblk and takes a closer look at the second-stage bootloader of the SPARC V9 architecture.

The design of OpenBoot firmware limits the size of the first-stage bootloader to around 8 KiB. This is just enough to locate the boot device and load the second-stage bootloader, which can be much larger and contains all the logic required to load the operating system kernel. On some operating systems, the second-stage bootloader is called ufsboot or ofwboot; however, in this article, it is referred to as sysboot.

The rest of the article describes how to build SPARC V9 development tools for cross-compilation. It also demonstrates how the first-stage bootloader can use OpenBoot client interface services to locate the boot device and load the second-stage bootloader.

Development Tools for Cross-Compilation

Because we are performing bare-metal programming, which does not rely on operating systems or standard libraries, we can easily build a set of tools to generate SPARC V9 target binaries. My build platform is Linux on ARM aarch64; however, other platforms should work just as well.

The script below performs the following tasks:

  1. Downloads source code tar files for Binutils and GCC.
  2. Unpacks tar files.
  3. Builds Binutils and then GCC development tools.
  4. Installs development tools in user's home directory.

When configuring and building tools for cross-compilation, it is essential to explicitly specify the --build, --host, and --target options:

  • --build and --host specify the platforms on which the development tools are built and run, respectively.
  • --target specifies the platform for which the development tools produce runtime binaries.

🖙 It is important to specify: --target=sparc64-unknown-elf, which produces 64-bit SPARC ELF binaries for a bare-metal (no operating system) target.

# Create various build directories
BUILD_ROOT="${HOME:?}/gcc_build" &&
TAR_DIR="${BUILD_ROOT:?}/tar" &&
SRC_DIR="${BUILD_ROOT:?}/src" &&
OBJ_DIR="${BUILD_ROOT:?}/obj"
mkdir -p ${TAR_DIR:?} ${SRC_DIR:?} ${OBJ_DIR:?}

# Download the latest Binutils and GCC
BINUTILS_VER="2.46.0" &&
GCC_VER="15.2.0" &&
cd ${TAR_DIR:?} &&
wget https://ftp.gnu.org/gnu/binutils/binutils-${BINUTILS_VER:?}.tar.gz &&
wget https://ftp.gnu.org/gnu/gcc/gcc-${GCC_VER:?}/gcc-${GCC_VER:?}.tar.gz

# Unpack all downloaded files
cd ${SRC_DIR:?} &&
for i in $(ls ${TAR_DIR:?}); do tar -xf ${TAR_DIR:?}/$i & done; wait

# Before building GCC we need to download a few prerequisites
cd ${SRC_DIR:?}/gcc-${GCC_VER:?} &&
/bin/sh contrib/download_prerequisites

# Set various build options
MAKE_JOBS=4 &&
BUILD="aarch64-unknown-linux-gnu" &&
HOST="${BUILD:?}" &&
TARGET="sparc64-unknown-elf" &&
PREFIX="${HOME:?}/gcc-${GCC_VER:?}_${TARGET:?}" &&
PATH="${PREFIX:?}/bin:${PATH}" &&
CC="gcc" &&
CXX="g++" &&
unset LDFLAGS &&
export PATH CC CXX

# Build and install Binutils with static standard libs
pkg="binutils-${BINUTILS_VER:?}"; cd ${OBJ_DIR:?} &&
test ! -d ${pkg:?} && mkdir ${pkg:?}; cd ${pkg:?} &&
${SRC_DIR:?}/${pkg:?}/configure --prefix=${PREFIX:?} --libdir=${PREFIX:?}/lib \
  --build="${BUILD:?}" --host="${HOST:?}" --target="${TARGET:?}" \
  --enable-shared --enable-64-bit-bfd \
  --with-static-standard-libraries --disable-nls &&
gmake -j "${MAKE_JOBS:?}" && gmake install

# Build and install GCC
pkg="gcc-${GCC_VER:?}"; cd ${OBJ_DIR:?} &&
test ! -d ${pkg:?} && mkdir ${pkg:?}; cd ${pkg:?} &&
${SRC_DIR:?}/${pkg:?}/configure --prefix=${PREFIX:?} --libdir=${PREFIX:?}/lib \
  --build="${BUILD:?}" --host="${HOST:?}" --target="${TARGET:?}" --with-cpu=v9 \
  --enable-languages=c --enable-shared --disable-libssp --disable-nls &&
gmake --output-sync=target -j "${MAKE_JOBS:?}" && gmake install

The development tools are installed at:
${HOME}/gcc-15.2.0_sparc64-unknown-elf

When compiling code for SPARC V9, the following GCC options should be used:
-ffreestanding -nostdlib -nostartfiles

Now that the development tools for cross-compilation are in place, we can proceed to the next steps: developing SPARC V9 bootloaders.

SPARC V9 Boot Device Layout

To keep things simple, I use the following boot device layout: the first 32 KiB are reserved for the Sun VTOC label and the stage-1 bootloader, while the next 1 MiB is reserved for the stage-2 bootloader.

The boot sequence is as follows:

  1. The OpenBoot firmware reads the first 512 bytes and verifies the Sun VTOC label.
  2. The OpenBoot firmware then reads the next 8 KiB containing the stage-1 bootloader (boot1) and executes it.
  3. The stage-1 bootloader allocates a 1 MiB memory buffer, opens a handle to the specified boot device, seeks to offset 32,768, reads 1 MiB of data into the buffer, and then executes the stage-2 bootloader (boot2) from that memory.

My current stage-1 bootloader is about 5 KiB in size, but it could be expanded to roughly 8 KiB. The stage-2 bootloader is currently around 10 KiB and could be expanded to as much as 1 MiB.

My SPARC V9 test machine is a Sun T5220 that supports booting from a USB flash drive. I use both native and SPARC V9 development tools to build the Sun VTOC utility along with the stage-1 and stage-2 bootloaders, and then write them to the USB drive.

# Include SPARC V9 cross-tools in current PATH
export PATH=${HOME:?}/gcc-15.2.0_sparc64-unknown-elf/bin:${PATH}

# Build native vtoc_label utility
cd ${SRC_DIR}/vtoc_label && gcc -O2 -std=c11 -Wall -pedantic \
  -D_FILE_OFFSET_BITS=64 -D_POSIX_C_SOURCE=200809L \
  -o ${BIN_DIR}/vtoc_label vtoc_label.c

# Build SPARC V9 stage-1 bootloader
cd ${SRC_DIR}/boot1 && sparc64-unknown-elf-gcc \
  -Os -std=c11 -Wall -pedantic -ffreestanding -nostdlib -nostartfiles \
  -o ${BIN_DIR}/boot1.bin start.s main.c ofw.c util.c && \
sparc64-unknown-elf-strip ${BIN_DIR}/boot1.bin

# Build SPARC V9 stage-2 bootloader
cd ${SRC_DIR}/boot2 && sparc64-unknown-elf-gcc \
  -O2 -std=c11 -Wall -pedantic -ffreestanding -nostdlib -nostartfiles \
  -o ${BIN_DIR}/boot2.bin start.s main.c ofw.c util.c v9reg.c v9reg_save.s && \
sparc64-unknown-elf-strip ${BIN_DIR}/boot2.bin

# Write Sun VTOC disk label and bootloaders
${BIN_DIR}/vtoc_label write <device> &&
dd if=${BIN_DIR}/boot1.bin of=<device> bs=512 seek=1 &&
dd if=${BIN_DIR}/boot2.bin of=<device> bs=1024 seek=32 &&
sync

The bootloaders are placed at fixed offsets at the start of the boot device. This simple layout avoids the complexity of file systems and is straightforward to implement.

Source Code and Boot Demo

The mechanism by which the stage-1 bootloader loads and executes the stage-2 bootloader is provided by the OpenBoot client interface services. They provide specific functions for opening a boot device and reading from or writing to data at designated offsets. My stage-1 bootloader follows the following sequence:

  1. Invokes the OpenBoot finddevice() client interface to open a handle for the /chosen node.
  2. Invokes the OpenBoot getprop() client interface to open handles for stdin, stdout, and bootpath, which are located under the /chosen node.
  3. Invokes the OpenBoot claim() client interface to allocate a memory buffer for the stage-2 bootloader.
  4. Invokes the OpenBoot seek() and read() client interfaces to load the stage-2 bootloader into the allocated memory buffer.
  5. Invokes the OpenBoot execute-buffer() client interface to execute the stage-2 bootloader.

Once the stage-2 bootloader starts executing, it captures the current state of the machine registers and prints them to the console. The complete source code for the bootloaders is located here: sparcv9_bootloader.tar.gz.

{0} ok boot /pci@0/pci@0/pci@1/pci@0/pci@1/pci@0/usb@0,2/storage@3/disk@0
Boot device: /pci@0/pci@0/pci@1/pci@0/pci@1/pci@0/usb@0,2/storage@3/disk@0  File and args: 

Running SPARC V9 stage-1 bootloader

Allocating memory for stage-2 bootloader:
addr=0xFE982000, size=0x100000

Reading stage-2 bootloader:
ihandle_bootpath=0xFEB50608, offset=0x8000, size=0x100000


Running SPARC V9 stage-2 bootloader

Displaying machine registers:

i0          0x00000000FEBA9D88  4273642888
i1          0x0000000000000027  39
i2          0x0000000000000000  0
i3          0x00000000000000FF  255
i4          0x0000000000000030  48
i5          0x0000000000000036  54
i6 (fp)     0x00000000FEBA93D1  4273640401
i7          0x0000000000100138  1048888

l0          0x000000000000298E  10638
l1          0x0000000000000000  0
l2          0x000000000000257D  9597
l3          0x00000000000010AC  4268
l4          0x0000000000040008  262152
l5          0x0000000000000001  1
l6          0x0000000000004000  16384
l7          0x00000000080010D0  134222032

o0          0x0000000000000000  0
o1          0x000000000003C40A  246794
o2          0x000000000004D32C  316204
o3          0x00000000F025EFE4  4029018084
o4          0x00000000FEE82F10  4276629264
o5          0x00000000FEE81798  4276623256
o6 (sp)     0x00000000FEBA9321  4273640225
o7          0x0000000000100490  1049744

g0          0x0000000000000000  0
g1          0x0000000000000021  33
g2          0x0000000000000001  1
g3          0x0000000000000000  0
g4          0x0000000000000000  0
g5          0x0000000000000000  0
g6          0x0000000000000000  0
g7          0x0000000000000000  0

pc          0x0000000000101A54
ccr         0b00010001 (xcc=c, icc=c)
asi         0x0000000000000000
tick        0x0000000E02465966

pil         13
tba         0x00000000F0200000
tl          0
gl          0
pstate      0b0000000010110 (tct=0, cle=0, tle=0, mm=0, pef=1, am=0, priv=1, ie=1)

cwp         2
cansave     4
canrestore  2
cleanwin    7
otherwin    0
wstate      0x00

Press any key to exit: 
Program terminated
{0} ok

For further details on the SPARC V9 architecture and its registers, refer to the February 1993 Microprocessor Report article listed in the References section below.

References

Microprocessor Report SPARC V9 Adds Wealth Of New Features

Friday, December 12, 2025

Why Agile left me disappointed and frustrated

Agile software development

Agile can be defined in various ways; the following is one such definition:

Agile software development is an umbrella term for a set of frameworks and practices based on the values and principles expressed in the Manifesto for Agile Software Development and the 12 Principles behind it.

Some of the claimed benefits of Agile include:

  • Faster delivery of working software through iterative development
  • Improved adaptability, allowing teams to respond quickly to changing requirements
  • Greater customer involvement and continuous feedback
  • Better visibility and transparency throughout the project
  • Enhanced team collaboration and communication
  • Early identification of risks and issues through frequent reviews

I did not choose Agile myself; it was imposed on all of us by the company I worked for at the time. From the very beginning, I was never fond of it, and over the following decade I became increasingly aware of its problems and developed a strong dislike for it.

Overbearing, nanny-like process

I am aware that Agile can be applied in different ways and practised with varying approaches. Some argue that if Agile becomes too overbearing or inflexible, it is being implemented incorrectly. However, it often seems that any issues with Agile are dismissed, with the blame consistently placed on the people following the process rather than the methodology itself.

The company I worked for insisted on using the Agile methodology with a Scrum framework. In retrospect, I believe Scrum was the worst part. Simple, straightforward development tasks were transformed into a bizarre kind of kindergarten role-playing, complete with Scrum Masters and user stories. Every working day, people were forced to gather at 10 a.m., march into a meeting room like mindless robots, and repeat the same phrases: “Yesterday I did this…” and “Today I will do this…”, while others stood around silently, staring at the walls or the floor. This monotonous ritual continued for weeks, months, and years, for as long as anyone had the patience to remain with the company.

The Agile Manifesto

The Agile Manifesto outlines the core principles of Agile. I would like to offer my own perspective on its twelve principles.

1. Our highest priority is to satisfy the customer through the early and continuous delivery of valuable software.

The highest priority should not be customer satisfaction at the expense of everything else. This approach is misguided, because customer satisfaction does not depend solely on early software releases. There are many other crucial factors, such as usability, scalability, reliability, and support. Releasing software too early increases the likelihood of defects and design flaws, which can ultimately undermine the user experience.

The drive for early and continuous software delivery can also have a negative impact on team morale. I have personally experienced this many times when management, guided by this principle, set unrealistic deadlines and expected a constant stream of fully tested features, even when the time and resources were insufficient to deliver them.

2. Welcome changing requirements, even late in development. Agile processes harness change for the customer’s competitive advantage.

No, push back against this if you can. It is not an effective way to develop complex projects. The desire to please and satisfy the customer often comes at the expense of the development team. If requirements are constantly changing, this likely indicates that the requirements specification was never properly completed or that the project is inherently too experimental.

Nobody wants to work overtime or on weekends just to meet a project deadline when the customer decides to change the requirements. While Agile does not explicitly mandate such working practices, in reality, this is how many companies operate.

3. Deliver working software frequently, from a couple of weeks to a couple of months, with a preference to the shorter timescale.

Why rush? Are you competing to see who can type the fastest on a keyboard? It all depends on the project. Some projects can be delivered in stages, while others should not be rushed and may take many months before anything tangible can be presented to the customer. The development team should rely on their own judgement and experience to determine the optimal schedule. Just because somebody expects a feature within a week does not necessarily mean it is feasible given the current resources.

Management can sometimes develop a misguided perception that if you are not delivering working software on a weekly basis, you are somehow lazy or incompetent. The constant pressure to justify your role to the company by demonstrating measurable progress each sprint can be overwhelming. It is far more valuable to have knowledgeable and motivated software developers who are not micromanaged by individuals lacking deep technical expertise. Trust developers to do their jobs and to decide when features are ready for release.

4. Business people and developers must work together daily throughout the project.

“Must” is a strong word here. If specific business requirements need to be implemented in software, it can be helpful for developers to understand some of the underlying business practices. This is usually addressed during requirements gathering and can be a valuable interaction. However during the software development, the phrase "too many cooks spoil the broth" is also appropriate here. Software developers need to concentrate on their development tasks and have the freedom to innovate. Frequent requests and critiques from those without deep technical knowledge can often be unwelcome.

5. Build projects around motivated individuals. Give them the environment and support they need, and trust them to get the job done.

Completely agree with this.

6. The most efficient and effective method of conveying information to and within a development team is face-to-face conversation.

This is not always the case. In fact, I personally spent many long hours in face-to-face discussions with people who refused to read documentation or who simply wanted to appear busy, organising numerous repetitive meetings without accomplishing much.

7. Working software is the primary measure of progress.

Absolutely not! People have different skills and can contribute in many ways: updating documentation, fixing bugs, providing help and training to junior developers, volunteering to administer company web or email servers, assisting with customer escalations, and more.

8. Agile processes promote sustainable development. The sponsors, developers, and users should be able to maintain a constant pace indefinitely.

Life is more complicated than this. Greater emphasis should be placed on people, as they naturally experience ups and downs. Maintaining a constant pace can be difficult due to various life events such as bereavement, depression, medical issues, or personal conflicts at home. Treat people fairly and give them some leeway when they need it.

Even in the absence of external pressures, when life is otherwise smooth, constant demands from so-called “efficiency experts” to work better, harder, and faster can still lead some people to develop symptoms of anxiety.

9. Continuous attention to technical excellence and good design enhances agility.

Yes people should aim for technical excellence and good design.

10. Simplicity–the art of maximizing the amount of work not done–is essential.

That sounds ideal, but in reality, developers often have to maintain legacy codebases full of cruft and complexity. It is important to be pragmatic and accept that writing messy code cannot always be avoided.

11. The best architectures, requirements, and designs emerge from self-organizing teams.

Is this a law of nature? If not, I would take it with a very large pinch of salt.

12. At regular intervals, the team reflects on how to become more effective, then tunes and adjusts its behavior accordingly.

It is ultimately up to the team to decide. If they do not find this activity useful, they should not be forced to carry it out at regular intervals.

Final thoughts

Personally, I feel that Agile and its Manifesto are largely overhyped. They have created a huge industry for companies to sell books and training courses. Ultimately, just as I do not need a life coach to guide me through my daily tasks, I also do not need Agile to tell me how to organise my software development tasks.

Monday, December 8, 2025

Installing NetBSD-10 on Raspberry Pi 4

Introduction

Installing NetBSD-10 on the Raspberry Pi 4 is much the same as installing it on the Raspberry Pi 3. Refer to this article for more details: Installing NetBSD-10 on Raspberry Pi 3.

There are however some differences:

  • The Raspberry Pi 4 uses different EEPROM to boot the system and can boot directly from GPT partitions.
  • The Raspberry Pi 4’s UEFI firmware is capable of booting NetBSD-10.

For further information on updating the Raspberry Pi 4’s EEPROM, please refer to this link: Raspberry Pi boot EEPROM.

NetBSD Installation Steps

Several steps are required to install NetBSD. The following sections explain each step in greater detail.

Step 1: Create GPT Partitions

The first step in installing NetBSD is to create required GPT partitions. In this example, a microSD card is used, which is detected as sd0 device:

gpt destroy sd0
gpt create -f sd0
gpt add -a 1m -l "EFI-system"  -t efi  -s 128m sd0
gpt add -a 1m -l "NetBSD-root" -t ffs  -s 8g   sd0
gpt add -a 1m -l "NetBSD-swap" -t swap -s 4g   sd0
gpt add -a 1m -l "NetBSD-var"  -t ffs  -s 4g   sd0
gpt add -a 1m -l "NetBSD-opt"  -t ffs          sd0

Step 2: Create File Systems

Execute dkctl to display the wedges currently mapped to partitions. Note that wedge mappings are dynamic and may differ on your system:

dkctl sd0 listwedges
/dev/rsd0: 5 wedges:
dk1: EFI-system, 262144 blocks at 2048, type: msdos
dk2: NetBSD-root, 16777216 blocks at 264192, type: ffs
dk3: NetBSD-swap, 8388608 blocks at 17041408, type: swap
dk4: NetBSD-var, 8388608 blocks at 25430016, type: ffs
dk5: NetBSD-opt, 90914816 blocks at 33818624, type: ffs

Use the wedge mappings shown above to create the necessary file systems. Note that raw special devices should be used; therefore, the wedges specified in the newfs commands should be /dev/rdkN, where N is the wedge ID:

newfs_msdos -F 32 /dev/rdk1

for i in rdk2 rdk4 rdk5
do
  newfs -B le -O 2ea /dev/${i} || break
done

The EFI partition is formatted as a FAT32 file system, while the remaining partitions (excluding swap) are formatted as NetBSD little-endian (option "-B le") UFS file systems. If you are using the NetBSD big-endian (evbarm-aarch64eb) port, adjust the newfs byte-order option accordingly to create big-endian file systems.

Step 3: Mount File Systems and Extract Files

Mount all file systems:

mkdir /mnt-netbsd-root && mount -o log NAME=NetBSD-root /mnt-netbsd-root &&
mkdir /mnt-netbsd-root/boot && mount -t msdos NAME=EFI-system /mnt-netbsd-root/boot &&
mkdir /mnt-netbsd-root/var && mount -o log NAME=NetBSD-var /mnt-netbsd-root/var &&
mkdir /mnt-netbsd-root/opt && mount -o log NAME=NetBSD-opt /mnt-netbsd-root/opt

Download Raspberry Pi 4 UEFI firmware, NetBSD EFI bootloader, and NetBSD sets. Note that the X11 sets are not included, as the Raspberry Pi 4 will be used in headless/server mode in this setup:

ftp https://github.com/pftf/RPi4/releases/download/v1.50/RPi4_UEFI_Firmware_v1.50.zip
ftp https://cdn.netbsd.org/pub/NetBSD/NetBSD-10.1/evbarm-aarch64/installation/misc/bootaa64.efi

mkdir netbsd_sets
for i in base comp etc games kern-GENERIC64 man misc modules rescue text
do
  ftp -o netbsd_sets/${i}.tar.xz https://cdn.netbsd.org/pub/NetBSD/NetBSD-10.1/evbarm-aarch64/binary/sets/${i}.tar.xz || break
done

Install UEFI firmware and NetBSD bootloader into boot partition:

unzip -d /mnt-netbsd-root/boot RPi4_UEFI_Firmware_v1.50.zip
mkdir -p /mnt-netbsd-root/boot/EFI/BOOT && cp bootaa64.efi /mnt-netbsd-root/boot/EFI/BOOT
sync

Extract NetBSD sets:

for i in base comp etc games kern-GENERIC64 man misc modules rescue text
do
  tar -C /mnt-netbsd-root -xpf netbsd_sets/${i}.tar.xz || break
done
sync

Step 4: Configure NetBSD

Create the /etc/fstab file:

cat > /mnt-netbsd-root/etc/fstab << 'EOF'
NAME=EFI-system    /boot       msdos     rw                0 0
NAME=NetBSD-root   /           ffs       rw,noatime,log    1 1
NAME=NetBSD-var    /var        ffs       rw,noatime,log    1 1
NAME=NetBSD-opt    /opt        ffs       rw,noatime,log    1 2
NAME=NetBSD-swap   none        swap      sw,dp             0 0
kernfs             /kern       kernfs    rw
procfs             /proc       procfs    rw
ptyfs              /dev/pts    ptyfs     rw
tmpfs              /var/shm    tmpfs     rw,-m1777,-sram%25
EOF

Create the additional mount points and the time zone symlink:

mkdir /mnt-netbsd-root/kern /mnt-netbsd-root/proc /mnt-netbsd-root/home
ln -sf ../usr/share/zoneinfo/Europe/London /mnt-netbsd-root/etc/localtime

Create the /etc/rc.conf file:

cat >> /mnt-netbsd-root/etc/rc.conf << 'EOF'
rc_configured=YES
critical_filesystems_local="${critical_filesystems_local} /opt"
hostname="rp4"
domainname="home.lan"

# Static IP config
net_interfaces="genet0"
ifconfig_genet0="inet 192.168.1.1 netmask 255.255.255.0"
dns_search="home.lan"
dns_nameservers="192.168.1.254"
defaultroute="192.168.1.254"

# Dynamic IP config
#dhcpcd=YES
#dhcpcd_flags="-qM genet0"

ip6addrctl=YES
ip6addrctl_policy="ipv4_prefer"

ntpdate=YES
sshd=YES
devpubd=YES
EOF

Step 5: Clean Up

Finally unmount all file systems and remove temporary mount points:

sync &&
umount /mnt-netbsd-root/boot &&
umount /mnt-netbsd-root/opt &&
umount /mnt-netbsd-root/var &&
umount /mnt-netbsd-root &&
rm -rf /mnt-netbsd-root

Step 6: First Boot

Attach the microSD card or USB disk to the Raspberry Pi 4 and power it on.

To enable automatic boot from a specific device, you may need to modify the UEFI firmware boot order: at the UEFI boot screen, press Esc, then navigate to Boot Maintenance Manager → Boot Optins → Change Boot Order, and move the microSD card and USB drive to the top of the list.

To disable the UEFI 3 GiB memory limit, follow these steps: at the UEFI boot screen, press Esc, then navigate to Device Manager → Raspberry Pi Configuration → Advanced Configuration, and disable the 3 GiB memory limit option.

The current version of the Raspberry Pi 4 UEFI firmware uses a default configuration that interferes with NetBSD’s CPU frequency settings. See my latest comments in this bug report for more details: port-arm/57139. To work around this issue: at the UEFI boot screen, press Esc, then navigate to Device Manager → Raspberry Pi Configuration → CPU Configuration, and set the following options CPU Clock = Custom and CPU Clock Rate = 60. This should allow NetBSD to set CPU frequency between 600MHz and 1500MHz.

Review afterboot(8), then carry out any additional configuration as required.

Note that the newly installed system will have an empty /etc/openssl/certs directory, which may cause problems when connecting to TLS enabled sites. To resolve this, generate the certificates using the /etc/rc.d/certctl_init script. This only needs to be done once, on the first boot:

/etc/rc.d/certctl_init onestart

Thursday, November 13, 2025

Installing NetBSD-10 on Raspberry Pi 3

Introduction

NetBSD-10 can be installed on the Raspberry Pi 3 either as a 32-bit operating system (evbarm-earmv7hf port) or as a 64-bit operating system (evbarm-aarch64 port). The 64-bit binaries deliver noticeably better performance, particularly for 64-bit integer operations. It is therefore recommended to download and install the 64-bit binaries.

The Raspberry Pi 3 boots an operating system using boot firmware, which is typically stored on a FAT32 partition. Two types of boot firmware are available:

NetBSD-10 does not seem to function correctly with UEFI firmware on the Raspberry Pi 3, as the kernel panics during boot. It is therefore recommended to use the original firmware instead.

The installation process outlined in this article requires access to an existing NetBSD system, either physical or virtual, in order to partition the disk, create file systems, extract the NetBSD sets, and configure the installed system. The interactive NetBSD installer sysinst is not used here, as it lacks the flexibility offered by the manual approach.

Disk Partitions: MBR and GPT

Two different disk partitioning schemes can be used: Master Boot Record (MBR) and GUID Partition Table (GPT). The Raspberry Pi 3 firmware supports booting only from MBR; however, GPT can still be used by employing a hybrid MBR/GPT. The hybrid MBR/GPT method enables the boot disk to be partitioned with GPT while tricking the firmware into recognising it as an MBR boot.

For straightforward disk partitioning requirements, pure MBR may be adequate; however, GPT (hybrid MBR/GPT in this case) offers several advantages, including support for disks larger than 2 TB and a more convenient overall structure.

NetBSD Installation Steps

Several steps are required to install NetBSD. The following sections explain each step in greater detail.

Step 1: Create GPT Partitions

The first step in installing NetBSD is to create required GPT partitions. In this example, a microSD card is used, which is detected as sd0 device:

gpt destroy sd0
gpt create -f sd0
gpt add -a 1m -l "EFI-system"  -t efi  -s 128m sd0
gpt add -a 1m -l "NetBSD-diag" -t ffs  -s 1g   sd0
gpt add -a 1m -l "NetBSD-root" -t ffs  -s 8g   sd0
gpt add -a 1m -l "NetBSD-swap" -t swap -s 1g   sd0
gpt add -a 1m -l "NetBSD-var"  -t ffs  -s 4g   sd0
gpt add -a 1m -l "NetBSD-opt"  -t ffs          sd0

The "gpt destroy" command clears disk of any previous GPT partitions and the "gpt create" command creates initial GPT table.

The "gpt add" commands create a number of partitions. The "-a 1m" flag aligns partitions on 1 MiB boundary. The "-l" flag label each parition with a specific name.

The following partitions are created:

  • EFI-system: FAT32 boot partition which will contain boot firmware and NetBSD kernel. This should be created first before any other partitions that may follow.
  • NetBSD-diag: The secondary diagnostic runtime environment that can be used to repair the primary runtime environment if it fails to boot properly.
  • NetBSD-root: The primary runtime environment that contains the root file system.
  • NetBSD-swap, NetBSD-var, NetBSD-opt: The remaining partitions used for various file systems.

The "gpt create" command also automatically creates a Protective MBR (PMBR) partition. This partition prevents legacy MBR-based disk utilities that are unaware of GPT from altering the disk sectors reserved for GPT.

Created GPT partitions can be viewd with "gpt show" command:

gpt show sd0
      start       size  index  contents
          0          1         PMBR
          1          1         Pri GPT header
          2         32         Pri GPT table
         34       2014         Unused
       2048     262144      1  GPT part - EFI System
     264192    2097152      2  GPT part - NetBSD FFSv1/FFSv2
    2361344   16777216      3  GPT part - NetBSD FFSv1/FFSv2
   19138560    2097152      4  GPT part - NetBSD swap
   21235712    8388608      5  GPT part - NetBSD FFSv1/FFSv2
   29624320   95109120      6  GPT part - NetBSD FFSv1/FFSv2
  124733440       2015         Unused
  124735455         32         Sec GPT table
  124735487          1         Sec GPT header

Sector 0 contains the MBR partition information, sector 1 contains the primary GPT header, and sectors 2–33 contain the primary GPT table. The first partition starts at sector 2048, followed by other NetBSD partitions.

Created MBR partitions can be viewd with "fdisk" command:

fdisk sd0
Partition table:
0: GPT Protective MBR (sysid 238)
    start 1, size 124735487 (60906 MB, Cyls 0-7764/108/24)
        PBR is not bootable: Bad magic number (0x0000)
1: <UNUSED>
2: <UNUSED>
3: <UNUSED>
No active partition.
Drive serial number: 0 (0x00000000)

The PMBR partition (sysid 238) is located at index 0 and spans the entire disk (sectors 1–124735487). This corresponds to the same disk area allocated for GPT.

Step 2: Create Hybrid MBR/GPT Layout

When the Raspberry Pi 3 is powered on, it locates the boot disk and attempts to boot from the first (index 0) MBR partition formatted with the FAT32 file system (sysid 12). To meet this requirement, a hybrid MBR/GPT layout must be created. The NetBSD "fdisk -gu" command can be used to modify the MBR at sector 0 without affecting any of the GPT partitions.

To create a hybrid MBR/GPT layout, the following modifications should be applied:

  • MBR partition 0 should be assigned sysid 12 and correspond to the sectors containing GPT EFI partition. This is the MBR boot partition used by the Raspberry Pi 3 during power-on.
  • MBR partition 1 should be assigned sysid 238 and correspond to the sectors containing GPT metadata. This is a placeholder PMBR partition. The exact sectors are not critical, provided they do not overlap with those of MBR partition 0. Mapping it to the sectors holding the GPT primary header and table is sufficient.

The actual MBR partition modifications are shown below:

fdisk -gu sd0
...
Which partition do you want to change?: [none] 0
The data for partition 0 is:
GPT Protective MBR (sysid 238)
    start 1, size 124735487 (60906 MB, Cyls 0-7764/108/24)
        PBR is not bootable: Bad magic number (0x0000)
sysid: [0..255 default: 238] 12
start: [0..7764cyl default: 1, 0cyl, 0MB] 2048
size: [0..7764cyl default: 124733440, 7764cyl, 60905MB] 262144
bootmenu: [] (space to clear)
The bootselect code is not installed, do you want to install it now? [n]
...
Which partition do you want to change?: [none] 1
The data for partition 1 is:
<UNUSED>
sysid: [0..255 default: 169] 238
start: [0..7764cyl default: 1, 0cyl, 0MB] 1
size: [0..0cyl default: 2047, 0cyl, 1MB] 2047
bootmenu: [] (space to clear)
The bootselect code is not installed, do you want to install it now? [n]
...
Which partition do you want to change?: [none] 
...
Installed bootfile doesn't support required options.
Update the bootcode from /usr/mdec/mbr? [n] 
...
Should we write new partition table? [n] y

The final hybrid MBR/GPT partition layout should look like this:

fdisk sd0
Partition table:
0: Primary DOS with 32 bit FAT - LBA (sysid 12)
    start 2048, size 262144 (128 MB, Cyls 0/32/33-16/113/33)
        PBR is not bootable: All bytes are identical (0x00)
1: GPT Protective MBR (sysid 238)
    start 1, size 2047 (1 MB, Cyls 0-0/32/32)
        PBR is not bootable: Bad magic number (0x0000)
2: <UNUSED>
3: <UNUSED>
No active partition.
Drive serial number: 0 (0x00000000)

Step 3: Create File Systems

Execute dkctl to display the wedges currently mapped to partitions. Note that wedge mappings are dynamic and may differ on your system:

dkctl sd0 listwedges
/dev/rsd0: 6 wedges:
dk1: EFI-system, 262144 blocks at 2048, type: msdos
dk2: NetBSD-diag, 2097152 blocks at 264192, type: ffs
dk3: NetBSD-root, 16777216 blocks at 2361344, type: ffs
dk4: NetBSD-swap, 2097152 blocks at 19138560, type: swap
dk5: NetBSD-var, 8388608 blocks at 21235712, type: ffs
dk6: NetBSD-opt, 95109120 blocks at 29624320, type: ffs

Use the wedge mappings shown above to create the necessary file systems. Note that raw special devices should be used; therefore, the wedges specified in the newfs commands should be /dev/rdkN, where N is the wedge ID:

newfs_msdos -F 32 /dev/rdk1

for i in rdk2 rdk3 rdk5 rdk6
do
  newfs -B le -O 2ea /dev/${i} || break
done

The EFI partition is formatted as a FAT32 file system, while the remaining partitions (excluding swap) are formatted as NetBSD little-endian (option "-B le") UFS file systems. If you are using the NetBSD big-endian (evbarm-aarch64eb) port, adjust the newfs byte-order option accordingly to create big-endian file systems.

Step 4: Mount File Systems and Extract Files

Mount all file systems associated with the primary and secondary runtime environments:

mkdir /mnt-netbsd-root && mount -o log NAME=NetBSD-root /mnt-netbsd-root &&
mkdir /mnt-netbsd-root/boot && mount -t msdos NAME=EFI-system /mnt-netbsd-root/boot &&
mkdir /mnt-netbsd-root/var && mount -o log NAME=NetBSD-var /mnt-netbsd-root/var &&
mkdir /mnt-netbsd-root/opt && mount -o log NAME=NetBSD-opt /mnt-netbsd-root/opt &&
mkdir /mnt-netbsd-diag && mount -o log NAME=NetBSD-diag /mnt-netbsd-diag

Download Raspberry Pi 3 boot firmware and NetBSD sets. Note that the X11 sets are not included, as the Raspberry Pi 3 will be used in headless/server mode in this setup:

ftp https://github.com/raspberrypi/firmware/releases/download/1.20240902/raspi-firmware_1.20240902.orig.tar.xz

mkdir netbsd_sets
for i in base comp dtb etc games kern-GENERIC64 man misc modules rescue text
do
  ftp -o netbsd_sets/${i}.tar.xz https://cdn.netbsd.org/pub/NetBSD/NetBSD-10.1/evbarm-aarch64/binary/sets/${i}.tar.xz || break
done

Extract boot firmware and NetBSD kernel into boot partition:

tar -C /mnt-netbsd-root/boot --strip-components=2 -xf raspi-firmware_1.20240902.orig.tar.xz '*/boot/*' &&
tar -C /mnt-netbsd-root/boot -xf netbsd_sets/kern-GENERIC64.tar.xz netbsd.img  &&
sync

ls -l /mnt-netbsd-root/boot
total 76342
-rwxr-xr-x  1 root  wheel      1594 Sep  2  2024 LICENCE.broadcom
-rwxr-xr-x  1 root  wheel     52476 Sep  2  2024 bootcode.bin
-rwxr-xr-x  1 root  wheel      7327 Sep  2  2024 fixup.dat
-rwxr-xr-x  1 root  wheel      5456 Sep  2  2024 fixup4.dat
-rwxr-xr-x  1 root  wheel      3232 Sep  2  2024 fixup4cd.dat
-rwxr-xr-x  1 root  wheel      8450 Sep  2  2024 fixup4db.dat
-rwxr-xr-x  1 root  wheel      8450 Sep  2  2024 fixup4x.dat
-rwxr-xr-x  1 root  wheel      3232 Sep  2  2024 fixup_cd.dat
-rwxr-xr-x  1 root  wheel     10295 Sep  2  2024 fixup_db.dat
-rwxr-xr-x  1 root  wheel     10295 Sep  2  2024 fixup_x.dat
-rwxr-xr-x  1 root  wheel  16775144 Dec 16  2024 netbsd.img
-rwxr-xr-x  1 root  wheel   2983488 Sep  2  2024 start.elf
-rwxr-xr-x  1 root  wheel   2259296 Sep  2  2024 start4.elf
-rwxr-xr-x  1 root  wheel    811804 Sep  2  2024 start4cd.elf
-rwxr-xr-x  1 root  wheel   3756808 Sep  2  2024 start4db.elf
-rwxr-xr-x  1 root  wheel   3006920 Sep  2  2024 start4x.elf
-rwxr-xr-x  1 root  wheel    811804 Sep  2  2024 start_cd.elf
-rwxr-xr-x  1 root  wheel   4828616 Sep  2  2024 start_db.elf
-rwxr-xr-x  1 root  wheel   3730536 Sep  2  2024 start_x.elf

Extract NetBSD sets into the primary and secondary runtime environments:

for i in base comp dtb etc games man misc modules rescue text
do
  tar -C /mnt-netbsd-root -xpf netbsd_sets/${i}.tar.xz || break
done

for i in base dtb etc games man misc modules rescue text
do
  tar -C /mnt-netbsd-diag -xpf netbsd_sets/${i}.tar.xz || break
done &&
rm -rf /mnt-netbsd-diag/boot/*

sync

We remove all files under /mnt-netbsd-diag/boot/ because they have already been extracted into the boot partition, which is shared by both runtime environments.

Step 5: Configure NetBSD Primary Runtime Environment

Create the cmdline.txt and config.txt files in the boot partition:

cat > /mnt-netbsd-root/boot/cmdline.txt << 'EOF'
root=NAME=NetBSD-root console=fb
#root=NAME=NetBSD-diag console=fb
EOF

cat > /mnt-netbsd-root/boot/config.txt << 'EOF'
# For more info see:
# https://www.raspberrypi.com/documentation/computers/config_txt.html
# https://www.raspberrypi.com/documentation/computers/legacy_config_txt.html

arm_64bit=1
upstream_kernel=1
os_prefix=dtb/broadcom/
cmdline=/cmdline.txt
kernel=/netbsd.img
kernel_address=0x200000
enable_uart=1
force_turbo=0
EOF

Create the /etc/fstab file:

cat > /mnt-netbsd-root/etc/fstab << 'EOF'
NAME=EFI-system    /boot       msdos     rw                0 0
NAME=NetBSD-root   /           ffs       rw,noatime,log    1 1
NAME=NetBSD-var    /var        ffs       rw,noatime,log    1 1
NAME=NetBSD-opt    /opt        ffs       rw,noatime,log    1 2
NAME=NetBSD-swap   none        swap      sw,dp             0 0
kernfs             /kern       kernfs    rw
procfs             /proc       procfs    rw
ptyfs              /dev/pts    ptyfs     rw
tmpfs              /var/shm    tmpfs     rw,-m1777,-sram%25
EOF

Create the additional mount points and the time zone symlink:

mkdir /mnt-netbsd-root/kern /mnt-netbsd-root/proc /mnt-netbsd-root/home
ln -sf ../usr/share/zoneinfo/Europe/London /mnt-netbsd-root/etc/localtime

Create the /etc/rc.conf file:

cat >> /mnt-netbsd-root/etc/rc.conf << 'EOF'
rc_configured=YES
critical_filesystems_local="${critical_filesystems_local} /opt"
hostname="rp3"
domainname="home.lan"

# Static IP config
net_interfaces="mue0"
ifconfig_mue0="inet 192.168.1.1 netmask 255.255.255.0 tso4 tso6 ip4csum tcp4csum tcp6csum udp4csum udp6csum"
dns_search="home.lan"
dns_nameservers="192.168.1.254"
defaultroute="192.168.1.254"

# Dynamic IP config
#dhcpcd=YES
#dhcpcd_flags="-qM mue0"

ip6addrctl=YES
ip6addrctl_policy="ipv4_prefer"

ntpdate=YES
sshd=YES
devpubd=YES
EOF

Step 6: Configure NetBSD Secondary Diagnostic Runtime Environment

Create the /etc/fstab file:

cat > /mnt-netbsd-diag/etc/fstab << 'EOF'
NAME=EFI-system    /boot       msdos     rw                0 0
NAME=NetBSD-diag   /           ffs       rw,noatime,log    1 1
NAME=NetBSD-swap   none        swap      sw,dp             0 0
kernfs             /kern       kernfs    rw
procfs             /proc       procfs    rw
ptyfs              /dev/pts    ptyfs     rw
tmpfs              /var/shm    tmpfs     rw,-m1777,-sram%25
EOF

Create the additional mount points and the time zone symlink:

mkdir /mnt-netbsd-diag/kern /mnt-netbsd-diag/proc /mnt-netbsd-diag/home
ln -sf ../usr/share/zoneinfo/Europe/London /mnt-netbsd-diag/etc/localtime

Create the /etc/rc.conf file:

cat >> /mnt-netbsd-diag/etc/rc.conf << 'EOF'
rc_configured=YES
hostname="rp3-diag"
domainname="home.lan"

# Static IP config
net_interfaces="mue0"
ifconfig_mue0="inet 192.168.1.1 netmask 255.255.255.0 tso4 tso6 ip4csum tcp4csum tcp6csum udp4csum udp6csum"
dns_search="home.lan"
dns_nameservers="192.168.1.254"
defaultroute="192.168.1.254"

# Dynamic IP config
#dhcpcd=YES
#dhcpcd_flags="-qM mue0"

ip6addrctl=YES
ip6addrctl_policy="ipv4_prefer"

ntpdate=YES
sshd=YES
devpubd=YES
EOF

Step 7: Clean Up

Finally unmount all file systems and remove temporary mount points:

sync &&
umount /mnt-netbsd-root/boot &&
umount /mnt-netbsd-root/opt &&
umount /mnt-netbsd-root/var &&
umount /mnt-netbsd-root &&
umount /mnt-netbsd-diag &&
rm -rf /mnt-netbsd-root /mnt-netbsd-diag

Step 8: First Boot

Attach the microSD card or USB disk to the Raspberry Pi 3 and power it on. NetBSD can now be booted into either the primary or secondary runtime environment, selected by modifying the cmdline.txt file in the boot partition. Review afterboot(8), then carry out any additional configuration as required.

Note that the newly installed system will have an empty /etc/openssl/certs directory, which may cause problems when connecting to TLS enabled sites. To resolve this, generate the certificates using the /etc/rc.d/certctl_init script. This only needs to be done once, on the first boot:

/etc/rc.d/certctl_init onestart

Tuesday, October 28, 2025

SPARC V9 Boot Process – Part 1: bootblk

Introduction

SPARC V9 is a 64-bit Reduced Instruction Set Computer (RISC) architecture introduced in the mid-1990s that was widely used in servers and workstations manufactured by Sun Microsystems. As its popularity gradually declined, the hardware lost the performance advantage it once held in its early years and is now considered a legacy architecture.

Despite its age and competitive obsolescence, SPARC V9 is an interesting architecture to study. It has several unique characteristics, and some of us who lived through the dot-com boom still enjoy using Sun hardware for nostalgic reasons.

This article takes a closer look at the overlooked elements of the SPARC V9 boot process. It explores the OpenBoot firmware and demonstrates how to implement a first-stage bootloader from scratch.

OpenBoot

OpenBoot represents Sun Microsystems’ implementation of the IEEE 1275 Open Firmware standard, providing facilities for hardware configuration, system diagnostics, and operating system initialisation on SPARC V9 architectures. After completing the Power-On Self-Test (POST), OpenBoot firmware displays the ok prompt (provided the autoboot property is set to false), allowing the boot process to be started from a disk or over a network.

The OpenBoot PROM includes an FCode interpreter, which enables the execution of code written in a machine-independent interpreted language known as FCode. Firmware for pluggable storage adapters or even the first-stage bootloader can be written in FCode, allowing it to remain portable across different CPU architectures that implement a compatible FCode interpreter.

During initialisation, OpenBoot constructs the device tree — a hierarchical data structure that represents the system’s hardware and configuration parameters, and may also include firmware device drivers. The device tree consists of device nodes, where each node may have properties, methods, data, and other child nodes.

There are several standard system nodes; however, the /chosen node includes a number of useful properties that can be accessed by the first-stage bootloader. This node contains parameters chosen or specified at runtime:

Property Name Encoding Value
name string "chosen"
stdin integer Ihandle for the console input.
stdout integer Ihandle for the console output.
bootpath string The device path for the last boot device.
bootargs string The arguments to the last boot command.
memory integer Ihandle for the package that describes physical memory.
mmu integer Ihandle for the package that describes memory management unit.

Below is an example of what it looks like on a Sun T5220:

{0} ok dev /chosen
{0} ok .properties
bootargs                 
bootpath                 
mmu                      fff74080 
memory                   fff74290 
stdout                   feb51d88 
stdin                    feb51f90 
stdout-#lines            ffffffff 
name                     chosen

For example, the first-stage bootloader can use the hexadecimal value of the stdin ihandle to read text from the console, and the hexadecimal value of the stdout ihandle to write text to the console.

OpenBoot provides two primary interfaces: the user interface and the client interface. The user interface presents the ok prompt and offers a command-line environment for user interaction. The client interface supplies a set of services that can be invoked by a client program. A client program may be the first-stage bootloader, known as bootblk, and can comprise either FCode bytecodes or 64-bit SPARC V9 instructions packaged within an ELF64 binary.

In the following sections, I describe how a 64-bit ELF64 binary can call the OpenBoot client interface handler and invoke a range of services.

OpenBoot Client Interface Services

The OpenBoot firmware provides a number of services that can be invoked by the first-stage bootblk bootloader. The services are accessed via the client interface handler (a function pointer), with arguments and return values passed through an array of fixed size cells. A cell is a unit of storage in OpenBoot and on SPARC V9 its size is 8 bytes (64 bits).

The array passed to the OpenBoot client interface handler comprises the following cells:

Cell Name Description
service Pointer specifying the address of a '\0' terminated string of the client interface service
Num args Integer specifying the number of input arguments to the client interface service
Num rets Integer specifing the number of return values from the client interface service
Arg1 ...
Arg2 ...
ArgN Input arguments to the client interface service
Ret1 ...
Ret2 ...
RetN Return values from the client interface service

In the C programming language, a call to the write service can be implemented as shown in the following example:

/* 64-bit Open Firmware uses 64-bit storage cells */
typedef uint64_t ofw_cell_cdt;

/*
* Open Firmware ihandle is a handle/reference identifying a package instance.
* The ihandle always seems to be a 32-bit integer.
*/
typedef uint32_t ihandle_cdt;
#define OFW_IHANDLE_INVALID ((ihandle_cdt)-1)

/* Open Firmware Client Interface Function handler */
static int (*ofw_cif_handler)(void *);

/* Open Firmware ihandles for stdin and stdout */
static ihandle_cdt ihandle_stdin    = OFW_IHANDLE_INVALID;
static ihandle_cdt ihandle_stdout   = OFW_IHANDLE_INVALID;
static ihandle_cdt ihandle_bootpath = OFW_IHANDLE_INVALID;

/*
* Write to Open Firmware stdout.
*
* Arguments:
* strbuf     (in): String buffer.
* strbuf_len (in): String buffer length in chars.
*
* Pre-cond:
* 1. ofw_init() was executed earlier.
* 2. strbuf points to a valid string buffer.
* 3. strbuf_len indicates the number of string chars to write to stdout.
*
* Post-cond:
* 1. Characters written from strbuf to stdout.
*
* Return:
* On success: number of bytes copied from strbuf to stdout.
* On failure: (uint32_t)-1.
*/
uint32_t ofw_write_stdout(const char *strbuf, uint32_t strbuf_len)
{
    ofw_cell_cdt cif_arg[7];

    /* If stdout ihandle is not initialised, return -1 */
    if (ihandle_stdout == OFW_IHANDLE_INVALID) { return (uint32_t)-1; }

    cif_arg[0] = (ofw_cell_cdt)"write";        /* Service */
    cif_arg[1] = (ofw_cell_cdt)3;              /* Number of arguments */
    cif_arg[2] = (ofw_cell_cdt)1;              /* Number of return values */
    cif_arg[3] = (ofw_cell_cdt)ihandle_stdout; /* Arg1: stdout ihandle */
    cif_arg[4] = (ofw_cell_cdt)strbuf;         /* Arg2: strbuf address */
    cif_arg[5] = (ofw_cell_cdt)strbuf_len;     /* Arg3: strbuf length */
    cif_arg[6] = (ofw_cell_cdt)-1;             /* Ret1: return value */

    /* Call into cif handler */
    ASSERT(ofw_cif_handler(cif_arg) == 0);
    return (uint32_t)cif_arg[6];
}

For a specification of the client interface services, consult the IEEE 1275-1994 standard.

Sun VTOC Disk Label and Bootblk Requirements

When OpenBoot attempts to boot from a disk drive, it checks for a valid Sun VTOC disk label in the first 512-byte sector of the boot device. If a valid disk label is not found, OpenBoot displays an error message and aborts the boot process.

Once the Sun disk label has been checked, OpenBoot reads the next 16 512-byte sectors and loads bootblk first-stage bootloader. Since the number of sectors read during this stage is hard-coded into the firmware, the size of bootblk is limited to approximately 8 KiB and should fit within the 16 sectors immediately following the disk label. In my testing on a Sun T5220, I was able to load a bootblk slightly over 10 KiB in size; however, larger bootblk binaries resulted in the firmware error: "ERROR: Last Trap: Fast Data Access MMU Miss".

I developed a utility called vtoc_label, which can write a fake Sun VTOC disk label to a file or disk. The integer values within the disk label must be stored in big-endian byte order; therefore, the utility employs byte-order conversion functions to translate between host and network byte order, allowing it to generate correct disk labels even on little-endian architectures.

The bootblk is primarily written in the C programming language, with a small section containing the _start function implemented in SPARC V9 assembly. The boot stages are as follows:

  1. Upon power-on, firmware begins to execute, probing devices, performing initialisation and running diagnostic routines.
  2. Firmware enables the Memory Management Unit (MMU) and sets up a basic SPARC V9 trap table.
  3. The boot command executes, specifying the boot device and boot arguments.
  4. The first 512-byte sector is read from the boot device and the Sun VTOC disk label is checked for validity.
  5. The bootblk binary is read from the next 16 512-byte sectors after the disk label.
  6. The address of the OpenBoot client interface handler is copied to register %o4.
  7. The bootblk binary format is checked (FCode, ELF64, etc) and then executed.
  8. Control is transferred to the bootblk binary (i.e. first-stage bootloader), which can then load a larger second-stage bootloader or an operating system kernel.

Source Code and Boot Demo

The complete source code for the bootloaders is located here: sparcv9_bootloader.tar.gz. Below follows a demonstration of a simple bootblk first-stage bootloader.

Initially, I compile the vtoc_label utility and boot1.bin binary. Then, I use the vtoc_label and dd utilities to write the disk label followed by the bootloader:

# Include SPARC V9 cross-tools in current PATH
export PATH=${HOME:?}/gcc-15.2.0_sparc64-unknown-elf/bin:${PATH}

# Build native vtoc_label utility
cd ${SRC_DIR}/vtoc_label && gcc -O2 -std=c11 -Wall -pedantic \
  -D_FILE_OFFSET_BITS=64 -D_POSIX_C_SOURCE=200809L \
  -o ${BIN_DIR}/vtoc_label vtoc_label.c

# Build SPARC V9 stage-1 bootloader
cd ${SRC_DIR}/boot1 && sparc64-unknown-elf-gcc \
  -Os -std=c11 -Wall -pedantic -ffreestanding -nostdlib -nostartfiles \
  -o ${BIN_DIR}/boot1.bin start.s main.c ofw.c util.c && \
sparc64-unknown-elf-strip ${BIN_DIR}/boot1.bin

# Write Sun VTOC disk label and bootloader
${BIN_DIR}/vtoc_label write <device> &&
dd if=${BIN_DIR}/boot1.bin of=<device> bs=512 seek=1 &&
sync

Finally, I insert the USB flash drive into the Sun T5220 and boot from the drive containing my bootblk binary, which demonstrates basic input and output functionality on the system console:

{0} ok boot /pci@0/pci@0/pci@1/pci@0/pci@1/pci@0/usb@0,2/storage@3/disk@0
Boot device: /pci@0/pci@0/pci@1/pci@0/pci@1/pci@0/usb@0,2/storage@3/disk@0  File and args: 

Running SPARC V9 stage-1 bootloader

ihandle_stdin  0x100348
ihandle_stdout 0x100358

Type your name: root
Hello root. Press any key to exit: 
Program terminated
{0} ok

In the second article: SPARC V9 Boot Process – Part 2: sysboot, I will examine how the first-stage bootloader can load and execute larger binaries and overcome the 8 KiB size limitation imposed by the firmware.

References

IEEE 1275-1994 Standard for Boot Firmware
IEEE Draft Std P1275.1/D14a Standard for Boot Firmware

Saturday, October 26, 2024

Setting up pkgsrc on Solaris or Illumos

NetBSD pkgsrc framework is a collection of make files, scripts and patches, used for downloading, configuring and building mostly open source software packages. See https://pkgsrc.org for more details.

Every quarter a new pkgsrc release is made. To keep up to date with the recent releases, download them from https://cdn.netbsd.org/pub/pkgsrc/.

Pkgsrc can also be used on Solaris or Illumos, however there are a few extra steps required to bootstrap it before it can be used. There are many different ways in how pkgsrc can be configured, depending on personal preferences. The notes below are mainly for myself and describe how I tend to use it on Solaris.

1. Decide which toolchain to use

On Solaris there may be multiple toolchains installed: Sun/Oracle studio compilers, GNU compilers, or LLVM compilers. Pkgsrc can support different toolchains, however a lot of open source software builds with the least problems using GNU compilers. For that reason I would recommend using GCC C and C++ compilers.

GCC compilers can be configured to use either Sun original binutils or GNU binutils. It is recommended to use GCC compilers configured with Sun binutils, as this seems to result in the least problems when building software from source. I have another article which describes how to build GCC compilers on Solaris from scratch. At the time of writing this article, gcc-14.X causes build failure with some packages as it has more pedantic error checking, hence using gcc-13.X provides a less painful experience.

Pkgsrc includes several GCC compilers, which can be built from source using pkgsrc framework and installed as pkgsrc packages, however in order to build a compiler from pkgsrc, you need to bootsrap pkgsrc with a native compiler. Bootstrapping pkgsrc builds various support binaries and if the native compiler is GCC, it links the binaries to GCC native compiler's shared libraries. Later when you build a new compiler from pkgsrc and tell it to use this compiler, then it will link later binaries to pkgsrc GCC compiler's share libraries. In the end, you end up with some binaries linked to one compiler and another set to another compiler. There are ways to hack around it, but on Solaris I always build GCC compilers from source and use them as the native compilers. This way I can have multiple compilers and have the freedom to install whichever GCC version that works best for me, instead of relying on pkgsrc for this task.

I normally install GCC compilers under /opt and then setup /opt/gcc symlink to point to a working compiler version.

2. Decide on pkgsrc install location and directory name

I normally install pkgsrc packages under /opt/pkg-<release_ver> and then setup /opt/pkg symlink to point the current stable packages directory. This way, I can use the same Solaris zone for building multiple pkgsrc releases concurrently. For example, I can use current /opt/pkg-2024Q2 packages while building and installing /opt/pkg-2024Q3 packages. If the new packages build and run correctly, I can later simply update the symlink and may be a few rc.d start scripts, as they hardcode the absolute paths to binaries.

3. Download pkgsrc stable release

Here "stable" is a relative term and probably applies mostly to NetBSD. Some packages may or may not build and work on Solaris/Illumos.

cd /opt &&
wget https://cdn.netbsd.org/pub/pkgsrc/pkgsrc-2024Q3/pkgsrc.tar.gz &&
gtar -xf pkgsrc.tar.gz &&
mv pkgsrc pkgsrc-2024Q3 &&
chown -RP root:root pkgsrc-2024Q3

4. Define environment variables for bootstrapping pkgsrc

Adjust these based on your own preferences.

MAKE_JOBS=128 &&
GCC_BASE="/opt/gcc" &&
PKGSRC_VER="2024Q3" &&
PKGSRC_BASE="/opt/pkgsrc-${PKGSRC_VER:?}" &&
PKG_BASE="/opt/pkg-${PKGSRC_VER:?}" &&
LD_OPTIONS="\
-L${PKG_BASE:?}/lib-gcc -R${PKG_BASE:?}/lib-gcc \
-L${PKG_BASE:?}/lib-gcc/sparcv9 -R${PKG_BASE:?}/lib-gcc/sparcv9" &&
PATH="${GCC_BASE:?}/bin:${PKG_BASE:?}/bin:${PKG_BASE:?}/sbin:/bin:/usr/bin:/sbin:/usr/sbin" &&
export LD_OPTIONS PATH

GCC_BASE is the base directory where native GCC compilers are installed. This consists of various sub-directories like bin/, lib/, etc.

PKGSRC_BASE is the base directory where the pkgsrc tree is located.

PKG_BASE is the base directory where binary pkgsrc packages get installed.

LD_OPTIONS is set for Solaris linker to hardcode the path for GCC shared libraries. See pkg/57685 why the usual LDFLAGS variable does not work here.

PATH should contain directories with: GCC compiler binaries, pkgsrc installed binaries and all other system binaries.

4. Copy GCC libraries to pkgsrc install directory

When building packages from source, some of them get linked to GCC shared libraries. We need to make sure that:

  1. We tell pkgsrc where to find GCC libraries. For that we export Solaris linker LD_OPTIONS environment variable.
  2. Packages continue to work correctly if the GCC compiler used to build them is later removed from the system. For that we physically copy required libraries to pkgsrc install directory.
test -d "${PKG_BASE:?}/lib-gcc/sparcv9" || \
	mkdir -p "${PKG_BASE:?}/lib-gcc/sparcv9" &&
gtar -C "${GCC_BASE:?}/lib" -cf - $(ls "${GCC_BASE:?}/lib/"lib*.so* | \
	while read i; do basename "$i"; done) | \
	gtar -C "${PKG_BASE:?}/lib-gcc" -xpf - &&
gtar -C "${GCC_BASE:?}/lib/sparcv9" -cf - $(ls "${GCC_BASE:?}/lib/sparcv9/"lib*.so* | \
	while read i; do basename "$i"; done) | \
	gtar -C "${PKG_BASE:?}/lib-gcc/sparcv9" -xpf -

5. Bootstrap pkgsrc

Here we build pkgsrc support binaries, using /opt/pkg-<release_ver>.objects as the base directory for object files and /opt/pkg-<release_ver>.distfiles as the base directory for downloaded source archives.

rm -rf "/opt/pkg-${PKGSRC_VER:?}.objects/bootstrap" &&
env \
MAKECONF="" \
PKGMAKECONF="" \
DISTDIR="/opt/pkg-${PKGSRC_VER:?}.distfiles" \
PKGSRC_COMPILER=gcc \
USE_NATIVE_GCC=yes \
CC="${GCC_BASE:?}/bin/gcc" \
CXX="${GCC_BASE:?}/bin/g++" \
CFLAGS="-O2 -mcpu=v9" \
CXXFLAGS="-O2 -mcpu=v9" \
"${PKGSRC_BASE:?}/bootstrap/bootstrap" \
	--prefer-pkgsrc yes \
	--make-jobs     "${MAKE_JOBS:?}" \
	--workdir       "/opt/pkg-${PKGSRC_VER:?}.objects/bootstrap" \
	--prefix        "${PKG_BASE:?}" \
	--pkgdbdir      "${PKG_BASE:?}/db/pkg"

6. Generate pkgsrc mk.conf

The mk.conf file is used by pkgsrc when building software packages from source. It contains various preference settings that specifies file locations, build options and compiler flags.

mv "${PKG_BASE:?}/etc/mk.conf" "${PKG_BASE:?}/etc/mk.conf.orig" &&
cat > "${PKG_BASE:?}/etc/mk.conf" << EOF
.ifdef BSD_PKG_MK       # begin pkgsrc settings

# Build packages concurrently. On Solaris this requires installing pkgtools/shlock package
#PKGSRC_LOCKTYPE=          sleep
#PKGSRC_SLEEPSECS=         60

OBJHOSTNAME=              yes

ABI=                      64
UNPRIVILEGED=             no
X11_TYPE=                 modular
MAKE_JOBS=                16

# WARNING: Changing PREFER_* after bootstrap will require rebuilding all
# packages with a dependency that switched between native/pkgsrc.
PREFER_PKGSRC=            yes
SKIP_LICENSE_CHECK=       yes
DEPENDS_TARGET=           package-install

LOCALBASE=                /opt/pkg-${PKGSRC_VER:?}
PKG_DBDIR=                \${LOCALBASE}/db/pkg
SYSCONFBASE=              \${LOCALBASE}/etc
VARBASE=                  \${LOCALBASE}/var
PKG_TOOLS_BIN=            \${LOCALBASE}/sbin
PKGINFODIR=               info
PKGMANDIR=                man

# Use Solaris /usr/bin/bash as the shell
TOOLS_PLATFORM.sh?=       /usr/bin/bash

# Use pkgsrc install
TOOLS_PLATFORM.install?=  \${LOCALBASE}/bin/bsdinstall

# Use pkgsrc sed. May need to be uncommented on older versions of Solaris.
# Check pkgsrc etc/mk.conf.orig to see if this line was enabled.
#TOOLS_PLATFORM.sed?=      \${LOCALBASE}/bin/nbsed

# Enable when using native GCC
PKGSRC_COMPILER=          gcc
USE_NATIVE_GCC=           yes
CC=                       /opt/gcc/bin/gcc
CXX=                      /opt/gcc/bin/g++

# Enable when using pkgsrc GCC
#USE_PKGSRC_GCC=           yes
#USE_PKGSRC_GCC_RUNTIME=   yes
#GCC_REQD=                 13

CFLAGS+=                  -O2 -mcpu=v9
CXXFLAGS+=                -O2 -mcpu=v9

DBG=                      # prevent DBG from adding default optimizer flags

PACKAGES=                 /opt/pkg-${PKGSRC_VER:?}.packages
WRKOBJDIR=                /opt/pkg-${PKGSRC_VER:?}.objects
DISTDIR=                  /opt/pkg-${PKGSRC_VER:?}.distfiles

MASTER_SORT=              .uk .be .fr .de .dk .ch .it .fi .pl .no .nl .se
.endif                  # end pkgsrc settings
EOF

7. Build some packages

🖙 Before building more packages, it may be prudent to save current pkgsrc bootstrap state into tar archive, so that later it could be extracted on a different machine without bootstrapping it from scratch

gtar -zcf "/opt/pkg-${PKGSRC_VER:?}_bootstrap.tgz" "${PKG_BASE:?}"

It is best to create a script for this

PKGSRC_VER="2024Q3" &&
PKGSRC_BASE="/opt/pkgsrc-${PKGSRC_VER:?}" &&
PKG_BASE="/opt/pkg-${PKGSRC_VER:?}" &&
PKG_DBDIR="${PKG_BASE:?}/db/pkg" &&
LD_OPTIONS="\
-L${PKG_BASE:?}/lib-gcc -R${PKG_BASE:?}/lib-gcc \
-L${PKG_BASE:?}/lib-gcc/sparcv9 -R${PKG_BASE:?}/lib-gcc/sparcv9" &&
PATH="${GCC_BASE:?}/bin:${PKG_BASE:?}/bin:${PKG_BASE:?}/sbin:${PKG_BASE:?}/libexec:/bin:/usr/bin:/sbin:/usr/sbin" &&
export PKG_DBDIR LD_OPTIONS PATH

PKG_LIST='
meta-pkgs/modular-xorg-protos
meta-pkgs/modular-xorg-libs
meta-pkgs/modular-xorg-fonts
meta-pkgs/modular-xorg-utils
meta-pkgs/modular-xorg-apps
'

for i in ${PKG_LIST:?}
do
	cd "${PKGSRC_BASE:?}/${i}" && bmake package-install || break
done

# For pkgsrc support commands to work correctly, PKG_DBDIR must be set correctly
pkg_info -a

Thursday, October 24, 2024

Installing Developer Studio 12.6 on SPARC Solaris

Oracle seem to offer Developer Studio compilers free of charge. You just need to sign in with your free Oracle account, request access to software packages and accept license terms.

I encountered a number of snags with this process:

First, I could not figure out where to download the key and certificate files, which would allow me to set up pkg publisher. The instructions read:

Download your personal key and certificate files, called pkg.oracle.com.key.pem and pkg.oracle.com.certificate.pem from the certificate page.

The last two words: "certificate page" are linked to the certificate page, however the link is nearly impossible to see, since the text highlight colour blends in with the normal text colour on the page.

Second, the instructions for setting up the publisher seem to be incorrect. They tell you to download the key and certificate files to your home directory and then run 'pkg set-publisher'. However this results in errors:

# pkg set-publisher \
-k ./pkg.oracle.com.key.pem \
-c ./pkg.oracle.com.certificate.pem \
-G'*' -g https://pkg.oracle.com/solarisstudio/release solarisstudio

Unable to locate certificate '//pkg.oracle.com.certificate.pem' for publisher 'solarisstudio' needed to access 'https://pkg.oracle.com/solarisstudio/release/'.

It looks like the key and certificate files need to be copied to /var/pkg/ssl directory in order for them to be correctly located:

# ls -1 pkg*
pkg.oracle.com.certificate.pem
pkg.oracle.com.key.pem

# cp pkg* /var/pkg/ssl
# pkg set-publisher \
-k /var/pkg/ssl/pkg.oracle.com.key.pem \
-c /var/pkg/ssl/pkg.oracle.com.certificate.pem \
-G '*' -g https://pkg.oracle.com/solarisstudio/release solarisstudio

# pkg publisher
PUBLISHER                   TYPE     STATUS P LOCATION
solaris        (syspub)     origin   online T <system-repository>
solarisstudio               origin   online F https://pkg.oracle.com/solarisstudio/release/

# pkg install --accept developerstudio-126
...

# /opt/developerstudio12.6/bin/cc -V
cc: Studio 12.6 Sun C 5.15 SunOS_sparc 2017/05/30

All done.

Wednesday, October 16, 2024

Building GCC compilers for SPARC Solaris

In this article I would like to share some ideas on how to build C, C++ and Ada compilers for SPARC Solaris.

To build GCC compilers we need a bootstrap compiler with which to build. It may be possible to use for example, x86 Linux GCC host and then cross compile for SPARC Solaris target. However, this article assumes there is a native SPARC Solaris GCC compiler, which is then used to build a new version of a native GCC compiler. If the bootstrap GCC compiler does not support Ada programming language, then exclude it from GCC configure --enable-languages list.

GCC needs binutils (assembler, linker, loader, etc) programs in order to work correctly. There are two types of binutils for Solaris: Sun original binutils and GNU binutils. GCC can use either of them, however it is not able to switch between them dynamically, hence GCC needs to be configured and built separately for each type of binutils. This article describes both versions, just in case.


1. Create build directories

First we create build directories for downloaded packages, source files and object files.

BUILD_ROOT="/opt/gcc_build" &&
TAR_DIR="${BUILD_ROOT:?}/tar" &&
SRC_DIR="${BUILD_ROOT:?}/src" &&
OBJ_DIR="${BUILD_ROOT:?}/obj"
mkdir -p ${TAR_DIR:?} ${SRC_DIR:?} ${OBJ_DIR:?}

BUILD_ROOT is set to /opt/gcc_build.
TAR_DIR
is set to /opt/gcc_build/tar, downloaded packages are stored here.
SRC_DIR is set to /opt/gcc_build/src, source files are unpacked here.
OBJ_DIR is set to /opt/gcc_build/obj, object files are stored here.


2. Download and unpack packages

These versions of packages are known to build on Solaris. Later versions may introduce various Linuxisms, thus causing breakage.

DEVUTILS_PREFIX=/opt/gcc_build_tools &&
PATH="${DEVUTILS_PREFIX:?}/bin:/usr/bin:/usr/sbin" &&
export PATH &&
\
GCC_VER="13.3.0" &&
MAKE_VER="4.4" &&
TAR_VER="1.35" &&
PATCH_VER="2.7.6" &&
COREUTILS_VER="9.4" &&
BINUTILS_VER="2.43" &&
OPENSSL_VER="3.3.1" &&
WGET_VER="1.24.5" &&
cd ${TAR_DIR:?} && WGET_OPT="--no-check-certificate" &&
\
# These packages are needed for building GCC. Stock binaries may be too old on some versions of Solaris \
wget ${WGET_OPT:?} https://ftp.gnu.org/gnu/gcc/gcc-${GCC_VER:?}/gcc-${GCC_VER:?}.tar.gz &&
wget ${WGET_OPT:?} https://ftp.gnu.org/gnu/make/make-${MAKE_VER:?}.tar.gz &&
wget ${WGET_OPT:?} https://ftp.gnu.org/gnu/tar/tar-${TAR_VER:?}.tar.gz &&
wget ${WGET_OPT:?} https://ftp.gnu.org/gnu/patch/patch-${PATCH_VER}.tar.gz &&
wget ${WGET_OPT:?} https://ftp.gnu.org/gnu/coreutils/coreutils-${COREUTILS_VER}.tar.gz &&
wget ${WGET_OPT:?} https://ftp.gnu.org/gnu/binutils/binutils-${BINUTILS_VER:?}.tar.gz &&
wget ${WGET_OPT:?} https://www.openssl.org/source/openssl-${OPENSSL_VER:?}.tar.gz &&
wget ${WGET_OPT:?} https://ftp.gnu.org/gnu/wget/wget-${WGET_VER:?}.tar.gz &&
\
# These packages are optional and only needed for testing GCC \
DEJAGNU_VER="1.6.3" &&
TCL_VER="8.6.14" &&
EXPECT_VER="5.45.4" &&
WGET_OPT="--no-check-certificate" &&
wget ${WGET_OPT:?} https://ftp.gnu.org/gnu/dejagnu/dejagnu-${DEJAGNU_VER:?}.tar.gz &&
wget ${WGET_OPT:?} https://prdownloads.sourceforge.net/tcl/tcl${TCL_VER:?}-src.tar.gz &&
wget ${WGET_OPT:?} https://sourceforge.net/projects/expect/files/Expect/${EXPECT_VER:?}/expect${EXPECT_VER:?}.tar.gz
\
# Unpack all packages \
cd ${SRC_DIR:?} &&
for i in $(ls ${TAR_DIR:?}); do gtar -xf ${TAR_DIR:?}/$i & done; wait


3. Download GCC prerequisites

GCC needs several prerequisites packages in order to build correctly. These include: GMP, MPFR, MPC, etc. We patch the download_prerequisites script to avoid errors on systems which cannot verify SSL certificates.

cd ${SRC_DIR:?}/gcc-${GCC_VER:?} &&
cp contrib/download_prerequisites contrib/download_prerequisites.orig &&
sed "s/fetch='wget'/fetch='wget --no-check-certificate'/g" contrib/download_prerequisites.orig > contrib/download_prerequisites &&
/bin/sh contrib/download_prerequisites


4.A Build GCC with Sun binutils

The script below configures and builds GCC with Sun binutils and installs new compilers under /opt/gcc-<version>_sun_binutils.

PREFIX specifies GCC installation prefix.
DEVUTILS_PREFIX contains various build tools needed for downloading, patching, configuring and building GCC.
BUILD_CC contains the bootstrap build compiler.

MAKE_JOBS=64 &&
MARCH="sparc64-sun-solaris2.11" &&
PREFIX="/opt/gcc-${GCC_VER:?}_sun_binutils" &&
DEVUTILS_PREFIX=/opt/gcc_build_tools &&
BUILD_CC="/opt/gcc" &&
PATH="${DEVUTILS_PREFIX:?}/bin:${BUILD_CC:?}/bin:/usr/bin:/usr/sbin" &&
CC="gcc" &&
CXX="g++" &&
unset LDFLAGS &&
export PATH CC CXX \
\
# Build and install GCC \
pkg="gcc-${GCC_VER:?}"; cd ${OBJ_DIR:?} &&
test ! -d ${pkg:?} && mkdir ${pkg:?}; cd ${pkg:?} &&
${SRC_DIR:?}/${pkg:?}/configure --prefix="${PREFIX:?}" \
--build="${MARCH:?}" --host="${MARCH:?}" --target="${MARCH:?}" --with-cpu=v9 \
--enable-languages=c,c++,ada --enable-shared --enable-threads=posix \
--enable-bootstrap --enable-multilib --enable-obsolete --disable-nls \
--with-as="/usr/bin/as" --with-ld="/usr/bin/ld" &&
gmake --output-sync=target -j "${MAKE_JOBS:?}" && gmake install &&
\
# Perform final cleanup: \
rm -rf ${PREFIX:?}/share/info

# Run GCC tests (requires tcl, expect and dejagnu) \
# See https://gcc.gnu.org/install/test.html \
MAKE_JOBS=64 &&
PREFIX="/opt/gcc-${GCC_VER:?}_sun_binutils" &&
DEVUTILS_PREFIX=/opt/gcc_build_tools &&
PATH="${DEVUTILS_PREFIX:?}/bin:${PREFIX:?}/bin:/usr/bin:/usr/sbin" &&
CC="gcc" &&
CXX="g++" &&
LDFLAGS="" &&
export PATH CC CXX LDFLAGS &&
pkg="gcc-${GCC_VER:?}"; cd ${OBJ_DIR:?}/${pkg:?} &&
ulimit -s 32768 && gmake -j ${MAKE_JOBS:?} -k check

# Test results are in the following files
$ find . -name "*.sum"


4.B Build GCC with GNU binutils

The script below configures and builds GCC with GNU binutils and installs new compilers under /opt/gcc-<version>_gnu_binutils.

PREFIX specifies GCC installation prefix.
DEVUTILS_PREFIX contains various build tools needed for downloading, patching, configuring and building GCC.
BUILD_CC contains the bootstrap build compiler.

First, we build GNU binutils with bootstrap compiler. We use option --with-static-standard-libraries so that binutils do not depend on any bootstrap compiler's shared libraries.

Second, we build new version of GCC compilers. It is important that the directory with GNU binutils is located first in our PATH so that GCC configure script can correctly locate GNU as (assembler) and ld (linker) tools.

Third, we rebuild GNU binutils with shared standard libraries. We set LDFLAGS to hardcode the paths to new GCC compilers shared libraries.

🖙 Don't try to use binutils --enable-gold=default linker config option, this linker doesn't seem to work properly on SPARC Solaris when building GCC.

MAKE_JOBS=64 &&
MARCH="sparc64-sun-solaris2.11" &&
PREFIX="/opt/gcc-${GCC_VER:?}_gnu_binutils" &&
DEVUTILS_PREFIX=/opt/gcc_build_tools &&
BUILD_CC="/opt/gcc" &&
PATH="${PREFIX:?}/bin:${DEVUTILS_PREFIX:?}/bin:${BUILD_CC:?}/bin:/usr/bin:/usr/sbin" &&
CC="gcc" &&
CXX="g++" &&
unset LDFLAGS &&
export PATH CC CXX \
\
# Build and install temporary binutils with static standard libs \
pkg="binutils-${BINUTILS_VER:?}"; cd ${OBJ_DIR:?} &&
test ! -d ${pkg:?} && mkdir ${pkg:?}; cd ${pkg:?} &&
${SRC_DIR:?}/${pkg:?}/configure --prefix=${PREFIX:?} \
--build="${MARCH:?}" --host="${MARCH:?}" --target="${MARCH:?}" \
--with-static-standard-libraries \
--enable-shared --enable-ld=default --enable-64-bit-bfd --disable-nls &&
gmake MAKEINFO=true -j "${MAKE_JOBS:?}" &&
gmake MAKEINFO=true -j "${MAKE_JOBS:?}" install &&
cd ../ && rm -rf ${pkg:?} &&
\
# Build and install GCC \
pkg="gcc-${GCC_VER:?}"; cd ${OBJ_DIR:?} &&
test ! -d ${pkg:?} && mkdir ${pkg:?}; cd ${pkg:?} &&
${SRC_DIR:?}/${pkg:?}/configure --prefix="${PREFIX:?}" \
--build="${MARCH:?}" --host="${MARCH:?}" --target="${MARCH:?}" --with-cpu=v9 \
--enable-languages=c,c++,ada --enable-shared --enable-threads=posix \
--enable-bootstrap --enable-multilib --enable-obsolete --disable-nls \
--with-gnu-as --with-gnu-ld &&
gmake --output-sync=target -j "${MAKE_JOBS:?}" && gmake install &&
\
# Build and install final binutils with dynamic standard libs \
PATH="${DEVUTILS_PREFIX:?}/bin:${PREFIX:?}/bin:/usr/bin:/usr/sbin" &&
CC="gcc" &&
CXX="g++" &&
LDFLAGS="-Wl,--library-path=${PREFIX:?}/lib -Wl,-rpath=${PREFIX:?}/lib" &&
export PATH CC CXX LDFLAGS &&
\
pkg="binutils-${BINUTILS_VER:?}"; cd ${OBJ_DIR:?} &&
test ! -d ${pkg:?} && mkdir ${pkg:?}; cd ${pkg:?} &&
${SRC_DIR:?}/${pkg:?}/configure --prefix=${PREFIX:?} --disable-nls \
--build="${MARCH:?}" --host="${MARCH:?}" --target="${MARCH:?}" \
--enable-shared --enable-ld=default --enable-64-bit-bfd --disable-nls &&
gmake MAKEINFO=true -j "${MAKE_JOBS:?}" &&
gmake MAKEINFO=true -j "${MAKE_JOBS:?}" install &&
\
# Perform final cleanup \
unset LDFLAGS &&
rm -rf ${PREFIX:?}/share/info

# Run GCC tests (requires tcl, expect and dejagnu) \
# See https://gcc.gnu.org/install/test.html \
MAKE_JOBS=64 &&
PREFIX="/opt/gcc-${GCC_VER:?}_gnu_binutils" &&
DEVUTILS_PREFIX=/opt/gcc_build_tools &&
PATH="${DEVUTILS_PREFIX:?}/bin:${PREFIX:?}/bin:/usr/bin:/usr/sbin" &&
CC="gcc" &&
CXX="g++" &&
LDFLAGS="" &&
export PATH CC CXX LDFLAGS &&
pkg="gcc-${GCC_VER:?}"; cd ${OBJ_DIR:?}/${pkg:?} &&
ulimit -s 32768 && gmake -j ${MAKE_JOBS:?} -k check

# Test results are in the following files
$ find . -name "*.sum"


5. Build GCC tools

It is useful to have a small set of self-contained build tools, with which new versions of GCC can be built. Many of these tools can be installed with Solaris native package management commands. This may not be an option with older and unsupported versions of Solaris, hence a process for building these tools manually is described below.

DEVUTILS_PREFIX is set to where build tools will be installed.
BUILD_CC is set to the base directory where current build compiler is located. There could be multiple compilers, hence set up /opt/gcc to be a symlink to the working compiler.

Note that CFLAGS and CXXFLAGS contain -static-libgcc -static-libstdc++ flags. This builds standalone tools which do not depend on GCC shared libraries. The build compiler can later be removed from the system and the tools should continue to work.

MAKE_JOBS=64 &&
MARCH="sparc64-sun-solaris2.11" &&
DEVUTILS_PREFIX=/opt/gcc_build_tools &&
BUILD_CC="/opt/gcc" &&
PATH="${DEVUTILS_PREFIX:?}/bin:${BUILD_CC:?}/bin:/usr/bin:/usr/sbin" &&
CC="gcc" &&
CXX="g++" &&
CFLAGS="-O2 -mcpu=v9 -static-libgcc -static-libstdc++" &&
CXXFLAGS="-O2 -mcpu=v9 -static-libgcc -static-libstdc++" &&
LDFLAGS="-Wl,-L${DEVUTILS_PREFIX:?}/lib -Wl,-R${DEVUTILS_PREFIX:?}/lib" &&
export PATH CC CXX CFLAGS CXXFLAGS LDFLAGS &&
\
# Build and install GNU make \
pkg="make-${MAKE_VER:?}"; cd ${OBJ_DIR:?} &&
test ! -d ${pkg:?} && mkdir ${pkg:?}; cd ${pkg:?} &&
${SRC_DIR:?}/${pkg:?}/configure --prefix=${DEVUTILS_PREFIX:?} --disable-nls &&
./build.sh && ./make install && ln -sf make ${DEVUTILS_PREFIX:?}/bin/gmake &&
hash -r &&
\
# Build and install GNU tar \
pkg="tar-${TAR_VER:?}"; cd ${OBJ_DIR:?} &&
test ! -d ${pkg:?} && mkdir ${pkg:?}; cd ${pkg:?} &&
FORCE_UNSAFE_CONFIGURE=1 ${SRC_DIR:?}/${pkg:?}/configure --prefix=${DEVUTILS_PREFIX:?} --disable-nls &&
gmake -j ${MAKE_JOBS:?} && gmake install &&
ln -s tar ${DEVUTILS_PREFIX:?}/bin/gtar &&
\
# Build and install GNU patch \
pkg="patch-${PATCH_VER:?}"; cd ${OBJ_DIR:?} &&
test ! -d ${pkg:?} && mkdir ${pkg:?}; cd ${pkg:?} &&
${SRC_DIR:?}/${pkg:?}/configure --prefix=${DEVUTILS_PREFIX:?} --disable-nls &&
gmake -j ${MAKE_JOBS:?} && gmake install &&
\
# Build and Install subset of GNU coreutils (needed by GCC contrib/download_prerequisites script) \
pkg="coreutils-${COREUTILS_VER:?}"; cd ${OBJ_DIR:?} &&
test ! -d ${pkg:?} && mkdir ${pkg:?}; cd ${pkg:?} &&
FORCE_UNSAFE_CONFIGURE=1 ${SRC_DIR:?}/${pkg:?}/configure --prefix=${DEVUTILS_PREFIX:?} --disable-nls &&
gmake -j ${MAKE_JOBS:?} && mkdir -p ${DEVUTILS_PREFIX:?}/bin &&
src/ginstall -c src/md5sum src/sha1sum src/sha512sum ${DEVUTILS_PREFIX:?}/bin &&
\
# Build and install OpenSSL (needed by wget) \
pkg="openssl-${OPENSSL_VER:?}"; cd ${OBJ_DIR:?} &&
test ! -d ${pkg:?} && mkdir ${pkg:?}; cd ${pkg:?} &&
${SRC_DIR:?}/${pkg:?}/Configure --prefix=${DEVUTILS_PREFIX:?} --libdir=lib \
shared solaris64-sparcv9-gcc &&
gmake -j ${MAKE_JOBS:?} && gmake install_sw &&
\
# Build and install GNU wget (needed by GCC contrib/download_prerequisites script) \
pkg="wget-${WGET_VER:?}"; cd ${OBJ_DIR:?} &&
test ! -d ${pkg:?} && mkdir ${pkg:?}; cd ${pkg:?} &&
OPENSSL_CFLAGS="-I${DEVUTILS_PREFIX:?}/include" \
OPENSSL_LIBS="-L${DEVUTILS_PREFIX:?}/lib -lssl -lcrypto" \
${SRC_DIR:?}/${pkg:?}/configure --prefix=${DEVUTILS_PREFIX:?} --with-ssl=openssl --disable-nls &&
gmake -j ${MAKE_JOBS:?} && gmake install &&
\
# These are only needed for testing GCC \
\
# Build and install tcl \
pkg="tcl${TCL_VER:?}"; cd ${OBJ_DIR:?} &&
test ! -d ${pkg:?} && mkdir ${pkg:?}; cd ${pkg:?} &&
${SRC_DIR:?}/${pkg:?}/unix/configure --prefix=${DEVUTILS_PREFIX:?} &&
gmake -j ${MAKE_JOBS:?} && gmake install && gmake install-private-headers &&
ln -s tclsh* ${DEVUTILS_PREFIX:?}/bin/tclsh &&
\
# Build and install expect \
pkg="expect${EXPECT_VER:?}"; cd ${OBJ_DIR:?} &&
test ! -d ${pkg:?} && mkdir ${pkg:?}; cd ${pkg:?} &&
${SRC_DIR:?}/${pkg:?}/configure --prefix=${DEVUTILS_PREFIX:?} \
--with-tcl=${DEVUTILS_PREFIX:?}/lib --with-tclinclude=${DEVUTILS_PREFIX:?}/include &&
gmake -j ${MAKE_JOBS:?} && gmake install &&
\
# Build and install dejagnu \
pkg="dejagnu-${DEJAGNU_VER:?}"; cd ${OBJ_DIR:?} &&
test ! -d ${pkg:?} && mkdir ${pkg:?}; cd ${pkg:?} &&
${SRC_DIR:?}/${pkg:?}/configure --prefix=${DEVUTILS_PREFIX:?} &&
gmake -j ${MAKE_JOBS:?} && gmake install