\documentclass[aspectratio=169,obeyspaces,spaces,hyphens,dvipsnames]{beamer}
\usepackage[utf8]{inputenc}
\usepackage{lmodern}% http://ctan.org/pkg/lm
\usepackage{minted}
\usepackage{hyperref}
\usepackage{xcolor}
\usepackage{pgfplots}
\usepackage{tikz}
\usepackage[normalem]{ulem}

\usepackage[hindi, provide=*]{babel}
\babelfont{rm}[Renderer=Harfbuzz]{FreeSerif}

\mode<presentation>
\usetheme{Bootlin}

\def\signed #1{{\leavevmode\unskip\nobreak\hfil\penalty50\hskip2em
  \hbox{}\nobreak\hfil(#1)
  \parfillskip=0pt \finalhyphendemerits=0 \endgraf}}

\newsavebox\mybox
\newenvironment{aquote}[1]
  {\savebox\mybox{#1}\begin{quotation}}
  {\signed{\usebox\mybox}\end{quotation}}

\title{Embedded Linux from scratch in 50 minutes (on RISC-V)}
\authors{Michael Opdenacker}
\email{michael.opdenacker@bootlin.com}
\slidesurl{https://bootlin.com/pub/conferences/2024/risc-v/}
\institute{Michael Opdenacker}
\conference{Bangalore 2024}

\begin{document}

\section{Embedded Linux from scratch in 50 minutes (on RISC-V)}

\begin{frame}{Michael Opdenacker}
\begin{columns}
  \column{0.75\textwidth}
  \begin{itemize}
  \item Founder and Embedded Linux engineer at Bootlin:
    \begin{itemize}
    \item Embedded Linux {\bf expertise}
    \item {\bf Development}, consulting and training
    \item Focusing {\bf only on Free and Open Source Software}
    \end{itemize}
  \item About myself:
    \begin{itemize}
      \item Always happy to learn from every new project,\\
            and share what I learn.
      \item Initial author of Bootlin's freely available embedded Linux,
	    kernel and boot time reduction training materials (\url{https://bootlin.com/docs/})
      \item Documentation maintainer for the Yocto Project
    \end{itemize}
  \end{itemize}
  \column{0.25\textwidth}
    \begin{center}
    \includegraphics[width=0.5\textwidth]{graphics/logo-cc.pdf}\\
    \end{center}
\end{columns}
\end{frame}

\begin{frame}{About this presentation}
\begin{itemize}
    \item This presentation is an update to a talk I made in 2021
          \url{https://bootlin.com/pub/conferences/2021/fosdem/opdenacker-embedded-linux-45minutes-riscv/}
    \item This presentation is available under the same
          \href{https://creativecommons.org/licenses/by-sa/3.0/} {Creative-Commons Attribution Share-Alike 3.0 license}
    \item I'm doing this presentation on my own behalf.\\
          This doesn't represent opinions or statements from Bootlin.
\end{itemize}
\end{frame}

\subsection{Introduction}

\begin{frame}{What I like in embedded Linux}
\begin{itemize}
  \item Linux is perfect for operating devices with a fixed set of
        features.\\
         Unlike on the desktop, Linux is almost in every existing embedded system.
  \item Embedded Linux makes Linux easy to learn: just a few programs and
        libraries are sufficient. {\bf You can understand the usefulness of each
        file in your filesystem}.
  \item The Linux kernel is standalone: no complex dependencies against
	external software. The code is in C (or Rust)!
  \item Linux works with just a few MB of RAM and storage
  \item There's a new version of Linux every 2-3 months.
  \item Relatively small development community. You end up meeting lots
        of familiar faces at technical conferences (like the Embedded
	Linux Conference).
  \item Lots of opportunities (and funding available) for becoming a
        contributor (Linux kernel, bootloader, build systems...).
\end{itemize}
\end{frame}

\begin{frame}{Goals}
Show you the most important aspects of embedded Linux development work
\begin{itemize}
  \item Building a cross-compiling toolchain
  \item Creating a disk image
  \item Booting a using a bootloader
  \item Loading and starting the Linux kernel
  \item Building a root filesystem populated with basic utilities
  \item Configuring the way the system starts
  \item Setting up networking and controlling the system via a web interface
  \item Do this on QEMU and on real hardware!
\end{itemize}
\end{frame}

\begin{frame}{Things to build today}
\begin{itemize}
  \item Cross-compiling toolchain: {\em Buildroot 2024.02.1 (LTS)}
  \item Firmware / first stage bootloader: {\em OpenSBI}
  \item Bootloader: {\em U-Boot 2024.04}
  \item Kernel: {\em Linux 6.8.x}
  \item Root filesystem and application: {\em BusyBox 1.36.1}
\end{itemize}
That's possible to compile and assemble in less than 50 minutes!
\end{frame}

\subsection{Cross-compiling toolchain}

\begin{frame}
  \frametitle{What's a cross-compiling toolchain?}
  \begin{center}
    \includegraphics[width=0.8\textwidth]{graphics/cross-toolchain.pdf}
  \end{center}
\end{frame}

\begin{frame}
  \frametitle{Why generate your own cross-compiling toolchain?}
  Compared to ready-made toolchains:
  \begin{itemize}
    \item You can choose your compiler version
    \item You can choose your C library (glibc, uClibc, musl)
    \item You can tweak many other features!
    \item You gain reproducibility: if a bug is found, just apply a fix.\\
          Don't need to get another toolchain (different bugs)
  \end{itemize}
\end{frame}

\begin{frame}{Generating a RISC-V musl toolchain with Buildroot}
  \small
  \begin{columns}
    \column{0.75\textwidth}
    \begin{itemize}
      \item Download Buildroot 2024.02.1 from \url{https://buildroot.org}
      \item Extract the sources (\code{tar xf})
      \item Run \code{make menuconfig}
      \item In \code{Target options} \textrightarrow \code{Target
	    Architecture}, choose \code{RISCV}
      \item In \code{Toolchain} \textrightarrow \code{C library},
	    choose \code{musl}.
      \item Save your configuration and run:\\
	    \code{make sdk}
      \item At the end, you have a toolchain archive in
            \code{output/images/riscv64-buildroot-linux-musl_sdk-buildroot.tar.gz}
      \item Extract the archive in a suitable directory, and
	    in the extracted directory, run: \code{./relocate-sdk.sh}
    \end{itemize}
    \column{0.35\textwidth}
    \includegraphics[width=\textwidth]{asciinema/655846.png}\\
    \tiny \url{https://asciinema.org/a/655846}
  \end{columns}
\end{frame}

\subsection{The RISC-V CPU architecture}

\begin{frame}{RISC-V: a new open-source ISA}
  \begin{columns}
  \column{0.8\textwidth}
  \small
  \begin{itemize}
     \item ISA: {\em Instruction Set Architecture}
     \item Created by the University of California Berkeley, in a world
           dominated by proprietary ISAs with heavy royalties (ARM, x86)
     \item Exists in 32, 64 and 128 bit variants, from microcontrollers
	   to powerful server hardware.
     \item Anyone can use and extend it to create their own SoCs
	   and CPUs.
     \item This reduces costs and promotes reuse and collaboration
     \item Implementations can be proprietary. Many hardware vendors
	   are using RISC-V CPUs in their hardware (examples:
	   Microchip, Western Digital, Nvidia...)
     \item Free implementations are also available
  \end{itemize}
  See \url{https://en.wikipedia.org/wiki/RISC-V}
  \column{0.2\textwidth}
  \includegraphics[width=\textwidth]{graphics/RISC-V-logo.pdf}\\
  \vspace{1cm}
  \includegraphics[width=\textwidth]{graphics/shaktilogo.png}\\
  \vspace{0.5cm}
  \tiny Shakti Open Source Processor Development Ecosystem (BSD license)
  \end{columns}
\end{frame}

\begin{frame}{RISC-V boards supported by Linux}
  \small
  \begin{columns}
  \column{0.6\textwidth}
  How to find out with boards are supported by mainline Linux?
  \begin{itemize}
  \item In the Linux kernel sources, run:\\
        \code{find arch/riscv/boot/dts -name "*.dts"}
  \item You can also synthetize RISC-V cores on FPGAs
  \item You can also get started with the QEMU emulator, which simulates a
        virtual board with {\em virtio} hardware
  \end{itemize}
  Already try it with JSLinux: \url{https://bellard.org/jslinux/}
  \column{0.4\textwidth}
        \includegraphics[height=0.5\textheight]{asciinema/655447.png}
        \tiny \url{https://asciinema.org/a/655447}
  \end{columns}
\end{frame}

\begin{frame}[fragile]{Beagleboard.org RISC-V boards}
    Open Hardware and community friendly boards
    \begin{columns}
    \column{0.5\textwidth}
    \includegraphics[width=0.8\textwidth]{graphics/beagle-v-fire.png}
    \vspace{0.5cm}
    \small BeagleV-Fire\\
    \tiny \url{https://www.beagleboard.org/boards/beaglev-fire}\\
    Microchip Polarfire MPFS025T SoC FPGA, 150 USD.
    \column{0.5\textwidth}
    \includegraphics[width=0.8\textwidth]{graphics/beagle-v-ahead.png}
    \vspace{0.5cm}
    \small BeagleV-Ahead\\
    \tiny \url{https://www.beagleboard.org/boards/beaglev-ahead}\\
    Alibaba T-Head TH1520 SoC, 150 USD.
    \end{columns}
\end{frame}

\begin{frame}{Other community friendly RISC-V boards}
    \begin{columns}
    \tiny
    \column{0.48\textwidth}
    \includegraphics[height=0.3\textheight]{graphics/visionfive2.jpg}\\
    \href{https://www.starfivetech.com/en/site/boards}{VisionFive2}
    by StarFive, StarFive JH7110 quad-core CPU with IMG BXE4-32 GPU, 40 pin Raspberry PI
    compatible header, 130 USD (8 GB version). Good upstream support.\\
    \vspace{0.5cm}
    \includegraphics[height=0.3\textheight]{graphics/lpi4a.jpg}\\
    \href{https://wiki.sipeed.com/hardware/en/lichee/th1520/lpi4a/1_intro.html}{LicheePi
    4A} from Sipeed. Like BeagleV-Ahead, Alibaba T-Head TH1520 SoC.
    Supported through a community only effort. 180 USD.
    \column{0.52\textwidth}
    \includegraphics[width=\textwidth]{graphics/benchmark.png}\\
    \url{https://wiki.sipeed.com/hardware/en/lichee/th1520/lpi4a/1_intro.html}\\
    \vspace{0.5cm}
    StarFive JH7110: VisionFive2\\
    Alibaba T-Head TH1520: BeagleV-Ahead, LicheePi 4A\\
    \vspace{0.5cm}
    \small Thanks to Drew Fustini for the selection!
    \end{columns}
\end{frame}

\begin{frame}{Inexpensive Milk-V boards}
    \begin{columns}
    \tiny
    \column{0.5\textwidth}
    \includegraphics[height=0.5\textheight]{graphics/milkv-duo.jpg}\\
    \url{https://milkv.io/duo}\\
    \vspace{0.5cm}
    Milk-V Duo:\\
    Cvitech CV1800B C906@1GHz + C906@700MHz CPU\\
    64 MB RAM, 5 USD \\
    \vspace{0.5cm}
    Milk-V Duo 256M:\\
    Sophgo SG2002 C906@1GHz + C906@700MHz, 1xCortex-A53 @ 1GHz\\
    256 MB RAM, 8 USD
    \column{0.5\textwidth}
    \includegraphics[height=0.4\textheight]{graphics/milkv-duos.png}\\
    \vspace{0.5cm}
    \url{https://milkv.io/duo-s}\\
    Sophgo SG2000 C906@1GHz + C906@700MHz, 1xCortex-A53 @ 1GHz\\
    512 MB RAM, 10 USD\\
    \vspace{0.5cm}
    \footnotesize
    Products targeting camera applications\\
    Caution: 1 core for Linux, 1 core for RTOS\\
    Preliminary support in upstream kernel\\
    Thanks to Thomas Bonnefille for the recommendation!
    \end{columns}
\end{frame}

\subsection{Back to the cross-compiling toolchain}

\begin{frame}[fragile]{Testing the toolchain}
\begin{itemize}
\item Create a new \code{riscv64-env.sh} file you can source to set\\
    environment variables for your project:
\begin{block}{}
\scriptsize
\begin{verbatim}
export PATH=$HOME/toolchain/riscv64-buildroot-linux-musl_sdk-buildroot/bin:$PATH
\end{verbatim}
\end{block}
\item Run \code{source riscv64-env.sh}, take a \code{hello.c} file and test your new compiler:
\begin{block}{}
\scriptsize
\begin{verbatim}
$ riscv64-linux-gcc -static -o hello hello.c
$ file hello
hello: ELF 64-bit LSB executable, UCB RISC-V, double-float ABI, version 1 (SYSV), statically linked,
not stripped
\end{verbatim}
\end{block}
     We are compiling statically so far to avoid having to deal with shared
     libraries.
\item Test your executable with QEMU in user mode:
\begin{block}{}
\begin{verbatim}
$ qemu-riscv64 hello
Hello world!
\end{verbatim}
\end{block}
\end{itemize}
\end{frame}

\subsection{Hardware emulator}

\begin{frame}[fragile]{Finding which machines are emulated by QEMU}
Tests made with QEMU 6.2.0 (Ubuntu 22.04)
\begin{block}{}
\small
\begin{verbatim}
sudo apt install qemu-system-misc
$ qemu-system-riscv64 -M ?
Supported machines are:
none                 empty machine
shakti_c             RISC-V Board compatible with Shakti SDK
sifive_e             RISC-V Board compatible with SiFive E SDK
sifive_u             RISC-V Board compatible with SiFive U SDK
spike                RISC-V Spike board (default)
virt                 RISC-V VirtIO board
\end{verbatim}
\end{block}
We are going to use the \code{virt} one, emulating VirtIO peripherals\\
(more efficient than emulating real hardware).
\end{frame}

\subsection{Booting process and privileges}

\begin{frame}
  \frametitle{RISC-V privilege modes}
  \begin{columns}
  \column{0.65\textwidth}
  RISC-V has three privilege modes:
  \begin{itemize}
    \item {\bf U}ser (U-Mode): applications
    \item {\bf S}upervisor (S-Mode): OS kernel
    \item {\bf M}achine (M-Mode): bootloader and firmware
  \end{itemize}
  Here are typical combinations:
  \begin{itemize}
    \item {\bf M}: simple embedded systems
    \item {\bf M}, {\bf U}: embedded systems with memory protection
    \item {\bf M}, {\bf S}, {\bf U}: UNIX-style operating systems with virtual memory
  \end{itemize}
  \column{0.35\textwidth}
    \includegraphics[height=0.8\textheight]{graphics/riscv-boot.pdf}
  \end{columns}
\end{frame}

\subsection{Firmware}

\begin{frame}[fragile]
  \frametitle{OpenSBI: Open Supervisor Binary Interface}
  \begin{columns}
  \column{0.6\textwidth}
  \begin{itemize}
  \item Required to start an OS (S mode) from the Supervisor/Firmware (M mode)
  \item Would be the first thing to build.
  \item However, OpenSBI 0.9 is already integrated in
        \code{qemu-system-riscv64} and I got issues replacing it.
        Let's keep this one. It's like a BIOS.
  \end{itemize}
  \column{0.4\textwidth}
    \includegraphics[width=\textwidth]{graphics/qemu-opensbi.png}
  \end{columns}
\end{frame}

\subsection{U-Boot bootloader}

\begin{frame}[fragile]
  \frametitle{Environment for U-Boot cross-compiling}
  \begin{itemize}
  \item Clone the U-Boot Git tree (go to \url{https://u-boot.org})\\
  \begin{block}{}
  \begin{verbatim}
git clone https://github.com/u-boot/u-boot
cd u-boot
git tag | grep 2024.04
git checkout v2024.04
\end{verbatim}
  \end{block}{}
  \item Let's add an environment variable to our \code{riscv64-env.sh}
        file for cross-compiling:
  \begin{block}{}
  \begin{verbatim}
export CROSS_COMPILE=riscv64-linux-
\end{verbatim}
  \end{block}{}
  \item \code{CROSS_COMPILE} is the cross-compiler prefix, as our
      cross-compiler is {\bf \tt{riscv64-linux-}}\code{gcc}.
  \end{itemize}
\end{frame}

\begin{frame}[fragile]
  \frametitle{Cross-compiling U-Boot}
  \begin{itemize}
     \item Find U-Boot ready-made configurations for RISC-V:
  \begin{block}{}
  \begin{verbatim}
ls configs | grep riscv
\end{verbatim}
  \end{block}{}
  \item We will choose the configuration for QEMU and U-Boot running in S Mode:
  \begin{block}{}
  \begin{verbatim}
make qemu-riscv64_smode_defconfig
\end{verbatim}
  \end{block}{}
  \item Now let's compile U-Boot (\code{-j20}: 20 compile jobs in parallel)
  \begin{block}{}
  \begin{verbatim}
make -j20
\end{verbatim}
  \end{block}{}
  \item Result: \code{u-boot.bin} (859376 bytes!).\\
        We could make it much smaller by removing many options!
  \end{itemize}
\end{frame}

\begin{frame}[fragile]
  \frametitle{Starting U-Boot in QEMU}
  \begin{columns}
  \column{0.6\textwidth}
  \begin{block}{}
  \begin{verbatim}
qemu-system-riscv64 -m 2G \
    -nographic \
    -machine virt \
    -smp 8 \
    -kernel u-boot/u-boot.bin
\end{verbatim}
  \end{block}{}
  \begin{itemize}
    \item \code{-m}: amount of RAM in the emulated machine
    \item \code{-smp}: number of CPUs in the emulated machine
  \end{itemize}
  Exit QEMU with \code{[Ctrl][a]} followed by \code{[x]}
  \column{0.4\textwidth}
    \includegraphics[width=\textwidth]{graphics/qemu-uboot.png}
  \end{columns}
\end{frame}

\subsection{Linux kernel}

\begin{frame}[fragile]
  \frametitle{Environment for kernel cross-compiling}
  \begin{itemize}
  \item Download the latest Linux 6.8.x sources from \url{https://kernel.org}
  \item Extract the sources: \code{tar xf linux-6.8.<x>.tar.xz}
  \item Let's rename the source directory to make our instructions
        version independent: \code{mv linux-6.8.<x> linux}
  \item Go to the Linux source directory: \code{cd linux}
  \item Let's add two environment variables for kernel cross-compiling to our
  \code{riscv64-env.sh} file:
  \begin{block}{}
  \begin{verbatim}
export CROSS_COMPILE=riscv64-linux-
export ARCH=riscv
\end{verbatim}
  \end{block}{}
  \item \code{ARCH} is the name of the subdirectory in \kdir{arch} corresponding
      to the target architecture.
  \end{itemize}
\end{frame}

\begin{frame}[fragile]
  \frametitle{Kernel configuration}
  \begin{itemize}
  \item Lets take the default Linux kernel configuration for RISC-V:
    \begin{block}{}
    \small
    \begin{verbatim}
$ make help | grep defconfig
  defconfig       - New config with default from ARCH supplied defconfig
  savedefconfig   - Save current config as ./defconfig (minimal config)
  alldefconfig    - New config with all symbols set to default
  olddefconfig    - Same as oldconfig but sets new symbols to their
  nommu_k210_defconfig        - Build for nommu_k210
  nommu_k210_sdcard_defconfig - Build for nommu_k210_sdcard
  nommu_virt_defconfig        - Build for nommu_virt
$ make defconfig
\end{verbatim}
    \end{block}{}
  \item We can now further customize the configuration:
    \begin{block}{}
    \begin{verbatim}
make menuconfig
\end{verbatim}
    \end{block}{}
  \end{itemize}
\end{frame}

\begin{frame}[fragile]
  \frametitle{Compiling the kernel}
    \begin{block}{}
    \begin{verbatim}
make -j 20
\end{verbatim}
    \end{block}{}
    At the end, you have these files:\\
    \code{vmlinux}: raw kernel in ELF format (not bootable, for debugging)\\
    \code{arch/riscv/boot/Image}: uncompressed bootable kernel\\
    \code{arch/riscv/boot/Image.gz}: compressed kernel
\end{frame}

\subsection{Booting the kernel}

\begin{frame}[fragile]
  \frametitle{Booting the Linux kernel directly}
  We could boot the Linux kernel directly as follows
  \begin{block}{}
  \begin{verbatim}
qemu-system-riscv64 -m 2G \
    -nographic \
    -machine virt \
    -smp 8 \
    -kernel linux/arch/riscv/boot/Image \
    -append "console=ttyS0" \
\end{verbatim}
  \end{block}{}
  However, what we want to demonstrate is the normal booting process:\\
  OpenSBI \textrightarrow U-Boot \textrightarrow Linux \textrightarrow Userspace
\end{frame}

\begin{frame}[fragile]
  \frametitle{Booting the Linux kernel from U-Boot}
  \begin{itemize}
  \item We want to show how to set the U-Boot environment to load the
        Linux kernel and to specify the Linux kernel command line
  \item For this purpose, we will need some storage space to store the
        U-Boot environment, load the kernel binary, and also to
        contain the filesystem that Linux will boot on.
  \item Therefore, let's create a disk image to give some storage space
        for QEMU
  \end{itemize}
\end{frame}

\begin{frame}[fragile]
  \frametitle{Disk image creation (1)}
  \begin{columns}
  \column{0.6\textwidth}
  \begin{itemize}
  \item Let's create a 128 MB disk image:
    \begin{block}{}
    \begin{verbatim}
dd if=/dev/zero of=disk.img bs=1M count=128
\end{verbatim}
    \end{block}{}
  \item Let's create two partitions in this image
    \begin{block}{}
    \begin{verbatim}
cfdisk disk.img
\end{verbatim}
    \end{block}{}
    \begin{itemize}
      \item A first 64 MB primary partition (type \code{W95 FAT32 (LBA)}), marked as bootable
      \item A second partition with remaining space (default type: \code{Linux})
    \end{itemize}
  \item Fun note: no need to be \code{root} here!
  \end{itemize}
  \column{0.4\textwidth}
    \includegraphics[width=\textwidth]{asciinema/656814.png}\\
    \tiny \url{https://asciinema.org/a/656814}
  \end{columns}
\end{frame}

\begin{frame}[fragile]
  \frametitle{Disk image creation (2)}
  \begin{itemize}
  \item Let's access the partitions in this disk image:
    \begin{block}{}
    \begin{verbatim}
sudo losetup -f --show --partscan disk.img
/dev/loop31

ls -la /dev/loop31*
brw-rw---- 1 root disk   7,  2 Jan 14 10:50 /dev/loop31
brw-rw---- 1 root disk 259, 11 Jan 14 10:50 /dev/loop31p1
brw-rw---- 1 root disk 259, 12 Jan 14 10:50 /dev/loop31p2
\end{verbatim}
    \end{block}{}
  \item We can now format the partitions:
    \begin{block}{}
    \begin{verbatim}
sudo mkfs.vfat -F 32 -n boot /dev/loop31p1
sudo mkfs.ext4 -L rootfs /dev/loop31p2
\end{verbatim}
    \end{block}{}
  \end{itemize}
\end{frame}

\begin{frame}[fragile]
  \frametitle{Copying the Linux image to the FAT partition}
  \begin{itemize}
  \item Let's create a mount point for the FAT partition:
    \begin{block}{}
    \begin{verbatim}
mkdir /mnt/boot
\end{verbatim}
    \end{block}{}
  \item Let's mount it:
    \begin{block}{}
    \begin{verbatim}
sudo mount /dev/loop31p1 /mnt/boot
\end{verbatim}
    \end{block}{}
  \item Let's copy the kernel image to it:
    \begin{block}{}
    \begin{verbatim}
sudo cp linux/arch/riscv/boot/Image /mnt/boot
\end{verbatim}
    \end{block}{}
  \item And then unmount the filesystem to commit changes:
    \begin{block}{}
    \begin{verbatim}
sudo umount /mnt/boot
\end{verbatim}
    \end{block}{}
  \end{itemize}
\end{frame}

\begin{frame}[fragile]
  \frametitle{Recompiling U-Boot for environment support}
  \begin{columns}
  \column{0.6\textwidth}
  We want U-Boot be able to use an environment stored in
  the FAT partition we created. This way we can customize U-Boot's
  behaviour!
  \begin{itemize}
  \item So, let's reconfigure U-Boot:
    \begin{block}{}
    \begin{verbatim}
make menuconfig
\end{verbatim}
    \end{block}{}
    \begin{itemize}
      \item \code{CONFIG_ENV_IS_IN_FAT=y}
      \item \code{CONFIG_ENV_FAT_INTERFACE="virtio"}
      \item \code{CONFIG_ENV_FAT_DEVICE_AND_PART="0:1"}
    \end{itemize}
  \item Then recompile U-Boot
    \begin{block}{}
    \begin{verbatim}
make -j20
\end{verbatim}
    \end{block}{}
  \end{itemize}
  \column{0.4\textwidth}
    \includegraphics[width=\textwidth]{asciinema/656816.png}\\
    \tiny \url{https://asciinema.org/a/656816}
  \end{columns}
\end{frame}

\begin{frame}[fragile]
  \frametitle{Run U-Boot with an environment}
  \begin{itemize}
    \item Add a disk to the emulated machine:
    \begin{block}{}
    \begin{verbatim}
qemu-system-riscv64 -m 2G -nographic -machine virt -smp 8 \
    -kernel u-boot/u-boot.bin \
    -drive file=disk.img,format=raw,id=hd0 \
    -device virtio-blk-device,drive=hd0
\end{verbatim}
    \end{block}
    \item In U-Boot, you should now be able to save an environment:
    \begin{block}{}
\begin{verbatim}
=> setenv foo bar
=> saveenv
=> reset
...
=> printenv foo
bar
\end{verbatim}
    \end{block}
  \end{itemize}
\end{frame}

\subsection{Booting Linux from U-Boot}

\begin{frame}[fragile]
  \frametitle{Requirements for booting Linux}
  To boot the Linux kernel, U-Boot needs to load
  \begin{itemize}
    \item A Linux kernel image. In our case, let's load it from our virtio disk\\
	  to RAM (find a suitable RAM address by using
          the \code{bdinfo} command in U-Boot):
    \begin{block}{}
    \begin{verbatim}
fatload virtio 0:1 84000000 Image
\end{verbatim}
    \end{block}
    \item A {\em Device Tree Binary (DTB)}, letting the kernel know
          which SoC and devices we have. This allows the same kernel
	  to support many different SoCs and boards.
         \begin{itemize}
            \item {\em DTB} files are compiled from {\em DTS} files in
                  \kdir{arch/riscv/boot/dts}
	    \item However, there is no such {\em DTS} file for the
		  RISC-V QEMU virt board.
	    \item The {\em DTB} for our board is actually passed by QEMU
		  to OpenSBI and then to U-Boot.
	    \item In U-Boot, at least in our case, the {\em DTB} is available
		  in RAM at address \code{${fdtcontroladdr}}
         \end{itemize}
  \end{itemize}
\end{frame}

\begin{frame}[fragile]
  \frametitle{Linux kernel command line}
    In U-Boot, we need to set the Linux arguments ({\em kernel command line})
    \begin{block}{}
    \small
    \begin{verbatim}
setenv bootargs root=/dev/vda2 console=ttyS0 earlycon=sbi rw
\end{verbatim}
    \end{block}
    \begin{itemize}
       \item \code{root=/dev/vda2}\\
	     Device for Linux to mount as root filesystem
       \item \code{console=ttyS0}\\
	     Device (here first serial line) to send
	     Linux booting messages to
       \item \code{earlycon=sbi}\\
	     Allows to see messages before the console driver is initialized
             ({\em Early Console}).
       \item \code{rw}\\
	     Allows to mount the root filesystem in read-write mode.
    \end{itemize}
\end{frame}

\begin{frame}[fragile]
  \frametitle{Booting Linux}
  \begin{itemize}
    \item Here's the command to boot the Linux \code{Image} file:
    \begin{block}{}
    \begin{verbatim}
booti <Linux address> - <DTB address>
\end{verbatim}
    \end{block}
    \item In our case:
    \begin{block}{}
    \begin{verbatim}
booti 0x84000000 - ${fdtcontroladdr}
\end{verbatim}
    \end{block}
    \item So, let's define the default series of commands that U-Boot
	  will automatically run:
    \begin{block}{}
    \scriptsize
    \begin{verbatim}
setenv bootcmd 'fatload virtio 0:1 84000000 Image; booti 0x84000000 - ${fdtcontroladdr}'
\end{verbatim}
    \end{block}
    \item Save these new settings:
    \begin{block}{}
    \begin{verbatim}
saveenv
\end{verbatim}
    \end{block}
    \item And boot our system (\code{boot} runs \code{bootcmd}):
    \begin{block}{}
    \begin{verbatim}
boot
\end{verbatim}
    \end{block}
  \end{itemize}
\end{frame}

\begin{frame}[fragile]
  \frametitle{Booting Linux... almost there}
  \begin{columns}
  \column{0.7\textwidth}
  \tiny
  \begin{block}{}
  \begin{verbatim}
[    0.581124] NET: Registered PF_INET6 protocol family
[    0.588486] Segment Routing with IPv6
[    0.588698] In-situ OAM (IOAM) with IPv6
[    0.589026] sit: IPv6, IPv4 and MPLS over IPv4 tunneling driver
[    0.591357] NET: Registered PF_PACKET protocol family
[    0.595137] 9pnet: Installing 9P2000 support
[    0.595619] Key type dns_resolver registered
[    0.615441] debug_vm_pgtable: [debug_vm_pgtable         ]: Validating architecture page...
[    0.624764] Legacy PMU implementation is available
[    0.625600] clk: Disabling unused clocks
[    0.625790] ALSA device list:
[    0.625868]   No soundcards found.
[    0.672876] EXT4-fs (vda2): recovery complete
[    0.673455] EXT4-fs (vda2): mounted filesystem 2c5da046-5bee-4760-8d16-573bb8d1c176 r/w...
[    0.673876] VFS: Mounted root (ext4 filesystem) on device 254:2.
[    0.675577] devtmpfs: error mounting -2
[    0.698640] Freeing unused kernel image (initmem) memory: 2240K
[    0.700178] Run /sbin/init as init process
[    0.700602] Run /etc/init as init process
[    0.700710] Run /bin/init as init process
[    0.700812] Run /bin/sh as init process
[    0.700985] Kernel panic - not syncing: No working init found.  Try passing init= option...
  \end{verbatim}
  \end{block}
  \small
  Linux booted, mounted the root filesystem, but failed to find an {\em
  init} program to run. Let's add one!
  \column{0.3\textwidth}
    \includegraphics[height=0.8\textheight]{graphics/system-boot.pdf}
  \end{columns}
\end{frame}

\subsection{Building the root filesystem}

\begin{frame}[fragile]
  \frametitle{BusyBox - Most Linux commands in one binary}
  \tiny
  % To update this list, compile busybox with "make allyesconfig" on x86
  % Then run "./busybox" and reduce the terminal width until the first
  % line ends with "blkid". Then copy the output here.
  \begin{verbatim}
[, [[, acpid, add-shell, addgroup, adduser, adjtimex, arch, arp, arping, ash, awk, base64, basename, bc, beep, blkdiscard, blkid,
blockdev, bootchartd, brctl, bunzip2, bzcat, bzip2, cal, cat, chat, chattr, chgrp, chmod, chown, chpasswd, chpst, chroot, chrt,
chvt, cksum, clear, cmp, comm, conspy, cp, cpio, crond, crontab, cryptpw, cttyhack, cut, date, dc, dd, deallocvt, delgroup,
deluser, depmod, devmem, df, dhcprelay, diff, dirname, dmesg, dnsd, dnsdomainname, dos2unix, dpkg, dpkg-deb, du, dumpkmap,
dumpleases, echo, ed, egrep, eject, env, envdir, envuidgid, ether-wake, expand, expr, factor, fakeidentd, fallocate, false,
fatattr, fbset, fbsplash, fdflush, fdformat, fdisk, fgconsole, fgrep, find, findfs, flock, fold, free, freeramdisk, fsck,
fsck.minix, fsfreeze, fstrim, fsync, ftpd, ftpget, ftpput, fuser, getopt, getty, grep, groups, gunzip, gzip, halt, hd, hdparm,
head, hexdump, hexedit, hostid, hostname, httpd, hush, hwclock, i2cdetect, i2cdump, i2cget, i2cset, i2ctransfer, id, ifconfig,
ifdown, ifenslave, ifplugd, ifup, inetd, init, insmod, install, ionice, iostat, ip, ipaddr, ipcalc, ipcrm, ipcs, iplink, ipneigh,
iproute, iprule, iptunnel, kbd_mode, kill, killall, killall5, klogd, last, less, link, linux32, linux64, linuxrc, ln, loadfont,
loadkmap, logger, login, logname, logread, losetup, lpd, lpq, lpr, ls, lsattr, lsmod, lsof, lspci, lsscsi, lsusb, lzcat, lzma,
lzop, makedevs, makemime, man, md5sum, mdev, mesg, microcom, mim, mkdir, mkdosfs, mke2fs, mkfifo, mkfs.ext2, mkfs.minix, mkfs.vfat,
mknod, mkpasswd, mkswap, mktemp, modinfo, modprobe, more, mount, mountpoint, mpstat, mt, mv, nameif, nanddump, nandwrite,
nbd-client, nc, netstat, nice, nl, nmeter, nohup, nologin, nproc, nsenter, nslookup, ntpd, nuke, od, openvt, partprobe, passwd,
paste, patch, pgrep, pidof, ping, ping6, pipe_progress, pivot_root, pkill, pmap, popmaildir, poweroff, powertop, printenv, printf,
ps, pscan, pstree, pwd, pwdx, raidautorun, rdate, rdev, readahead, readlink, readprofile, realpath, reboot, reformime,
remove-shell, renice, reset, resize, resume, rev, rm, rmdir, rmmod, route, rpm, rpm2cpio, rtcwake, run-init, run-parts, runlevel,
runsv, runsvdir, rx, script, scriptreplay, sed, sendmail, seq, setarch, setconsole, setfattr, setfont, setkeycodes, setlogcons,
setpriv, setserial, setsid, setuidgid, sh, sha1sum, sha256sum, sha3sum, sha512sum, showkey, shred, shuf, slattach, sleep, smemcap,
softlimit, sort, split, ssl_client, start-stop-daemon, stat, strings, stty, su, sulogin, sum, sv, svc, svlogd, svok, swapoff,
swapon, switch_root, sync, sysctl, syslogd, tac, tail, tar, taskset, tc, tcpsvd, tee, telnet, telnetd, test, tftp, tftpd, time,
timeout, top, touch, tr, traceroute, traceroute6, true, truncate, ts, tty, ttysize, tunctl, ubiattach, ubidetach, ubimkvol,
ubirename, ubirmvol, ubirsvol, ubiupdatevol, udhcpc, udhcpc6, udhcpd, udpsvd, uevent, umount, uname, unexpand, uniq, unix2dos,
unlink, unlzma, unshare, unxz, unzip, uptime, users, usleep, uudecode, uuencode, vconfig, vi, vlock, volname, w, wall, watch,
watchdog, wc, wget, which, who, whoami, whois, xargs, xxd, xz, xzcat, yes, zcat, zcip
  \end{verbatim}
  \vfill
  \footnotesize
  Source: run \code{/bin/busybox} - July 2021 status
\end{frame}

\begin{frame}
  \frametitle{BusyBox - Downloading and configuring}
  \begin{columns}
  \column{0.7\textwidth}
     \begin{itemize}
        \item Download BusyBox 1.36.1 sources from \url{https://busybox.net}
        \item Extract the archive with \code{tar xf}
        \item Run \code{make allnoconfig}\\
	      Starts with no applet selected
	\item Run \code{make menuconfig}\\
	      \begin{itemize}
	      \item In \code{Settings} \textrightarrow \code{Build
	      Options}, enable \code{Build static binary (no shared libs)}
	      \item In \code{Settings} \textrightarrow \code{Build
	      Options}, set \code{Cross compiler prefix} to \code{riscv64-linux-}
	      \item In \code{Settings} \textrightarrow \code{Library Tuning},
              enable \code{Command line editing} and \code{Tab completion}.
	      \item Then enable support for the following commands:\\
		    \code{hush}, \code{init}, \code{reboot}, \code{mount}, \code{cat},
		    \code{chmod}, \code{echo}, \code{ls}, \code{mkdir},
                    \code{ps}, \code{top}, \code{uptime}, \code{vi}, \code{httpd}, \code{ifconfig}
	      \end{itemize}
     \end{itemize}
  \column{0.3\textwidth}
    \includegraphics[width=\textwidth]{asciinema/656959.png}\\
    \tiny \url{https://asciinema.org/a/656959}
  \end{columns}
\end{frame}

\begin{frame}[fragile]
  \frametitle{BusyBox - Installing and compiling}
  \begin{columns}
  \column{0.6\textwidth}
  \begin{itemize}
     \item Compiling: \code{make -j 20}\\
	   Resulting size: only 460,840 bytes! (could be 300,000 with fewer features)\\
	   Funny to see that we're using a 64 bit system to run
	   such small programs!
     \item Installing in \code{_install/}: \code{make install}
     \item See the created directory structure and the symbolic links
	   to \code{/bin/busybox}
     \item Installing to the root filesystem:
    \begin{block}{}
    \begin{verbatim}
sudo mkdir /mnt/rootfs
sudo mount /dev/loop31p2 /mnt/rootfs
sudo rsync -aH _install/ /mnt/rootfs/
\end{verbatim}
    \end{block}
  \end{itemize}
  \column{0.4\textwidth}
    \includegraphics[width=\textwidth]{graphics/busybox-tree.png}\\
  \end{columns}
\end{frame}

\begin{frame}[fragile]
  \frametitle{Completing the root filesystem (1)}
  \small
  We also need to create a \code{dev} directory for device files.
  The kernel will automatically mount the \code{devtmpfs} filesystem there
  (as \kconfigval{CONFIG_DEVTMPFS_MOUNT}{y})
  \scriptsize
  \begin{block}{}
  \begin{verbatim}
sudo mkdir /mnt/rootfs/dev
sudo umount /mnt/rootfs
\end{verbatim}
    \end{block}
  \small
  \vfill
  The system should have everything it needs to boot now:
  \scriptsize
  \begin{block}{}
  \begin{verbatim}
[    0.463042] VFS: Mounted root (ext4 filesystem) on device 254:2.
[    0.464862] devtmpfs: mounted
[    0.486872] Freeing unused kernel image (initmem) memory: 2240K
[    0.488446] Run /sbin/init as init process
starting pid 87, tty '': '/etc/init.d/rcS'
can't run '/etc/init.d/rcS': No such file or directory

Please press Enter to activate this console.
starting pid 89, tty '': '-/bin/sh'

BusyBox v1.36.1 (2024-04-29 07:21:47 CEST) built-in shell (ash)
#
\end{verbatim}
  \end{block}
\end{frame}


\begin{frame}[fragile]
  \frametitle{Completing the root filesystem (2)}
  Let's try to run the \code{ps} command to see the list of processes:
    \begin{block}{}
\begin{verbatim}
# ps
  PID USER       VSZ STAT COMMAND
ps: can't open '/proc': No such file or directory
\end{verbatim}
    \end{block}
  We need to create \code{/proc} and \code{/sys} so that we can
  mount the \code{proc} and \code{sysfs} virtual filesystems
  on the target, which are needed by many system commands. We
  can now run the commands {\bf on the target system}:
    \begin{block}{}
\begin{verbatim}
mkdir /proc
mkdir /sys
mount -t proc nodev /proc
mount -t sysfs nodev /sys
\end{verbatim}
    \end{block}
\end{frame}

\begin{frame}[fragile]
  \frametitle{Completing the root filesystem (3)}
  Let's automate the mounting of \code{proc} and \code{sysfs}...
  \begin{itemize}
  \item Let's create an \code{/etc/inittab} file to configure Busybox Init:
    \begin{block}{}
\begin{verbatim}
# This is run first script:
::sysinit:/etc/init.d/rcS
# Start an "askfirst" shell on the console:
::askfirst:/bin/sh
\end{verbatim}
    \end{block}
  \item Let's create and fill \code{/etc/init.d/rcS} to automatically
    mount the virtual filesystems:
    \begin{block}{}
\begin{verbatim}
#!/bin/sh
mount -t proc nodev /proc
mount -t sysfs nodev /sys
\end{verbatim}
    \end{block}
  \end{itemize}
\end{frame}

\begin{frame}[fragile]
  \frametitle{Common mistakes}
  \begin{itemize}
    \item Don't forget to make the \code{rcS} script executable. Linux
          won't allow to execute it otherwise.
    \item Do not forget \code{#!/bin/sh} at the beginning of shell scripts!
          Without the leading \code{#!} characters, the Linux kernel has no
          way to know it is a shell script and will try to execute it as
          a binary file!
    \item Don't forget to specify the execution of a shell in
          \code{/etc/inittab} or at the end of \code{/etc/init.d/rcS}.
          Otherwise, execution will just stop without letting you type new commands!
  \end{itemize}
\end{frame}

\begin{frame}[fragile]
  \frametitle{Add support for networking (1)}
  \begin{itemize}
    \item Add a network interface to the emulated machine:
    \begin{block}{}
\begin{verbatim}
sudo qemu-system-riscv64 -m 2G -nographic -machine virt -smp 8 \
    -kernel u-boot/u-boot.bin \
    -drive file=disk.img,format=raw,id=hd0 \
    -device virtio-blk-device,drive=hd0 \
    -netdev tap,id=tapnet,ifname=tap2,script=no,downscript=no \
    -device virtio-net-device,netdev=tapnet
\end{verbatim}
    \end{block}
    \item Need to be \code{root} to bring up the \code{tap2} network
          interface
  \end{itemize}
\end{frame}

\begin{frame}[fragile]
  \frametitle{Add support for networking (2)}
  \begin{itemize}
    \item On the target machine:
    \begin{block}{}
\begin{verbatim}
ifconfig -a
ifconfig eth0 192.168.2.100
\end{verbatim}
    \end{block}
    \item On the host machine:
    \begin{block}{}
\begin{verbatim}
ifconfig -a
sudo ifconfig tap2 192.168.2.1
ping 192.168.2.100
\end{verbatim}
    \end{block}
  \end{itemize}
\end{frame}

\begin{frame}[fragile]
  \frametitle{Simple CGI script}
    \begin{block}{}
    \small
\begin{verbatim}
#!/bin/sh
echo "Content-type: text/html"
echo
echo "<html>"
echo "<meta http-equiv=\"refresh\" content=\"1\">"
echo "<header></header><body>"
echo "<h1>Uptime information</h1>"
echo "Your embedded device has been running for:<pre><font color=Blue>"
echo `uptime`
echo "</font></pre>"
echo "</body></html>"
\end{verbatim}
    \end{block}
    Store it in \code{/www/cgi-bin/uptime} and make it executable.
\end{frame}

\begin{frame}[fragile]
  \frametitle{Start a web server}
  \begin{itemize}
    \item On the target machine:
    \begin{block}{}
\begin{verbatim}
/usr/sbin/httpd -h /www
\end{verbatim}
    \end{block}
    \item On the host machine, open in your browser:\\
          \url{http://192.168.2.100/cgi-bin/uptime}
  \end{itemize}
\end{frame}


\begin{frame}
  \frametitle{Demo: booting Linux on Milk-V Duo S board}
  \begin{columns}
  \column{0.7\textwidth}
  \begin{itemize}
    \item We can use the same binary kernel!
    \item A Linux kernel can be built for many different SoCs at the
          same time.
    \item All we need is just a different description of the hardware
         (DTB)
    \item However, support for this board and its SoC is pretty basic
          in the mainline kernel so far:
          \begin{itemize}
              \item The MMC driver not fully ready yet (patches
                    submitted for the 6.9 kernel)
              \item We will therefore boot on a filesystem in RAM
                    ({\em Initramfs}), included in the kernel binary.
          \end{itemize}
  \end{itemize}
  \column{0.3\textwidth}
    \includegraphics[width=\textwidth]{graphics/milkv-duos.pdf}\\
  \end{columns}
\end{frame}

\begin{frame}
  \frametitle{Connecting the Milk-V Duo S board}
  \begin{columns}
  \column{0.65\textwidth}
  \includegraphics[height=0.8\textheight]{graphics/milk-v-duos-connected.jpg}
  \column{0.35\textwidth}
  \small
  Let's use the new \code{tio} command to access the serial line:
  \begin{itemize}
    \item \code{tio} doesn't die but waits when the line is disconnected
    \item \code{tio} can also log to a file
    \item \code{tio} is easy to use: \\
          \code{tio /dev/ttyUSB0}
  \end{itemize}
  \end{columns}
\end{frame}


\begin{frame}[fragile]
  \frametitle{SD card for the Milk-V Duo S board}
  \begin{itemize}
  \item Format your micro-SD card as previously:
       \begin{block}{}
\begin{verbatim}
sudo cfdisk /dev/mmcblk0
\end{verbatim}
        \end{block}
  \item Mount the boot partition
  \item We are not ready to use a mainline U-Boot yet, so copy the
        \code{fip.bin} file from
        \url{https://gitlab.com/michaelopdenacker/embedded-linux-from-scratch-riscv/-/raw/main/binaries/milk-v/duo-s/fip.bin}
        to the boot partition.
  \item Copy the same \code{Image} file to the boot partition:
       \begin{block}{}
\begin{verbatim}
cp arch/riscv/boot/Image /mnt/boot/
\end{verbatim}
        \end{block}
  \item Also copy a DTB from a very similar board:
       \begin{block}{}
\begin{verbatim}
cp arch/riscv/boot/dts/sophgo/cv1812h-huashan-pi.dtb /mnt/boot/
\end{verbatim}
        \end{block}
  \end{itemize}
\end{frame}

\begin{frame}[fragile]
  \frametitle{Boot the Milk-V Duo S board}
  \begin{columns}
  \column{0.75\textwidth}
  \small
  \begin{itemize}
  \item Insert the micro-SD card, power the board, and in the U-Boot prompt, type:
       {\scriptsize
       \begin{block}{}
\begin{verbatim}
setenv bootargs console=ttyS0,115200; fatload mmc 0 82000000 Image;
fatload mmc 0 84000000 cv1812h-huashan-pi.dtb; booti 82000000 - 84000000
\end{verbatim}
        \end{block}
        }
  \item However, it won't boot because we haven't given it a root
        filesystem yet.
  \item So, let's prepare an {\em Initramfs} to boot on, and include
        it into the kernel binary.
  \end{itemize}
  \column{0.25\textwidth}
    \includegraphics[width=\textwidth]{graphics/initramfs.pdf}\\
  \end{columns}
\end{frame}

\begin{frame}[fragile]
  \frametitle{Initramfs for RISC-V (1)}
  \small
  Something that should run on any RISC-V board!
  \begin{itemize}
  \item Mount your root filesystem image again and copy it to a directory
       \begin{block}{}
\begin{verbatim}
sudo mount /dev/loop31p2 /mnt/rootfs
sudo rsync -aH /mnt/rootfs ~/riscv/rootfs
\end{verbatim}
        \end{block}
  \item Linux will try to start \code{/init} in the {\em initramfs}
       \begin{block}{}
\begin{verbatim}
cd ~/riscv/rootfs
ln -s sbin/init .
\end{verbatim}
        \end{block}
  \item You also need to mount the \code{devtmpfs} filesystem manually
        by adding this line to \code{etc/init.d/rcS}:
       \begin{block}{}
\begin{verbatim}
mount -t devtmpfs nodev /dev
\end{verbatim}
        \end{block}
  \end{itemize}
\end{frame}

\begin{frame}[fragile]
  \frametitle{Initramfs for RISC-V (2)}
  \begin{columns}
  \column{0.45\textwidth}
  \small
  \begin{itemize}
  \item Unlike on ARM, you also need a \code{/dev/console} file in
        an {\em Initramfs} before mounting \code{/dev/}:
       \begin{block}{}
\begin{verbatim}
sudo mknod dev/console c 5 1
\end{verbatim}
        \end{block}
  \item Now, configure Linux to bundle this new directory as {\em Initramfs}.
        In \code{General setup}, set \code{Initramfs source file(s)}
        to \code{../rootfs}.
  \item Recompile Linux and update the \code{Image} file on the boot
        partition.
  \item Voilà!
  \end{itemize}
  \column{0.55\textwidth}
       \tiny
       \begin{block}{}
\begin{verbatim}
[    1.106406] mousedev: PS/2 mouse device common for all mice
[    1.114012] sdhci: Secure Digital Host Controller Interface driver
[    1.120482] sdhci: Copyright(c) Pierre Ossman
[    1.125252] Synopsys Designware Multimedia Card Interface Driver
[    1.131783] sdhci-pltfm: SDHCI platform and OF driver helper
[    1.138127] usbcore: registered new interface driver usbhid
[    1.143955] usbhid: USB HID core driver
[    1.149969] NET: Registered PFOINET6 protocol family
[    1.157399] Segment Routing with IPv6
[    1.161377] In-situ OAM (IOAM) with IPv6
[    1.165618] sit: IPv6, IPv4 and MPLS over IPv4 tunneling driver
[    1.173106] NET: Registered PF_PACKET protocol family
[    1.178579] 9pnet: Installing 9P2000 support
[    1.183219] Key type dns_resolver registered
[    1.229810] debug_vm_pgtable: [debug_vm_pgtable         ]: Validat...
[    1.241117] Legacy PMU implementation is available
[    1.246652] clk: Disabling unused clocks
[    1.250832] ALSA device list:
[    1.253991]   No soundcards found.
[    1.259366] dw-apb-uart 4140000.serial: forbid DMA for kernel console
[    1.268035] Freeing unused kernel image (initmem) memory: 2476K
[    1.274290] Run /init as init process

Please press Enter to activate this console.

BusyBox v1.36.1 (2024-04-30 06:50:07 CEST) built-in shell (ash)

~ #
\end{verbatim}
        \end{block}
  \end{columns}
\end{frame}

\begin{frame}
  \frametitle{A few things to remember}
  \begin{itemize}
    \item Embedded Linux is just made out of simple components.\\
          It makes it easier to get started with Linux.
    \item You just need a toolchain, a bootloader, a kernel and a few executables.
    \item RISC-V is a new, open Instruction Set Architecture, use it and support it!
    \item With \href{https://asciinema.org/}{Asciinema}, you can copy text from videos!
    \item You will love \code{tio} as a replacement to \code{picocom}.
  \end{itemize}
\end{frame}

\begin{frame}[fragile]
  \frametitle{Going further and thanks}
  \begin{columns}
  \column{0.7\textwidth}
  \begin{itemize}
    \item Drew Fustini's unmatched presentation about Linux on RISC-V:
          \url{https://tinyurl.com/elc2023-bof}
    \item Bootlin's training materials and conference presentations
          (Creative Commons CC-BY-SA licence): \\
          \url{https://bootlin.com/docs/}
    \item Thanks to Drew Fustini for sharing his personal advice.
    \item Thanks to YOU for attending this talk!
  \end{itemize}
  \column{0.3\textwidth}
    \includegraphics[width=\textwidth]{graphics/you-are-gurus.pdf}\\
    \vspace{0.5cm}
    \includegraphics[width=\textwidth]{graphics/clock.png}\\
    \vspace{0.5cm}
    \scriptsize
    \begin{center}
    See \href{https://gitlab.com/michaelopdenacker/embedded-linux-from-scratch-riscv/-/blob/main/graphics/README}
    {credits for all images}
    \end{center}
  \end{columns}
\end{frame}

\questionslide

\end{document}
