# pidp8i-image-gen

This directory contains the [rpi-image-gen](https://github.com/raspberrypi/rpi-image-gen)
configuration and layers for building PiDP-8/I OS images. It replaces the legacy `bosi`
tool which was used to build images based on Raspberry Pi OS Buster Lite.

## Background

The original `bosi` tool was a bash script that operated in three stages:

- `do_init` — installed packages and cloned the fossil repository on a live running Pi
- `do_build` — built the PiDP-8/I software from source on the target hardware
- `do_prepare` — sanitized the running system before imaging (wiping SSH keys,
  bash history, etc.)
- `do_image` — captured the SD card image via `dd` on macOS

This approach required physical access to the target hardware throughout the build
process, and produced images tied to Raspberry Pi OS Buster Lite (Debian 10), which
reached end-of-life in 2022.

## Migration to rpi-image-gen

`rpi-image-gen` is the Raspberry Pi Foundation's modern image generation tool. It
builds complete OS images inside a chroot on a build host (a Pi 5 in our case),
eliminating the need for a live target system during the build. The finished image
boots directly into a fully configured state.

The `bosi` stages map to rpi-image-gen concepts as follows:

| bosi stage | rpi-image-gen equivalent |
|---|---|
| `do_init` (package install) | `mmdebstrap: packages:` in layer YAML |
| `do_build` (fossil clone + build) | `customize-hooks:` in layer YAML |
| `do_prepare` (sanitize) | handled automatically by rpi-image-gen |
| `do_image` (capture SD card) | `rpi-image-gen build` output |

## Hardware Support

The original `bosi` tool targeted the Pi Zero (BCM2835, ARMv6). rpi-image-gen does
not support ARMv6, so the minimum supported target is now the **Pi Zero 2 W**
(BCM2710, ARMv8 quad-core Cortex-A53). This also retires the NLS (No Long Setup)
image variant, since the Zero 2 W is fast enough to build the OS/8 RK05 disk images
from source.

Supported targets:

| Target | Device layer override |
|---|---|
| Pi Zero 2 W | `IGconf_device_layer=rpizero2w` |
| Pi 3 | `IGconf_device_layer=rpi3` |
| Pi 4 | `IGconf_device_layer=rpi4` |
| Pi 5 | `IGconf_device_layer=rpi5` |

The default device layer is `rpi-generic64-device`, a thin wrapper around
`rpi-generic64` that declares the `device` provider capability per the recommended
rpi-image-gen pattern. See `layer/rpi-generic64-device.yaml` and
[rpi-image-gen issue #202](https://github.com/raspberrypi/rpi-image-gen/issues/202).

## Project Structure

```
pidp8i-image-gen/
├── README.md                        this file
├── config/
│   └── pidp8i-trixie.yaml           main build config (Debian Trixie, arm64)
├── files/
│   ├── pidp8i-firstboot.service     systemd user service: runs 'pidp8i install'
│   │                                on first boot, then reboots
│   └── wpa_supplicant-wlan0.conf    WiFi credentials template — copy to the
│                                    boot partition and fill in before first boot
└── layer/
    ├── pidp8i-common.yaml           main layer: packages, build, install, config
    └── rpi-generic64-device.yaml    device provider wrapper for rpi-generic64
```

## Build Host Requirements

- Raspberry Pi 5 running Debian Trixie or Bookworm
- `rpi-image-gen` cloned to `~/rpi-image-gen`
- Network access to `tangentsoft.com` (fossil clone during build)
- This project synced to `~/pidp8i-image-gen` on the build host

## Building

Sync project files to the Pi 5 build host:

```bash
rsync -av --delete ~/trunk/pidp8i-image-gen/ pi5:~/pidp8i-image-gen/
```

Build the image on the Pi 5:

```bash
cd ~/rpi-image-gen
sudo ./rpi-image-gen build -S ~/pidp8i-image-gen -c pidp8i-trixie.yaml
```

To target a specific device:

```bash
sudo ./rpi-image-gen build -S ~/pidp8i-image-gen -c pidp8i-trixie.yaml \
  -- IGconf_device_layer=rpizero2w
```

The output image is written to `~/rpi-image-gen/work/deploy-<date>/`.

## Notable Implementation Details

**User service installation:** `pidp8i install` requires a live systemd user session
bus and cannot run in the chroot. It is deferred to a one-shot systemd user service
(`pidp8i-firstboot.service`) that runs on first boot, installs the simulator service,
and reboots. From the second boot onwards the simulator starts automatically.

**GPIO permissions:** The `pdp8` binary accesses `/dev/gpiomem` directly with no
library dependencies. A udev rule sets `GROUP=gpio MODE=0660` on the device node.
The `pidp8i` user is added to the `gpio` group by the `rpi-user-credentials` layer.

**linger:** `loginctl enable-linger` cannot run in the chroot. The linger file is
created directly at `/var/lib/systemd/linger/pidp8i`.

**Password change on first login:** We use a sentinel file
(`~/.password-not-changed`) checked by `.bashrc` rather than `passwd -e`
to force a password change on first interactive login. The reason is that
`passwd -e` causes PAM to block the linger-triggered systemd user session
at boot, preventing the simulator from autostarting without any login.
Since `.bashrc` only runs for interactive shells, the sentinel file
approach is invisible to systemd and leaves linger unaffected.

**PATH:** The Makefile's PATH update uses `$SUDO_USER` which is unset in the chroot.
The layer adds `/opt/pidp8i/bin` and `/opt/pidp8i/share/man` to the pidp8i user's
`.profile` directly.

**WiFi:** The image ships with a `wpa_supplicant-wlan0.conf` template on the boot
partition (FAT32, visible on any OS). The user fills in their SSID and password before
first boot. The firstboot service moves it to `/etc/wpa_supplicant/` on first boot.
Alternatively, `sudo raspi-config` → System Options → Wireless LAN can be used after
connecting via ethernet.
