Skip to content

resources: Add doc for riscv-fs-busybox-opensbi-nodisk resource #61

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 7 commits into
base: stable
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions src/riscv-fs/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,9 @@ This repository contains the latest resources for RISC-V full-system builds.

**Directory Structure:**

- **riscv-busybox-opensbi:**
Documentation on building a RISC-V OpenSBI bootloader including Linux kernel and Busybox compiled into a single binary. No extra disk.

- **riscv-opensbi:**
Documentation on building a RISC-V OpenSBI bootloader and Linux 6.5.5 kernel.

Expand Down
249 changes: 249 additions & 0 deletions src/riscv-fs/riscv-busybox-opensbi/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,249 @@
---
title: RISC-V Full System in One Kernel Resource
tags:
- fullsystem
- bootloader
- riscv
layout: default
permalink: resources/riscv-fs-busybox-opensbi
shortdoc: >
Resources to build an OpenSBI bootloader with Linux image, initramfs and Busybox that works with gem5 full system simulations.
author: ["Jonathan Kretschmer"]
---

# RISC-V Linux Full System Bootloader, initramfs and Workload in One

This document provides instructions to create an
(OpenSBI)[https://github.com/riscv-software-src/opensbi] bootloader binary
with Linux kernel image payload containing an initramfs with
(Busybox)[https://www.busybox.net/] that works with gem5 full system simulations.

This guide is inspired by and partly copied from
(riscv-fs-nodisk)[https://github.com/gem5/gem5-resources/blob/stable/src/riscv-fs-alt/riscv-boot-exit-nodisk/README.md]
and (Linux on RISC-V using QEMU and BUSYBOX from scratch)[https://risc-v-machines.readthedocs.io/en/latest/linux/simple/].

For this guide we assume following directory structure. Gem5 source itself is
required either, but not listed here to avoid duplication.
```
riscv-all-in-one/
├── busybox/ # busybox source
├── cpio/ # contains the .cpio file
├── initramfs/ # contains the structure of initramfs
├── linux/ # linux source
├── opensbi/ # OpenSBI source, providing a RISC-V bootloader
├── riscv-gnu-toolchain/ # riscv tool chain for cross compilation
└── README.md # This README file
```

# Building the resource
## Step 1. Building the `riscv-gnu-toolchain`
In this step, we'll use
[GNU toolchain for RISC-V](https://github.com/riscv-collab/riscv-gnu-toolchain).

This step is necessary if you do not have basic libraries built for RISCV or
if you're cross-compiling RISCV.

```sh
cd riscv-all-in-one/
git clone https://github.com/riscv-collab/riscv-gnu-toolchain.git
cd riscv-gnu-toolchain
git switch --detach 20f615317e2ce888dfc11b29ccde4a649494b654 # for reproducability, newer versions should work either
mkdir build
cd build
# --prefix parameter specifying the installation location
# --enable-multilib build either cross-compiler with support for both 32-bit and 64-bit
../configure --prefix=/path/to/rvtoolchain --enable-multilib
make linux -j$(nproc)
```
To update the PATH environment variable so that the RISCV compilers can be
found,
```sh
export PATH=/path/to/rvtoolchain/bin/:$PATH
```
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Do we have to compile the toolchain? If there's a good reason not to use the precompiled toolchain from Ubuntu, then this step is OK, but if it's possible to use the precompiled toolchain, then we should do that.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It might be helpful if you modify the toolchain itself, however in the default case for this resource the shipped toolchain is faily enough, so i changed it to use Debian's g++-12-riscv64-linux-gnu package. See lines 43-74.


## Step 2. Getting and Building `busybox`
More information about Busybox is [here](https://www.busybox.net/).
```sh
cd riscv-all-in-one/
git clone --branch 1_36_stable https://git.busybox.net/busybox.git # choose a stable branch

# alternatively downloading tar
wget https://git.busybox.net/busybox/snapshot/busybox-1_36_1.tar.bz2
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I assume we do have to compile busybox (unlike gcc above). But can you confirm this?

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, Busybox is not distributing for the required RISCV platform (see their download page). Also i configure it to use no shared libraries in order to simplify the guide.

tar xf busybox-1_36_1.tar.bz2
mv busybox-1_36_1 busybox

cd busybox
# create a default configuration
make CROSS_COMPILE=riscv64-unknown-linux-gnu- defconfig
make CROSS_COMPILE=riscv64-unknown-linux-gnu- menuconfig
# Configure static linking in order to simplify things.
# Settings --->
# Build Options --->
# Build static binary (no shared libs) ---> yes
make CROSS_COMPILE=riscv64-unknown-linux-gnu- all -j$(nproc)
make CROSS_COMPILE=riscv64-unknown-linux-gnu- install # optional
```
The files of interest are in `busybox/_install/bin`.

## Step 3. Compiling the Workload (e.g. gem5's m5)
Change to the directory with your clone of gem5 version 24.0.0.1.
```sh
cd gem5/
cd util/m5
scons build/riscv/out/m5
```
**Note**: the default cross-compiler is `riscv64-unknown-linux-gnu-`.
To change the cross-compiler, you can set the cross-compiler using the scons
sticky variable `riscv.CROSS_COMPILE`. For example,
```sh
scons riscv.CROSS_COMPILE=riscv64-linux-gnu- build/riscv/out/m5
```

## Step 4. Determining the Structure of `initramfs`
Your Linux requires a *file system* in order to properly run. So we will
prepare the file structure and the `init` script.
```sh
cd riscv-all-in-one/
mkdir initramfs
cd initramfs
mkdir -p {bin,sbin,dev,etc,home,mnt,proc,sys,usr,tmp}
mkdir -p usr/{bin,sbin}
mkdir -p proc/sys/kernel
# Without following devices, we cannot see the output to `stdout` and `stderr`
fakeroot -- mknod -m 622 dev/console c 5 1
fakeroot -- mknod -m 622 dev/sda b 8 0
fakeroot -- mknod -m 622 dev/tty c 5 0
fakeroot -- mknod -m 622 dev/ttyprintk c 5 3
fakeroot -- mknod -m 622 dev/null c 1 3
```
**Note:** `mknod -m 622 /dev/tty c 5 0` means we're creating `/dev/tty` with
permission of `622`. `c` means a character device being created, `5` is the
major number, and `0` is the minor number. More information about the
major/minor numbering is available at
(https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/tree/Documentation/admin-guide/devices.txt).

Drop the `busybox` executable from the previous section into the filesystem:
```sh
cp ../busybox/busybox ./bin/
```
Drop the workload:
```sh
cp /path/to/your/gem5/util/m5/build/riscv/out/m5 ./sbin/m5 # replace m5 by the desired workload
```
After the kernel has started, we have to start Busybox and finalize the system
initialization. We will use a script called `init` that will do the hard work,
and finally starts the workload.
Create `initramfs/init` script with the following content,
```
#!/bin/busybox sh

# Make symlinks
/bin/busybox --install -s

# Mount system
mount -t devtmpfs devtmpfs /dev
mount -t proc proc /proc
mount -t sysfs sysfs /sys
mount -t tmpfs tmpfs /tmp

# Busybox TTY fix
setsid cttyhack sh

# https://git.busybox.net/busybox/tree/docs/mdev.txt?h=1_32_stable
echo /sbin/mdev > /proc/sys/kernel/hotplug
mdev -s

# enter a shell
#sh
# or execute the workload
/sbin/m5 exit # replace with desired workload
```
Add executable flag:
```sh
chmod +x init
```
At this stage, your initramfs should look like:
```sh
$ tree
.
├── bin
│ └── busybox
├── dev
│ ├── console
│ ├── null
│ ├── sda
│ ├── tty
│ └── ttyprintk
├── etc
├── home
├── init
├── mnt
├── proc
│ └── sys
│ └── kernel
├── sbin
│ └── m5
├── sys
├── tmp
└── usr
├── bin
└── sbin

15 directories, 8 files
```
To create the cpio file from the `initramfs` folder,
```sh
mkdir riscv-all-in-one/cpio
cd riscv-all-in-one/initramfs
fakeroot -- find . -print0 | cpio --owner root:root --null -o --format=newc > ../cpio/initramfs.cpio
# alternatively with the tool available in the (previously build) linux kernel
#../linux/usr/gen_initramfs.sh -o ../cpio/initramfs.cpio ./
lsinitramfs ../cpio/initramfs.cpio # checking the file structure of the created cpio file
```

## Step 5. Compiling `Linux Kernel` with a customized `initramfs`
```sh
cd riscv-all-in-one/
git clone --depth 1 --branch v6.11 https://git.kernel.org/pub/scm/linux/kernel/git/stable/linux.git

# alternatively downloading tar
wget https://cdn.kernel.org/pub/linux/kernel/v6.x/linux-6.11.10.tar.xz
tar xf linux-6.11.10.tar.xz
mv linux-6.11.10 linux

cd linux
# ?? CFLAGS_KERNEL=" -march=rv64gc"
make ARCH=riscv CROSS_COMPILE=riscv64-unknown-linux-gnu- defconfig
make ARCH=riscv CROSS_COMPILE=riscv64-unknown-linux-gnu- menuconfig
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

riscv64-unknown-linux-gnu-? Above you used riscv64-linux-gnu-.

We should use just one cross compiler, and, unless there's a really good reason, it should be a precompiled toolchain, not one that we compile ourselves.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

In the initial commit the lines that referred to riscv64-linux-gnu- just mentioned an alternative usage if the toolchain was not prefixed by the default riscv64-unknown-linux-gnu-. Sorry for the confusion.

As i use the Debian shipped compilers now, i have to specify the toolchain prefix for scons anyways. See line 109.

# Go to "General setup --->"
# Check on "Initial RAM filesystem and RAM disk (initramfs/initrd) support"
# Change "Initramfs source file(s)" to the absoblute path of riscv-all-in-one/cpio/initramfs.cpio
make ARCH=riscv CROSS_COMPILE=riscv64-unknown-linux-gnu- all -j$(nproc)
```
The file of interest is at `arch/riscv/boot/Image`.

## Step 6. Compiling `OpenSBI` with the Linux kernel as the payload
```sh
cd riscv-all-in-one/
git clone https://github.com/riscv-software-src/opensbi
cd opensbi
git switch --detach v1.4
make PLATFORM=generic FW_PAYLOAD_PATH=../linux/arch/riscv/boot/Image CROSS_COMPILE=riscv64-unknown-linux-gnu- -j$(nproc)
```
The desired bootloader file is at `build/platform/generic/firmware/fw_payload.elf`.

# Example

You can run the created bootloader with Linux and Busybox payload using the example riscv `fs_linux.py` config,
```sh
cd gem5
./build/RISCV/gem5.opt configs/example/riscv/fs_linux.py --kernel="../gem5-resources/src/riscv-fs/riscv-all-in-one/opensbi/build/platform/generic/firmware/fw_payload.elf"
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I would prefer all examples to use the stdlib. If you could describe how to turn this into a Workload it would improve the description.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is the hard part for me. As far as i see, only specifying a bootloader without kernel and disk image is not intended by the boards, e.g. RiscvBoard or RISCVMatchedBoard.

Therefore i just show how to slightly modify the riscv-fs.py by obtaining the new kernel or bootloader resource and leaving the disk_image (and kernel) parameter of the set_kernel_disk_workload call unmodified (as dummy resource). See lines 266-282

In the end it might look like

# Set the Full System workload.
board.set_kernel_disk_workload(
    kernel=obtain_resource(
        "riscv-bootloader-vmlinux-5.10", resource_version="1.0.0"
    ), # dummy resource
    disk_image=obtain_resource("riscv-disk-img", resource_version="1.0.0"),  # dummy resource
    bootloader=obtain_resource(
        "riscv-fs-busybox-opensbi-nodisk", resource_version="1.0.0"
    ),
)

```
You can check the console output with `telnet` or gem5's `m5term`,
```sh
telnet localhost <port>
# or
cd util/term
make
./m5term localhost <port>
```