\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{\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 ~/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 #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}