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 (evbarmv7hf port) or as a 64-bit operating system (evbarm64 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, to partition the disk, create file systems, extract the NetBSD sets, and configure the installed system. The interactive NetBSD installer 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, MBR may be adequate; however, GPT 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 creates:

  • 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

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 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.1"
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 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.1"
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 7: Clean Up

Finally unmount all file systems:

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

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.

No comments:

Post a Comment