\documentclass{beamer}
\usepackage{hyperref}
\usepackage{graphicx}
\usepackage{qrcode}
\usepackage{xcolor}
% Colorblind-friendly palette from .
\definecolor{Vermillion} {cmy}{0, 0.8, 1}
\definecolor{Orange} {cmy}{0, 0.5, 1}
\definecolor{SkyBlue} {cmy}{0.8, 0, 0}
\definecolor{BluishGreen} {cmy}{0.97, 0, 0.75}
\definecolor{Yellow} {cmy}{0.1, 0.5, 0.9}
\definecolor{Blue} {cmy}{1, 0.5, 0}
\definecolor{ReddishPurple} {cmy}{0.1, 0.7, 0}
\newcommand{\DECAlpha}{DEC~Alpha}
\newcommand{\BSD}{BSD}
\newcommand{\BSDCan}{BSDCan}
\newcommand{\NetBSD}{NetBSD}
\newcommand{\NetBSDcurrent}{\NetBSD-current}
\newcommand{\rump}{Rump}
\newcommand{\email}[1]{\texttt{#1}}
\newcommand{\texttildecenter}{\raisebox{0.5ex}{\texttildelow}}
\newcommand{\brdots}{[\,\dots]}
\title{How to get started hacking \NetBSD}
\author{Taylor R Campbell \\
\email{riastradh@NetBSD.org}}
\date{\BSDCan\ 2024 \\
Ottawa, Canada \\
June 1, 2024}
\begin{document}
\frame{\titlepage}
\begin{frame}
\frametitle{How to get started hacking \NetBSD}
\centering
\url{https://www.NetBSD.org/gallery/presentations/riastradh/bsdcan2024/getstarted.pdf}
\vspace{\baselineskip}
\qrcode[height=0.625\textheight]{https://www.NetBSD.org/gallery/presentations/riastradh/bsdcan2024/getstarted.pdf}
\end{frame}
\begin{frame}[fragile]
\frametitle{How to get started hacking \NetBSD}
\begin{verbatim}
cvs -d anoncvs@anoncvs.NetBSD.org:/cvsroot co -P src
\end{verbatim}
Alternatively, with Git or Mercurial:
\begin{itemize}
\item \verb!git clone https://github.com/NetBSD/src!
\item \verb!hg clone https://anonhg.NetBSD.org/src!
\end{itemize}
\begin{verbatim}
cd src
./build.sh -O ../obj -U -u -m alpha -N1 -j4 release
\end{verbatim}
\end{frame}
\begin{frame}
\frametitle{How I got started hacking \NetBSD}
\begin{itemize}
\item Lived in the Apple world through \texttildecenter 2008
\item<2-> Apple blocked running iTunes under gdb
\item<3-> Offended by denial of autonomy
\only<4->{\emph{in my own computer}}
\item<5-> Shopped around for something better
\item<6-> \dots something that would respect my autonomy
\item<7-> \dots and would run on my PowerBook G4
\end{itemize}
\end{frame}
\begin{frame}
\centering
\includegraphics[height=0.9\textheight]{mail20080605.png}
\end{frame}
\begin{frame}
\centering
\includegraphics[height=0.9\textheight]{mail20080617.png}
\end{frame}
\begin{frame}
\frametitle{How I got started hacking \NetBSD}
\begin{itemize}
\item Read kernel code for the first time
\item Just C code, guessed how syscalls are implemented
\item Found bug in cmsg(3) API for fd passing
\item Proposed fix, committed by christos@
\item<2-> \dots not quite fixed until later; fd passing is hard
\end{itemize}
\end{frame}
\begin{frame}
\frametitle{How I got started hacking \NetBSD\ device drivers}
\begin{itemize}
\item Spent July and August porting bwi(4) from
OpenBSD/DragonflyBSD
\item Got it working on my PowerBook G4 Airport Extreme by
September
\item<2-> \dots didn't do a great job, not a wifi expert
\end{itemize}
\end{frame}
\begin{frame}
\frametitle{How to get started hacking \NetBSD}
Check out source tree (a few gigabytes):
\begin{itemize}
\item \strut\rlap{\texttt{cvs -d anoncvs@anoncvs.NetBSD.org:/cvsroot co -P src}}
\item \texttt{git clone https://github.com/NetBSD/src}
\item \texttt{hg clone https://anonhg.NetBSD.org/src}
\end{itemize}
\end{frame}
\begin{frame}[fragile]
\frametitle{How to get started hacking \NetBSD}
Build a release:
\vspace{\baselineskip}
% ./build.sh -O ../obj -U -u -m alpha -N1 -j4 release
\texttt{./build.sh
\alt<2>{\textcolor{Vermillion}{-O ../obj}}{-O ../obj}
\alt<3>{\textcolor{Vermillion}{-U}}{-U}
\alt<4>{\textcolor{Vermillion}{-u}}{-u}
\alt<5>{\textcolor{Vermillion}{-m alpha}}{-m alpha}
\alt<6>{\textcolor{Vermillion}{-N1}}{-N1}
\alt<7>{\textcolor{Vermillion}{-j4}}{-j4}
\alt<8>{\textcolor{Vermillion}{release}}{release}
}
\vspace{\baselineskip}
\begin{description}
\item<2->[\texttt{-O ../obj}]
build products go here
\item<3->[\texttt{-U}]
unprivileged build (traditionally privileged in /usr/src)
\item<4->[\texttt{-u}]
update build, don't clean
\item<5->[\texttt{-m alpha}]
build for \DECAlpha\ architecture\hfill\break
(use \texttt{./build.sh list-arch} to see all options)
\item<6->[\texttt{-N1}]
verbosity level 1
\item<7->[\texttt{-j4}]
4-way parallel build
\item<8->[\texttt{release}]
build \NetBSD\ release---kernel, userland, sets, images\hfill\break
(use \texttt{./build.sh help} to see all operations)
\end{description}
\end{frame}
\begin{frame}
\frametitle{What's in your \NetBSD\ release build}
\begin{description}
\item[\texttt{../obj/tooldir.NetBSD-9.3-amd64}]\hfill\break
cross-compiler toolchain
\item[\texttt{../obj/releasedir/\$\{MACHINE\_ARCH\}}]\hfill\break
release products, including distribution sets, kernels, and
install/live media
\item[\texttt{../obj/destdir.\$\{MACHINE\}}]\hfill\break
staged \NetBSD\ installation
\item[\texttt{../obj/usr.bin/find}]\hfill\break
build tree for find(1) from src/usr.bin/find
\item[\texttt{../obj/sys/arch/\$\{MACHINE\}/compile/GENERIC}]\hfill\break
build tree for GENERIC kernel, including \texttt{netbsd} kernel
image (may vary on some ports)
\end{description}
\end{frame}
\begin{frame}
\frametitle{Other useful build.sh targets}
\begin{description}
\item[\texttt{./build.sh help}]
\item[\texttt{./build.sh list-arch}]
\item[\texttt{./build.sh tools}]\hfill\break
build just cross-compiler toolchain
\item[\texttt{./build.sh distribution}]\hfill\break
build just (tools and) userland but not kernels or release
\item[\texttt{./build.sh sets}]\hfill\break
build just distribution sets
\item[\texttt{./build.sh modules}]\hfill\break
build just kernel modules
\end{description}
\end{frame}
\begin{frame}
\frametitle{Build a kernel}
\begin{enumerate}
\item \texttt{./build.sh \textrm{\brdots}\ tools}
\item \texttt{./build.sh \textrm{\brdots}\ kernel=GENERIC}
\begin{itemize}
\item Note: For 64-bit Arm and 64-bit RISC-V, use
\texttt{kernel=GENERIC64}, not \texttt{kernel=GENERIC}
\item Some ports have various special-purpose kernels, not
GENERIC, such as evbppc TWRP1025
\end{itemize}
\end{enumerate}
\end{frame}
\begin{frame}[fragile]
\frametitle{Custom kernel config}
\begin{itemize}
\item Local custom changes to GENERIC in
sys/arch/\dots/conf/GENERIC.local
(or sys/arch/\dots/conf/GENERIC64.local)
\item Custom kernel config in sys/arch/\dots/conf/MYKERNEL
\end{itemize}
\vspace{\baselineskip}
Example sys/arch/amd64/conf/DEBUG:
\begin{verbatim}
include "arch/amd64/conf/GENERIC"
options DEBUG
options KERNHIST
options LOCKDEBUG
options UVMHIST
\end{verbatim}
\end{frame}
\begin{frame}[fragile]
\frametitle{Boot aarch64 in qemu}
\begin{verbatim}
# Create a disk image
mkdir ~/vm
cd ~/obj/releasedir/evbarm-aarch64/binary/gzimg
gunzip -c ~/vm/disk.img
# Make it a sparse 10 GB image
cd ~/vm
dd if=/dev/zero of=disk.img oseek=10g bs=1 count=1
# Make a symlink to the kernel
KERNDIR=~/obj/sys/arch/evbarm/compile/GENERIC64
ln -sfn $KERNDIR/netbsd.img .
\end{verbatim}
\end{frame}
\begin{frame}[fragile]
\frametitle{Start qemu-system-aarch64}
\begin{verbatim}
qemu-system-aarch64 \
-kernel netbsd.img \
-append "root=dk1" \
-M virt \
-cpu cortex-a53 \
-smp 2 \
-m 1g \
-drive if=none,file=disk.img,id=disk,format=raw \
-device virtio-blk-device,drive=disk \
-device virtio-rng-device \
-nic user,model=virtio-net-pci \
-nographic
\end{verbatim}
\end{frame}
\begin{frame}
\frametitle{If \texttt{qemu -kernel} doesn't work}
\begin{itemize}
\item \texttt{qemu -kernel} works on some ports:
alpha,
arm (32-bit and 64-bit aarch64),
riscv,
virt68k
\begin{itemize}
\item Not yet on x86 (but almost ready!)
\end{itemize}
\item If not:
\begin{itemize}
\item Use vnd(4) and/or rump\_ffs(4) to mount disk.img on the
host to update the kernel
\item Serve the kernel from the host with httpd(8) and download
it on the guest with ftp(1)
\end{itemize}
\end{itemize}
\end{frame}
\begin{frame}
\frametitle{gdb on live kernel under qemu}
\begin{itemize}
\item \texttt{\$ qemu-system-aarch64 \textrm{\brdots}\ -s \textrm{\brdots}}
\item
\texttt{\$ gdb netbsd.gdb}\par
\texttt{(gdb) target remote localhost:1234}
\item<2->
qemu \texttt{-s} is short for \texttt{-gdb tcp:1234}
\end{itemize}
\end{frame}
\begin{frame}[fragile]
\frametitle{gdb on live running kernel}
\begin{verbatim}
# gdb netbsd.gdb
(gdb) target kvm /dev/mem
\end{verbatim}
\end{frame}
\begin{frame}[fragile]
\frametitle{gdb and crash(8) on system core dumps}
\begin{verbatim}
# gdb netbsd.gdb
(gdb) target kvm /var/crash/netbsd.n.core
\end{verbatim}
\vspace{\baselineskip}
\begin{verbatim}
# crash -M /var/crash/netbsd.n \
-N /var/crash/netbsd.n.core
crash> bt
crash> ps
\end{verbatim}
(see ddb(4) for more commands)
\vspace{\baselineskip}
\begin{verbatim}
# dmesg -M /var/crash/netbsd.n \
-N /var/crash/netbsd.n.core
\end{verbatim}
\end{frame}
\begin{frame}[fragile]
\frametitle{Verify system core dumps work!}
Force the system to crash:
\begin{verbatim}
# sysctl -w debug.crashme_enable=1
# sysctl -w debug.crashme.panic=1
\end{verbatim}
\end{frame}
\begin{frame}[fragile]
\frametitle{Verify system core dumps work!}
Many other crashme nodes:
\begin{itemize}
\item simulate panic
\item<2-> enter ddb directly
\item<3-> recursively lock mutex(9)
\item<4-> enter infinite loop with interrupts blocked
\item<5-> launch golang, a well-known alternative test suite for
\NetBSD
\end{itemize}
\end{frame}
\begin{frame}[fragile]
\frametitle{Run ATF tests}
\begin{verbatim}
# cd /usr/tests
# atf-run | atf-report
\end{verbatim}
\end{frame}
\begin{frame}[fragile]
\frametitle{Run ATF tests unprivileged}
\begin{verbatim}
$ cd /usr/tests
$ atf-run | atf-report
\end{verbatim}
\vspace{\baselineskip}
\begin{itemize}
\item Not all ATF tests can run unprivileged
\item Those that can avoid changing system configuration
\end{itemize}
\end{frame}
\begin{frame}[fragile]
\frametitle{Run ATF tests and save output}
\begin{verbatim}
# cd /usr/tests
# atf-run | tee /var/tmp/atf-run.out | \
atf-report | tee /var/tmp/atf-report.out
\end{verbatim}
\end{frame}
\begin{frame}[fragile]
\frametitle{Run ATF tests in a chroot}
\begin{verbatim}
# chroot ~/obj/destdir.amd64
chroot# cd /dev && sh MAKEDEV all
chroot# mount -t ptyfs ptyfs /dev/pts
chroot# mount -t tmpfs tmpfs /tmp
chroot# cd /usr/tests
chroot# atf-run | atf-report
\end{verbatim}
Requires kernel at least as new as the chroot userland.
Very handy for testing pullups to release branches!
\end{frame}
\begin{frame}[fragile]
\frametitle{Developing one program/library at a time}
\begin{verbatim}
$ cd ~/src/usr.bin/find
$ $TOOLDIR/bin/nbmake-$MACHINE_ARCH -j4 dependall
$ $TOOLDIR/bin/nbmake-$MACHINE_ARCH -j4 install
\end{verbatim}
\begin{itemize}
\item Test again straight from the chroot!
\item
(For libraries: works only for dynamic libraries; static
libraries require rebuilding downstream users too)
\item One makefile per program/library, usually short
\item See src/share/mk/bsd.README for more information
\end{itemize}
\end{frame}
\begin{frame}[fragile]
\frametitle{Changing an include file}
\begin{verbatim}
$ cd ~/src/include
$ edit stdio.h
$ $TOOLDIR/bin/nbmake-$MACHINE_ARCH -j4 includes
\end{verbatim}
Then rebuild programs and libraries that
\texttt{\#include }
\end{frame}
\begin{frame}[fragile]
\frametitle{Device drivers}
\begin{itemize}
\item Autoconf drivers: represent hardware devices \NetBSD\ can
detect and handle
\item devsw entries: /dev interfaces between userland and kernel
\item<2-> Sometimes correspond
\end{itemize}
\end{frame}
\begin{frame}[fragile]
\frametitle{Anatomy of an autoconf driver}
Driver for hardware that can be detected by bus enumeration
\begin{verbatim}
struct foodev_softc {
device_t sc_self;
kmutex_t sc_lock;
...
};
CFATTACH_DECL2_NEW(foodev, sizeof(struct foodev_softc),
foodev_match, foodev_attach, foodev_detach,
NULL, NULL, NULL);
\end{verbatim}
\vspace{\baselineskip}
(Some day we'll switch to C99 designated initializers!)
\end{frame}
\begin{frame}[fragile]
\frametitle{Anatomy of an autoconf driver}
\begin{verbatim}
static int
foo_match(device_t self, cfmatch_t match, void *aux)
{
const struct pci_attach_args *pa = aux;
...
}
static int
foo_attach(device_t parent, device_t self, void *aux)
{ ... }
static int
foo_detach(device_t self, int flags)
{ ... }
\end{verbatim}
\end{frame}
\begin{frame}
\frametitle{Anatomy of an autoconf driver}
\begin{description}
\item[\texttt{foo\_match}]
Can this driver handle this device?
If so, with what priority versus other drivers that can?
\item[\texttt{foo\_attach}]
\begin{itemize}
\item Allocate resources for the device's driver state
\item Expose the device to any other kernel interfaces
\item Scan for children if any
\end{itemize}
\item[\texttt{foo\_detach}]
Device has been removed.
Disconnect any users in other kernel interfaces and free
resources.
\end{description}
\scriptsize
See
\url{https://www.NetBSD.org/gallery/presentations/riastradh/eurobsdcon2022/opendetach.pdf}
for more on tricky issues with detach!
\vspace{\baselineskip}
\centering
\qrcode[height=0.3125\textheight]{https://www.NetBSD.org/gallery/presentations/riastradh/eurobsdcon2022/opendetach.pdf}
\end{frame}
\begin{frame}[fragile]
\frametitle{Anatomy of a /dev character special}
Interface between userland and kernel identified by major and minor
number stored in character special on disk
\begin{verbatim}
const struct cdevsw foo_cdevsw = {
.d_open = foo_open,
.d_close = foo_close,
.d_read = foo_read,
.d_write = foo_write,
...
.d_cfdriver = &foodev_cd,
.d_devtounit = dev_minor_unit,
...
};
\end{verbatim}
(Don't forget to add to src/sys/conf/majors, or the appropriate
machine-dependent majors list, and etc/MAKEDEV!)
\end{frame}
\begin{frame}
\frametitle{Cloning devices}
\begin{itemize}
\item Traditional /dev nodes correspond to a single hardware
device with per-device state:
\begin{itemize}
\item /dev/wd0 corresponds to first ATA hard drive
\item /dev/sd0 corresponds to first SCSI hard drive
\item /dev/ttyU0 corresponds to first USB serial port
\end{itemize}
\item \textbf{Cloning devices} have per-open state:
\begin{itemize}
\item /dev/audio virtual mixed-audio interface
\item /dev/dri/card0 graphics rendering interface
\item /dev/vhci USB virtual host controller interface
\end{itemize}
\end{itemize}
\end{frame}
\begin{frame}[fragile]
\frametitle{Cloning devices}
\begin{verbatim}
const struct cdevsw foo_cdevsw = {
.d_open = foo_open,
.d_close = noclose,
.d_read = noread,
.d_write = nowrite,
...
};
static const struct fileops foo_fileops = {
.fo_name = "foo",
.fo_read = foo_read,
.fo_write = foo_write,
...
};
\end{verbatim}
\end{frame}
\begin{frame}[fragile]
\frametitle{Cloning devices}
\begin{verbatim}
static int
foo_open(dev_t d, int flags, int fmt, struct lwp *l)
{
struct file *fp;
int fd;
int error;
error = fd_allocfile(&fp, &fd);
if (error)
return error;
...
error = fd_clone(fp, fd, flags,
&foo_fileops, privatedata);
KASSERT(error == EMOVEFD);
return error;
}
\end{verbatim}
\end{frame}
\begin{frame}[fragile]
\frametitle{Access to device registers: bus\_space(9)}
\begin{verbatim}
bus_space_tag_t bst;
bus_space_handle_t bsh;
uint32_t ctl;
int error;
bst = args->bst;
error = bus_space_map(bst, args->addr, args->size, 0,
&bsh);
if (error)
goto fail;
ctl = bus_space_read_4(bst, bsh, FOO_CTL);
if (ctl & FOO_BROKEN)
goto broken;
ctl |= FOO_ENABLED;
bus_space_write_4(bst, bsh, FOO_CTL, foo);
\end{verbatim}
\end{frame}
\begin{frame}[fragile]
\frametitle{Handy macros for device register fields}
\begin{verbatim}
#include
#define RK_V1CRYPTO_TRNG_CTRL 0x0200 /* TRNG Control */
#define RK_V1CRYPTO_TRNG_CTRL_OSC_ENABLE __BIT(16)
#define RK_V1CRYPTO_TRNG_CTRL_CYCLES __BITS(15,0)
...
ctrl = RK_V1CRYPTO_TRNG_CTRL_OSC_ENABLE;
ctrl |= __SHIFTIN(100, RK_V1CRYPTO_TRNG_CTRL_CYCLES);
RKC_WRITE(sc, RK_V1CRYPTO_TRNG_CTRL, ctrl);
\end{verbatim}
\end{frame}
\begin{frame}
\frametitle{Exposing memory to devices: bus\_dma(9)}
DMA: Direct memory access
\begin{itemize}
\item Allocate buffers at addresses that are safe for DMA
\item Map buffers to bus addresses with IOMMU
\begin{itemize}
\item E.g., map user buffer from write(2) so ethernet device
can copy it out to the network
\end{itemize}
\item Handle bouncing if buffers can't be mapped directly
\end{itemize}
\end{frame}
\begin{frame}[fragile]
\frametitle{Testing kernel components with \rump}
\begin{itemize}
\item \rump\ runs unmodified kernel components and device drivers
in userland processes, by setting up state just like a kernel
\item \NetBSD\ test suite uses \rump\ extensively to test kernel
components, such as file systems
\item Edit, compile, test kernel components from userland:
\begin{itemize}
\item
run \texttt{nbmake-\$MACHINE} dependall/install from
src/sys/rump
\linebreak (or selective subdirectories)
\item redo atf-run/report from chroot
\end{itemize}
\end{itemize}
\end{frame}
\begin{frame}
\frametitle{Now get hacking \NetBSD!}
\LARGE Questions?
\end{frame}
\end{document}