\documentclass{beamer}

\usepackage{hyperref}
\usepackage{graphicx}
\usepackage{qrcode}
\usepackage{xcolor}

% Colorblind-friendly palette from <https://jfly.uni-koeln.de/color/>.
\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{\EuroBSDcon}{EuroBSDcon}
\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{\EuroBSDcon\ 2023 \\
  Coimbra, Portugal \\
  September 16, 2023}

\begin{document}

\frame{\titlepage}

\begin{frame}
  \frametitle{How to get started hacking \NetBSD}

  \centering

  \url{https://www.NetBSD.org/gallery/presentations/riastradh/eurobsdcon2023/getstarted.pdf}

  \vspace{\baselineskip}

  \qrcode[height=2in]{https://www.NetBSD.org/gallery/presentations/riastradh/eurobsdcon2023/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

# Alternatively, with Git or Mercurial:
#   git clone https://github.com/NetBSD/src
#   hg clone https://anonhg.NetBSD.org/src

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 a \NetBSD\ release\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 <arm64.img.gz >~/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{Updating kernel for non-aarch64}

  \begin{itemize}
    \item Use vnd(4) and/or rump\_ffs(4) to mount disk.img
    \item (Volunteer needed to make \NetBSD\ support booting with
       \texttt{-kernel netbsd}
       under qemu on other architectures!)
  \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.)

    \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
$ $TOOLDIR/bin/nbmake-$MACHINE_ARCH -j4 includes
\end{verbatim}
\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=1in]{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 <sys/cdefs.h>

#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}
