Docs/tutorial: Wrap the text in part?.md to 72 characters

Signed-off-by: Martin Roth <gaumless@gmail.com>
Change-Id: I4f0a07b4ab729aafdb4a1149a7617cd34392cf12
Reviewed-on: https://review.coreboot.org/c/coreboot/+/64967
Tested-by: build bot (Jenkins) <no-reply@coreboot.org>
Reviewed-by: Elyes Haouas <ehaouas@noos.fr>
This commit is contained in:
Martin Roth 2022-06-04 19:52:42 -06:00 committed by Martin L Roth
parent 25aeaac85b
commit d2c3e26513
3 changed files with 348 additions and 313 deletions

View File

@ -2,11 +2,11 @@ Tutorial, part 1: Starting from scratch
===========================================
This tutorial will guide you through the process of setting up a working
coreboot toolchain. In same cases you will find specific instructions for Debian (apt-get),
Fedora (dnf) and Arch Linux (pacman) based package management systems. Use the
instructions according to your system.
coreboot toolchain. In same cases you will find specific instructions
for Debian (apt-get), Fedora (dnf) and Arch Linux (pacman) based package
management systems. Use the instructions according to your system.
** Note: Summaries of each of the steps are at the end of the document. **
**Note: Summaries of each of the steps are at the end of the document.**
Download, configure, and build coreboot
@ -16,13 +16,15 @@ Download, configure, and build coreboot
### Step 1 - Install tools and libraries needed for coreboot
Debian based distros:
`sudo apt-get install -y bison build-essential curl flex git gnat libncurses5-dev m4 zlib1g-dev`
`sudo apt-get install -y bison build-essential curl flex git gnat`
`libncurses5-dev m4 zlib1g-dev`
Arch based distros:
`sudo pacman -S base-devel curl git gcc-ada ncurses zlib`
Redhat based distros:
`sudo dnf install git make gcc-gnat flex bison xz bzip2 gcc g++ ncurses-devel wget zlib-devel patch`
`sudo dnf install git make gcc-gnat flex bison xz bzip2 gcc g++`
`ncurses-devel wget zlib-devel patch`
### Step 2 - Download coreboot source tree
@ -36,8 +38,8 @@ cd coreboot
### Step 3 - Build the coreboot toolchain
Please note that this can take a significant amount of time. Use `CPUS=` to
specify number of `make` jobs to run in parallel.
Please note that this can take a significant amount of time. Use `CPUS=`
to specify number of `make` jobs to run in parallel.
This will list toolchain options and supported architectures:
@ -53,12 +55,12 @@ make crossgcc-aarch64 CPUS=$(nproc) # build Aarch64 toolchain
make crossgcc-riscv CPUS=$(nproc) # build RISC-V toolchain
```
Note that the i386 toolchain is currently used for all x86 platforms, including
x86_64.
Note that the i386 toolchain is currently used for all x86 platforms,
including x86_64.
Also note that you can possibly use your system toolchain, but the results are
not reproducible, and may have issues, so this is not recommended. See step 5
to use your system toolchain.
Also note that you can possibly use your system toolchain, but the
results are not reproducible, and may have issues, so this is not
recommended. See step 5 to use your system toolchain.
### Step 4 - Build the payload - coreinfo
@ -117,7 +119,8 @@ make savedefconfig
cat defconfig
```
There should only be two lines (or 3 if you're using the system toolchain):
There should only be two lines (or 3 if you're using the system
toolchain):
```Text
CONFIG_PAYLOAD_ELF=y
@ -134,8 +137,8 @@ At the end of the build, you should see:
`Build emulation/qemu-i440fx (QEMU x86 i440fx/piix4)``
This means your build was successful. The output from the build is in the build
directory. build/coreboot.rom is the full rom file.
This means your build was successful. The output from the build is in
the build directory. build/coreboot.rom is the full rom file.
Test the image using QEMU
@ -157,8 +160,8 @@ Start QEMU, and point it to the ROM you just built:
qemu-system-x86_64 -bios build/coreboot.rom -serial stdio
```
You should see the serial output of coreboot in the original console window, and
a new window will appear running the coreinfo payload.
You should see the serial output of coreboot in the original console
window, and a new window will appear running the coreinfo payload.
Summary
@ -168,10 +171,10 @@ Summary
### Step 1 summary - Install tools and libraries needed for coreboot
Depending on your distribution you have installed the minimum additional
software requirements to continue with downloading and building coreboot.
Not every distribution has the tools, that would be required,
installed by default. In the following we shortly introduce the purpose of the
installed packages:
software requirements to continue with downloading and building
coreboot. Not every distribution has the tools, that would be required,
installed by default. In the following we shortly introduce the purpose
of the installed packages:
* `build-essential` or `base-devel` are the basic tools for building software.
* `git` is needed to download coreboot from the coreboot git repository.
@ -180,67 +183,72 @@ installed packages:
are needed to build the coreboot toolchain. `gcc` and `gnat` have to be
of the same version.
If you started with a different distribution or package management system you
might need to install other packages. Most likely they are named slightly
different. If that is the case for you, we'd like to encourage you to contribute
to the project and submit a pull request with an update for this documentation
for your system.
If you started with a different distribution or package management
system you might need to install other packages. Most likely they are
named slightly different. If that is the case for you, we'd like to
encourage you to contribute to the project and submit a pull request
with an update for this documentation for your system.
### Step 2 summary - Download coreboot source tree
This will download a 'read-only' copy of the coreboot tree. This just means
that if you made changes to the coreboot tree, you couldn't immediately
contribute them back to the community. To pull a copy of coreboot that would
allow you to contribute back, you would first need to sign up for an account on
gerrit.
This will download a 'read-only' copy of the coreboot tree. This just
means that if you made changes to the coreboot tree, you couldn't
immediately contribute them back to the community. To pull a copy of
coreboot that would allow you to contribute back, you would first need
to sign up for an account on gerrit.
### Step 3 summary - Build the coreboot toolchain.
This builds one of the coreboot cross-compiler toolchains for X86 platforms.
Because of the variability of compilers and the other required tools between
the various operating systems that coreboot can be built on, coreboot supplies
and uses its own cross-compiler toolchain to build the binaries that end up as
part of the coreboot ROM. The toolchain provided by the operating system (the
'host toolchain') is used to build various tools that will run on the local
system during the build process.
This builds one of the coreboot cross-compiler toolchains for X86
platforms. Because of the variability of compilers and the other
required tools between the various operating systems that coreboot can
be built on, coreboot supplies and uses its own cross-compiler toolchain
to build the binaries that end up as part of the coreboot ROM. The
toolchain provided by the operating system (the 'host toolchain') is
used to build various tools that will run on the local system during the
build process.
### Step 4 summary - Build the payload
To actually do anything useful with coreboot, you need to build a payload to
include into the rom. The idea behind coreboot is that it does the minimum amount
possible before passing control of the machine to a payload. There are various
payloads such as grub or SeaBIOS that are typically used to boot the operating
system. Instead, we used coreinfo, a small demonstration payload that allows the
user to look at various things such as memory and the contents of the coreboot
file system (CBFS) - the pieces that make up the coreboot rom.
To actually do anything useful with coreboot, you need to build a
payload to include into the rom. The idea behind coreboot is that it
does the minimum amount possible before passing control of the machine
to a payload. There are various payloads such as grub or SeaBIOS that
are typically used to boot the operating system. Instead, we used
coreinfo, a small demonstration payload that allows the user to look at
various things such as memory and the contents of the coreboot file
system (CBFS) - the pieces that make up the coreboot rom.
### Step 5 summary - Configure the build
This step configures coreboot's build options using the menuconfig interface to
Kconfig. Kconfig is the same configuration program used by the linux kernel. It
allows you to enable, disable, and change various values to control the coreboot
build process, including which mainboard(motherboard) to use, which toolchain to
use, and how the runtime debug console should be presented and saved.
Anytime you change mainboards in Kconfig, you should always run `make distclean`
before running `make menuconfig`. Due to the way that Kconfig works, values will
be kept from the previous mainboard if you skip the clean step. This leads to a
hybrid configuration which may or may not work as expected.
This step configures coreboot's build options using the menuconfig
interface to Kconfig. Kconfig is the same configuration program used by
the linux kernel. It allows you to enable, disable, and change various
values to control the coreboot build process, including which
mainboard(motherboard) to use, which toolchain to use, and how the
runtime debug console should be presented and saved. Anytime you change
mainboards in Kconfig, you should always run `make distclean` before
running `make menuconfig`. Due to the way that Kconfig works, values
will be kept from the previous mainboard if you skip the clean step.
This leads to a hybrid configuration which may or may not work as
expected.
### Step 6 summary - Build coreboot
You may notice that a number of other pieces are downloaded at the beginning of
the build process. These are the git submodules used in various coreboot builds.
By default, the _blobs_ submodule is not downloaded. This git submodule may be
required for other builds for microcode or other binaries. To enable downloading
this submodule, select the option "Allow use of binary-only repository" in the
"General Setup" menu of Kconfig
This attempts to build the coreboot rom. The rom file itself ends up in the
build directory as 'coreboot.rom'. At the end of the build process, the build
displayed the contents of the rom file.
You may notice that a number of other pieces are downloaded at the
beginning of the build process. These are the git submodules used in
various coreboot builds. By default, the _blobs_ submodule is not
downloaded. This git submodule may be required for other builds for
microcode or other binaries. To enable downloading this submodule,
select the option "Allow use of binary-only repository" in the "General
Setup" menu of Kconfig This attempts to build the coreboot rom. The rom
file itself ends up in the build directory as 'coreboot.rom'. At the end
of the build process, the build displayed the contents of the rom file.
### Step 7 summary - Install QEMU
@ -253,11 +261,11 @@ process in a virtualised environment.
Here's the command line instruction broken down:
* `qemu-system-x86_64`
This starts the QEMU emulator with the i440FX host PCI bridge and PIIX3 PCI to
ISA bridge.
This starts the QEMU emulator with the i440FX host PCI bridge and PIIX3
PCI to ISA bridge.
* `-bios build/coreboot.rom`
Use the coreboot rom image that we just built. If this flag is left out, the
standard SeaBIOS image that comes with QEMU is used.
Use the coreboot rom image that we just built. If this flag is left out,
the standard SeaBIOS image that comes with QEMU is used.
* `-serial stdio`
Send the serial output to the console. This allows you to view the coreboot
boot log.
Send the serial output to the console. This allows you to view the
coreboot boot log.

View File

@ -4,28 +4,29 @@
If you already have an account, skip to Step 2.
Otherwise, go to <https://review.coreboot.org> in your preferred web browser.
Select **Sign in** in the upper right corner.
Otherwise, go to <https://review.coreboot.org> in your preferred web
browser. Select **Sign in** in the upper right corner.
Select the appropriate sign-in. For example, if you have a Google account,
select **Google OAuth2** (gerrit-oauth-provider plugin). **Note:** Your
username for the account will be the username of the account you used to
sign-in with. (ex. your Google username).
Select the appropriate sign-in. For example, if you have a Google
account, select **Google OAuth2** (gerrit-oauth-provider plugin).
**Note:** Your username for the account will be the username of the
account you used to sign-in with. (ex. your Google username).
## Step 2a: Set up SSH keys
If you prefer to use an HTTP password instead, skip to Step 2b.
If you do not have an SSH key set up on your account already (as is the case
with a newly created account), follow the instructions below; otherwise,
doing so could overwrite an existing key.
If you do not have an SSH key set up on your account already (as is the
case with a newly created account), follow the instructions below;
otherwise, doing so could overwrite an existing key.
In a terminal, run `ssh-keygen -t ed25519` and confirm the default path
`.ssh/id_ed25519`.
Make a passphrase -- remember this phrase. It will be needed whenever you use
this public key. **Note:** You might want to use a short password, or
forego the password altogether as you will be using it very often.
Make a passphrase -- remember this phrase. It will be needed whenever
you use this public key. **Note:** You might want to use a short
password, or forego the password altogether as you will be using it very
often.
Copy the content of `.ssh/id_ed25519.pub` (notice the ".pub" suffix
as you need to send the public key) into the textbox "New SSH Key" at
@ -33,17 +34,19 @@ https://review.coreboot.org/settings/#SSHKeys and save it.
## Step 2b: Set up an HTTP Password
Alternatively, instead of using SSH keys, you can use an HTTP password. To do so,
after you select your name and click on **Settings** on the left-hand side, rather
than selecting **SSH Public Keys**, select **HTTP Password**.
Alternatively, instead of using SSH keys, you can use an HTTP password.
To do so, after you select your name and click on **Settings** on the
left-hand side, rather than selecting **SSH Public Keys**, select **HTTP
Password**.
Click **Generate Password**. This should fill the "Password" box with a password. Copy
the password, and add the following to your `$HOME/.netrc` file:
Click **Generate Password**. This should fill the "Password" box with a
password. Copy the password, and add the following to your
`$HOME/.netrc` file:
machine review.coreboot.org login YourUserNameHere password YourPasswordHere
where YourUserNameHere is your username, and YourPasswordHere is the password you
just generated.
where YourUserNameHere is your username, and YourPasswordHere is the
password you just generated.
If your system is behind a snooping HTTPS proxy, you might also have to
make its SSL certificate known to curl, a system specific operation.
@ -55,26 +58,28 @@ certificate verification in git:
The `--global` argument sets it for all git transfers of your local
user, `false` means not to validate the certificate.
If that still doesn't allow you to pull or push changes to the server, the
proxy is likely tampering with the data stream, in which case there's nothing
we can do.
If that still doesn't allow you to pull or push changes to the server,
the proxy is likely tampering with the data stream, in which case
there's nothing we can do.
## Step 3: Clone coreboot and configure it for submitting patches
On Gerrit, click on the **Browse** tab in the upper left corner and select
**Repositories**. From the listing, select the "coreboot" repo. You may have
to click the next page arrow at the bottom a few times to find it.
On Gerrit, click on the **Browse** tab in the upper left corner and
select **Repositories**. From the listing, select the "coreboot" repo.
You may have to click the next page arrow at the bottom a few times to
find it.
If you are using SSH keys, select **ssh** from the tabs under "Project
coreboot" and run the "clone with commit-msg hook" command that's provided.
This should prompt you for your id_rsa passphrase, if you previously set one.
coreboot" and run the "clone with commit-msg hook" command that's
provided. This should prompt you for your id_rsa passphrase, if you
previously set one.
**Note:** if the **ssh** option is not showing, check that you have a username
set. Click the profile picture at the top right and select **User Settings**,
then set your username in the **Profile** section.
**Note:** if the **ssh** option is not showing, check that you have a
username set. Click the profile picture at the top right and select
**User Settings**, then set your username in the **Profile** section.
If you are using HTTP, instead, select **http** from the tabs under "Project coreboot"
and run the command that appears.
If you are using HTTP, instead, select **http** from the tabs under
"Project coreboot" and run the command that appears.
Now is a good time to configure your global git identity, if you haven't
already.
@ -82,30 +87,32 @@ already.
git config --global user.name "Your Name"
git config --global user.email "Your Email"
Finally, enter the local git repository and set up repository specific hooks
and other configurations.
Finally, enter the local git repository and set up repository specific
hooks and other configurations.
cd coreboot
make gitconfig
## Step 4: Submit a commit
An easy first commit to make is fixing existing checkpatch errors and warnings
in the source files. To see errors that are already present, build the files in
the repository by running `make lint` in the coreboot directory. Alternatively,
if you want to run `make lint` on a specific directory, run:
An easy first commit to make is fixing existing checkpatch errors and
warnings in the source files. To see errors that are already present,
build the files in the repository by running `make lint` in the coreboot
directory. Alternatively, if you want to run `make lint` on a specific
directory, run:
util/lint/lint-007-checkpatch <filepath>
where `filepath` is the filepath of the directory (ex. `src/cpu/amd/car`).
where `filepath` is the filepath of the directory (ex.
`src/cpu/amd/car`).
Any changes made to files under the src directory are made locally,
and can be submitted for review.
Once you finish making your desired changes, use the command line to stage
and submit your changes. An alternative and potentially easier way to stage
and submit commits is to use git cola, a graphical user interface for git. For
instructions on how to do so, skip to Step 4b.
Once you finish making your desired changes, use the command line to
stage and submit your changes. An alternative and potentially easier way
to stage and submit commits is to use git cola, a graphical user
interface for git. For instructions on how to do so, skip to Step 4b.
## Step 4a: Use the command line to stage and submit a commit
@ -119,20 +126,21 @@ To commit the change, run
git commit -s
**Note:** The -s adds a signed-off-by line by the committer. Your commit should be
signed off with your name and email (i.e. **Your Name** **\<Your Email\>**, based on
what you set with git config earlier).
**Note:** The -s adds a signed-off-by line by the committer. Your commit
should be signed off with your name and email (i.e. **Your Name**
**\<Your Email\>**, based on what you set with git config earlier).
Running git commit first checks for any errors and warnings using lint. If
there are any, you must go back and fix them before submitting your commit.
You can do so by making the necessary changes, and then staging your commit again.
Running git commit first checks for any errors and warnings using lint.
If there are any, you must go back and fix them before submitting your
commit. You can do so by making the necessary changes, and then staging
your commit again.
When there are no errors or warnings, your default text editor will open.
This is where you will write your commit message.
When there are no errors or warnings, your default text editor will
open. This is where you will write your commit message.
The first line of your commit message is your commit summary. This is a brief
one-line description of what you changed in the files using the template
below:
The first line of your commit message is your commit summary. This is a
brief one-line description of what you changed in the files using the
template below:
<filepath>: Short description
@ -143,29 +151,30 @@ For example,
**Note:** It is good practice to use present tense in your descriptions
and do not punctuate your summary.
Then hit Enter. The next paragraph should be a more in-depth explanation of the
changes you've made to the files. Again, it is good practice to use present
tense. Ex.
Then hit Enter. The next paragraph should be a more in-depth explanation
of the changes you've made to the files. Again, it is good practice to
use present tense. Ex.
Fix space prohibited between function name and open parenthesis,
line over 80 characters, unnecessary braces for single statement blocks,
space required before open brace errors and warnings.
line over 80 characters, unnecessary braces for single statement
blocks, space required before open brace errors and warnings.
When you have finished writing your commit message, save and exit the text
editor. You have finished committing your change. If, after submitting your
commit, you wish to make changes to it, running `git commit --amend` allows
you to take back your commit and amend it.
When you have finished writing your commit message, save and exit the
text editor. You have finished committing your change. If, after
submitting your commit, you wish to make changes to it, running `git
commit --amend` allows you to take back your commit and amend it.
When you are done with your commit, run `git push` to push your commit to
coreboot.org. **Note:** To submit as a private patch, use
`git push origin HEAD:refs/for/master%private`. Submitting as a private patch
means that your commit will be on review.coreboot.org, but is only visible to
yourself and those you add as reviewers. This mode isn't perfect: Somebody who
knows the commit ID can still fetch the change and everything it refers (e.g.
parent commits).
When you are done with your commit, run `git push` to push your commit
to coreboot.org. **Note:** To submit as a private patch, use `git push
origin HEAD:refs/for/master%private`. Submitting as a private patch
means that your commit will be on review.coreboot.org, but is only
visible to yourself and those you add as reviewers. This mode isn't
perfect: Somebody who knows the commit ID can still fetch the change and
everything it refers (e.g. parent commits).
This has been a quick primer on how to submit a change to Gerrit for review
using git. You may wish to review the [Gerrit code review workflow
This has been a quick primer on how to submit a change to Gerrit for
review using git. You may wish to review the [Gerrit code review
workflow
documentation](https://gerrit-review.googlesource.com/Documentation/intro-user.html#code-review),
especially if you plan to work on multiple changes at the same time.
@ -196,14 +205,14 @@ in-depth explanation of the changes you've made to the files. Again, it
is good practice to use present tense. Ex.
Fix space prohibited between function name and open parenthesis,
line over 80 characters, unnecessary braces for single statement blocks,
space required before open brace errors and warnings.
line over 80 characters, unnecessary braces for single statement
blocks, space required before open brace errors and warnings.
Then press Enter two times to move the cursor to below your description.
To the left of the text boxes, there is an icon with an downward arrow.
Press the arrow and select "Sign Off." Make sure that you are signing off
with your name and email (i.e. **Your Name** **\<Your Email\>**, based on what
you set with git config earlier).
Press the arrow and select "Sign Off." Make sure that you are signing
off with your name and email (i.e. **Your Name** **\<Your Email\>**,
based on what you set with git config earlier).
Now, review each of your changes and mark either individual changes or
an entire file as Ready to Commit by marking it as 'Staged'. To do
@ -234,11 +243,11 @@ and the commit succeeds, move to the command line and run `git push`.
Your commits can now be seen on review.coreboot.org if you select "Your"
and click on "Changes" and can be reviewed by others. Your code will
first be reviewed by build bot (Jenkins), which will either give you a warning
or verify a successful build; if so, your commit will receive a +1. Other
users may also give your commit +1. For a commit to be merged, it needs
to receive a +2. **Note:** A +1 and a +1 does not make a +2. Only certain users
can give a +2.
first be reviewed by build bot (Jenkins), which will either give you a
warning or verify a successful build; if so, your commit will receive a
+1. Other users may also give your commit +1. For a commit to be merged,
it needs to receive a +2. **Note:** A +1 and a +1 does not make a +2.
Only certain users can give a +2.
## Step 6 (optional): bash-git-prompt
@ -255,9 +264,11 @@ as this one is specific to bash.
Alternatively, follow the instructions below:
Run the following two commands in the command line:
cd
git clone https://github.com/magicmonty/bash-git-prompt.git .bash-git-prompt --depth=1
```Bash
cd
git clone https://github.com/magicmonty/bash-git-prompt.git \
.bash-git-prompt --depth=1
```
**Note:** cd will change your directory to your home directory, so the
git clone command will be run there.
@ -269,36 +280,38 @@ Finally, open the `~/.bashrc` file and append the following two lines:
Now, whenever you are in a git repository, it will continuously display
its state.
There also are additional configurations that you can change depending on your
preferences. If you wish to do so, look at the "All configs for .bashrc" section
on <https://github.com/magicmonty/bash-git-prompt>. Listed in that section are
various lines that you can copy, uncomment and add to your .bashrc file to
change the configurations. Example configurations include avoid fetching remote
status, and supporting versions of Git older than 1.7.10.
There also are additional configurations that you can change depending
on your preferences. If you wish to do so, look at the "All configs for
.bashrc" section on <https://github.com/magicmonty/bash-git-prompt>.
Listed in that section are various lines that you can copy, uncomment
and add to your .bashrc file to change the configurations. Example
configurations include avoid fetching remote status, and supporting
versions of Git older than 1.7.10.
## Appendix: Miscellaneous Advice
### Updating a commit after running git push:
Suppose you would like to update a commit that has already been pushed to the
remote repository. If the commit you wish to update is the most recent
commit you have made, after making your desired changes, stage the files
(either using git add or in git cola), and amend the commit. To do so,
if you are using the command line, run `git commit --amend`. If you are
using git cola, click on the gear icon located on the upper left side under
**Commit** and select **Amend Last Commit** in the drop down menu. Then, stage
the files you have changed, commit the changes, and run git push to push the
changes to the remote repository. Your change should be reflected in Gerrit as
a new patch set.
Suppose you would like to update a commit that has already been pushed
to the remote repository. If the commit you wish to update is the most
recent commit you have made, after making your desired changes, stage
the files (either using git add or in git cola), and amend the commit.
To do so, if you are using the command line, run `git commit --amend`.
If you are using git cola, click on the gear icon located on the upper
left side under **Commit** and select **Amend Last Commit** in the drop
down menu. Then, stage the files you have changed, commit the changes,
and run git push to push the changes to the remote repository. Your
change should be reflected in Gerrit as a new patch set.
If, however, the commit you wish to update is not the most recent commit you
have made, you will first need to checkout that commit. To do so, find the
URL of the commit on <https://review.coreboot.org> and go to that page; if
the commit is one that you previously pushed, it can be found by selecting
**My** and then **Changes** in the upper left corner. To checkout this commit,
in the upper right corner, click on **Download**, and copy the command listed
next to checkout by clicking **Copy to clipboard**. Then, run the copied
command in your coreboot repository. Now, the last commit should be the most
recent commit to that patch; to update it, make your desired changes, stage
the files, then amend and push the commit using the instructions in the above
If, however, the commit you wish to update is not the most recent commit
you have made, you will first need to checkout that commit. To do so,
find the URL of the commit on <https://review.coreboot.org> and go to
that page; if the commit is one that you previously pushed, it can be
found by selecting **My** and then **Changes** in the upper left corner.
To checkout this commit, in the upper right corner, click on
**Download**, and copy the command listed next to checkout by clicking
**Copy to clipboard**. Then, run the copied command in your coreboot
repository. Now, the last commit should be the most recent commit to
that patch; to update it, make your desired changes, stage the files,
then amend and push the commit using the instructions in the above
paragraph.

View File

@ -1,31 +1,32 @@
# Writing unit tests for coreboot
## Introduction
General thoughts about unit testing coreboot can be found in
[Unit testing coreboot](../technotes/2020-03-unit-testing-coreboot.md).
Additionally, [code coverage](../technotes/2021-05-code-coverage.md) support
is available for unit tests.
General thoughts about unit testing coreboot can be found in [Unit
testing coreboot](../technotes/2020-03-unit-testing-coreboot.md).
Additionally, [code coverage](../technotes/2021-05-code-coverage.md)
support is available for unit tests.
This document aims to guide developers through the process of adding and writing
unit tests for coreboot modules.
This document aims to guide developers through the process of adding and
writing unit tests for coreboot modules.
As an example of unit under test, `src/device/i2c.c` (referred hereafter as UUT
"Unit Under Test") will be used. This is simple module, thus it should be easy
for the reader to focus solely on the testing logic, without the need to spend
too much time on digging deeply into the source code details and flow of
operations. That being said, a good understanding of what the unit under test is
doing is crucial for writing unit tests.
As an example of unit under test, `src/device/i2c.c` (referred hereafter
as UUT "Unit Under Test") will be used. This is simple module, thus it
should be easy for the reader to focus solely on the testing logic,
without the need to spend too much time on digging deeply into the
source code details and flow of operations. That being said, a good
understanding of what the unit under test is doing is crucial for
writing unit tests.
This tutorial should also be helpful for developers who want to follow
[TDD](https://en.wikipedia.org/wiki/Test-driven_development). Even though TDD
has a different work flow of building tests first, followed by the code that
satisfies them, the process of writing tests and adding them to the tree is the
same.
[TDD](https://en.wikipedia.org/wiki/Test-driven_development). Even
though TDD has a different work flow of building tests first, followed
by the code that satisfies them, the process of writing tests and adding
them to the tree is the same.
## Analysis of unit under test
First of all, it is necessary to precisely establish what we want to test in a
particular module. Usually this will be an externally exposed API, which can be
used by other modules.
## Analysis of unit under test First of all, it is necessary to
precisely establish what we want to test in a particular module. Usually
this will be an externally exposed API, which can be used by other
modules.
```eval_rst
.. admonition:: i2c-test example
@ -34,66 +35,70 @@ used by other modules.
.. code-block:: c
int i2c_read_field(unsigned int bus, uint8_t chip, uint8_t reg, uint8_t *data,
uint8_t mask, uint8_t shift)
int i2c_write_field(unsigned int bus, uint8_t chip, uint8_t reg, uint8_t data,
uint8_t mask, uint8_t shift)
int i2c_read_field(unsigned int bus, uint8_t chip, uint8_t reg,
uint8_t *data, uint8_t mask, uint8_t shift)
int i2c_write_field(unsigned int bus, uint8_t chip, uint8_t reg,
uint8_t data, uint8_t mask, uint8_t shift)
For sake of simplicity, let's focus on `i2c_read_field` in this document.
For sake of simplicity, let's focus on `i2c_read_field` in this
document.
```
Once the API is defined, the next question is __what__ this API is doing (or
what it will be doing in case of TDD). In other words, what outputs we are
expecting from particular functions, when providing particular input parameters.
Once the API is defined, the next question is __what__ this API is doing
(or what it will be doing in case of TDD). In other words, what outputs
we are expecting from particular functions, when providing particular
input parameters.
```eval_rst
.. admonition:: i2c-test example
.. code-block:: c
int i2c_read_field(unsigned int bus, uint8_t chip, uint8_t reg, uint8_t *data,
uint8_t mask, uint8_t shift)
int i2c_read_field(unsigned int bus, uint8_t chip, uint8_t reg,
uint8_t *data, uint8_t mask, uint8_t shift)
This is a method which means to read content of register `reg` from i2c device
on i2c `bus` and slave address `chip`, applying bit `mask` and offset `shift`
to it. Returned data should be placed in `data`.
This is a method which means to read content of register `reg` from
i2c device on i2c `bus` and slave address `chip`, applying bit `mask`
and offset `shift` to it. Returned data should be placed in `data`.
```
The next step is to determine all external dependencies of UUT in order to mock
them out. Usually we want to isolate the UUT as much as possible, so that the
test result depends __only__ on the behavior of UUT and not on the other
modules. While some software dependencies may be hard to be mock (for example
due to complicated dependencies) and thus should be simply linked into the test
binaries, all hardware dependencies need to be mocked out, since in the
user-space host environment, targets hardware is not available.
The next step is to determine all external dependencies of UUT in order
to mock them out. Usually we want to isolate the UUT as much as
possible, so that the test result depends __only__ on the behavior of
UUT and not on the other modules. While some software dependencies may
be hard to be mock (for example due to complicated dependencies) and
thus should be simply linked into the test binaries, all hardware
dependencies need to be mocked out, since in the user-space host
environment, targets hardware is not available.
```eval_rst
.. admonition:: i2c-test example
`i2c_read_field` is calling `i2c_readb`, which eventually invokes
`i2c_transfer`. This method simply calls `platform_i2c_transfer`. The last
function in the chain is a hardware-touching one, and defined separately for
different SOCs. It is responsible for issuing transactions on the i2c bus.
For the purpose of writing unit test, we should mock this function.
`i2c_transfer`. This method simply calls `platform_i2c_transfer`. The
last function in the chain is a hardware-touching one, and defined
separately for different SOCs. It is responsible for issuing
transactions on the i2c bus. For the purpose of writing unit test,
we should mock this function.
```
## Adding new tests
In order to keep the tree clean, the `tests/` directory should mimic the `src/`
directory, so that test harness code is placed in a location corresponding to
UUT. Furthermore, the naming convention is to add the suffix `-test` to the UUT
name when creating a new test harness file.
In order to keep the tree clean, the `tests/` directory should mimic the
`src/` directory, so that test harness code is placed in a location
corresponding to UUT. Furthermore, the naming convention is to add the
suffix `-test` to the UUT name when creating a new test harness file.
```eval_rst
.. admonition:: i2c-test example
Considering that UUT is `src/device/i2c.c`, test file should be named
`tests/device/i2c-test.c`. When adding a new test file, it needs to be
registered with the coreboot unit testing infrastructure.
`tests/device/i2c-test.c`. When adding a new test file, it needs to
be registered with the coreboot unit testing infrastructure.
```
Every directory under `tests/` should contain a Makefile.inc, similar to what
can be seen under the `src/`. Register a new test in Makefile.inc, by
__appending__ test name to the `tests-y` variable.
Every directory under `tests/` should contain a Makefile.inc, similar to
what can be seen under the `src/`. Register a new test in Makefile.inc,
by __appending__ test name to the `tests-y` variable.
```eval_rst
.. admonition:: i2c-test example
@ -103,10 +108,11 @@ __appending__ test name to the `tests-y` variable.
tests-y += i2c-test
```
Next step is to list all source files, which should be linked together in order
to create test binary. Usually a tests requires only two files - UUT and test
harness code, but sometimes more is needed to provide the test environment.
Source files are registered in `<test_name>-srcs` variable.
Next step is to list all source files, which should be linked together
in order to create test binary. Usually a tests requires only two files
- UUT and test harness code, but sometimes more is needed to provide the
test environment. Source files are registered in `<test_name>-srcs`
variable.
```eval_rst
.. admonition:: i2c-test example
@ -117,9 +123,10 @@ Source files are registered in `<test_name>-srcs` variable.
i2c-test-srcs += src/device/i2c.c
```
Above minimal configuration is a basis for further work. One can try to build
and run test binary either by invoking `make tests/<test_dir>/<test_name>` or by
running all unit tests (whole suite) for coreboot `make unit-tests`.
Above minimal configuration is a basis for further work. One can try to
build and run test binary either by invoking `make
tests/<test_dir>/<test_name>` or by running all unit tests (whole suite)
for coreboot `make unit-tests`.
```eval_rst
.. admonition:: i2c-test example
@ -135,31 +142,34 @@ running all unit tests (whole suite) for coreboot `make unit-tests`.
make unit-tests
```
When trying to build test binary, one can often see linker complains about
`undefined reference` to couple of symbols. This is one of solutions to
determine all external dependencies of UUT - iteratively build test and resolve
errors one by one. At this step, developer should decide either it's better to
add an extra module to provide necessary definitions or rather mock such
dependency. Quick guide through adding mocks is provided later in this doc.
When trying to build test binary, one can often see linker complains
about `undefined reference` to couple of symbols. This is one of
solutions to determine all external dependencies of UUT - iteratively
build test and resolve errors one by one. At this step, developer should
decide either it's better to add an extra module to provide necessary
definitions or rather mock such dependency. Quick guide through adding
mocks is provided later in this doc.
## Writing new tests
In coreboot, [Cmocka](https://cmocka.org/) is used as unit test framework. The
project has exhaustive [API documentation](https://api.cmocka.org/). Let's see
how we may incorporate it when writing tests.
In coreboot, [Cmocka](https://cmocka.org/) is used as unit test
framework. The project has exhaustive [API
documentation](https://api.cmocka.org/). Let's see how we may
incorporate it when writing tests.
### Assertions
Testing the UUT consists of calling the functions in the UUT and comparing the
returned values to the expected values. Cmocka implements
[a set of assert macros](https://api.cmocka.org/group__cmocka__asserts.html) to
compare a value with an expected value. If the two values do not match, the test
Testing the UUT consists of calling the functions in the UUT and
comparing the returned values to the expected values. Cmocka implements
[a set of assert
macros](https://api.cmocka.org/group__cmocka__asserts.html) to compare a
value with an expected value. If the two values do not match, the test
fails with an error message.
```eval_rst
.. admonition:: i2c-test example
In our example, the simplest test is to call UUT for reading our fake devices
registers and do all calculation in the test harness itself. At the end, let's
compare integers with `assert_int_equal`.
In our example, the simplest test is to call UUT for reading our fake
devices registers and do all calculation in the test harness itself.
At the end, let's compare integers with `assert_int_equal`.
.. code-block:: c
@ -191,24 +201,25 @@ fails with an error message.
### Mocks
#### Overview
Many coreboot modules are low level software that touch hardware directly.
Because of this, one of the most important and challenging part of
writing tests is to design and implement mocks. A mock is a software component
which implements the API of another component so that the test can verify that
certain functions are called (or not called), verify the parameters passed to
those functions, and specify the return values from those functions. Mocks are
especially useful when the API to be implemented is one that accesses hardware
components.
Many coreboot modules are low level software that touch hardware
directly. Because of this, one of the most important and challenging
part of writing tests is to design and implement mocks. A mock is a
software component which implements the API of another component so that
the test can verify that certain functions are called (or not called),
verify the parameters passed to those functions, and specify the return
values from those functions. Mocks are especially useful when the API to
be implemented is one that accesses hardware components.
When writing a mock, the developer implements the same API as the module being
mocked. Such a mock may, for example, register a set of driver methods. Behind
this API, there is usually a simulation of real hardware.
When writing a mock, the developer implements the same API as the module
being mocked. Such a mock may, for example, register a set of driver
methods. Behind this API, there is usually a simulation of real
hardware.
```eval_rst
.. admonition:: i2c-test example
For purpose of our i2c test, we may introduce two i2c devices with set of
registers, which simply are structs in memory.
For purpose of our i2c test, we may introduce two i2c devices with
set of registers, which simply are structs in memory.
.. code-block:: c
@ -266,16 +277,17 @@ this API, there is usually a simulation of real hardware.
};
```
Cmocka uses a feature that gcc provides for breaking dependencies at the link
time. It is possible to override implementation of some function, with the
method from test harness. This allows test harness to take control of execution
from binary (during the execution of test), and stimulate UUT as required
without changing the source code.
Cmocka uses a feature that gcc provides for breaking dependencies at the
link time. It is possible to override implementation of some function,
with the method from test harness. This allows test harness to take
control of execution from binary (during the execution of test), and
stimulate UUT as required without changing the source code.
coreboot unit test infrastructure supports overriding of functions at link time.
This is as simple as adding a `name_of_function` to be mocked into
<test_name>-mocks variable in Makefile.inc. The result is that the test's
implementation of that function is called instead of coreboot's.
coreboot unit test infrastructure supports overriding of functions at
link time. This is as simple as adding a `name_of_function` to be
mocked into <test_name>-mocks variable in Makefile.inc. The result is
that the test's implementation of that function is called instead of
coreboot's.
```eval_rst
.. admonition:: i2c-test example
@ -284,44 +296,45 @@ implementation of that function is called instead of coreboot's.
i2c-test-mocks += platform_i2c_transfer
Now, dev can write own implementation of `platform_i2c_transfer`. This
implementation instead of accessing real i2c bus, will write/read from
fake structs.
Now, dev can write own implementation of `platform_i2c_transfer`.
This implementation instead of accessing real i2c bus, will
write/read from fake structs.
.. code-block:: c
int platform_i2c_transfer(unsigned int bus, struct i2c_msg *segments,
int count)
int platform_i2c_transfer(unsigned int bus, struct i2c_msg
*segments, int count)
{
}
```
#### Checking mock's arguments
A test can verify the parameters provided by the UUT to the mock function. The
developer may also verify that number of calls to mock is correct and the order
of calls to particular mocks is as expected (See
[this](https://api.cmocka.org/group__cmocka__call__order.html)). The Cmocka
macros for checking parameters are described
[here](https://api.cmocka.org/group__cmocka__param.html). In general, in mock
function, one makes a call to `check_expected(<param_name>)` and in the
corresponding test function, `expect*()` macro, with description which parameter
in which mock should have particular value, or be inside a described range.
A test can verify the parameters provided by the UUT to the mock
function. The developer may also verify that number of calls to mock is
correct and the order of calls to particular mocks is as expected (See
[this](https://api.cmocka.org/group__cmocka__call__order.html)). The
Cmocka macros for checking parameters are described
[here](https://api.cmocka.org/group__cmocka__param.html). In general, in
mock function, one makes a call to `check_expected(<param_name>)` and in
the corresponding test function, `expect*()` macro, with description
which parameter in which mock should have particular value, or be inside
a described range.
```eval_rst
.. admonition:: i2c-test example
In our example, we may want to check that `platform_i2c_transfer` is fed with
number of segments bigger than 0, each segment has flags which are in
supported range and each segment has buf which is non-NULL. We are expecting
such values for _every_ call, thus the last parameter in `expect*` macros is
-1.
In our example, we may want to check that `platform_i2c_transfer` is
fed with number of segments bigger than 0, each segment has flags
which are in supported range and each segment has buf which is
non-NULL. We are expecting such values for _every_ call, thus the
last parameter in `expect*` macros is -1.
.. code-block:: c
static void mock_expect_params_platform_i2c_transfer(void)
{
unsigned long int expected_flags[] = {0, I2C_M_RD, I2C_M_TEN,
I2C_M_RECV_LEN, I2C_M_NOSTART};
unsigned long int expected_flags[] = {0, I2C_M_RD,
I2C_M_TEN, I2C_M_RECV_LEN, I2C_M_NOSTART};
/* Flags should always be only within supported range */
expect_in_set_count(platform_i2c_transfer, segments->flags,
@ -330,8 +343,8 @@ in which mock should have particular value, or be inside a described range.
expect_not_value_count(platform_i2c_transfer, segments->buf,
NULL, -1);
expect_in_range_count(platform_i2c_transfer, count, 1, INT_MAX,
-1);
expect_in_range_count(platform_i2c_transfer, count, 1,
INT_MAX, -1);
}
And the checks below should be added to our mock
@ -347,11 +360,11 @@ in which mock should have particular value, or be inside a described range.
```
#### Instrument mocks
It is possible for the test function to instrument what the mock will return to
the UUT. This can be done by using the `will_return*()` and `mock()` macros.
These are described in
[the Mock Object section](https://api.cmocka.org/group__cmocka__mock.html) of
the Cmocka API documentation.
It is possible for the test function to instrument what the mock will
return to the UUT. This can be done by using the `will_return*()` and
`mock()` macros. These are described in [the Mock Object
section](https://api.cmocka.org/group__cmocka__mock.html) of the Cmocka
API documentation.
```eval_rst
.. admonition:: Example
@ -361,17 +374,18 @@ the Cmocka API documentation.
```
### Test runner
Finally, the developer needs to implement the test `main()` function. All tests
should be registered there and cmocka test runner invoked. All methods for
invoking Cmocka test are described
Finally, the developer needs to implement the test `main()` function.
All tests should be registered there and cmocka test runner invoked. All
methods for invoking Cmocka test are described
[here](https://api.cmocka.org/group__cmocka__exec.html).
```eval_rst
.. admonition:: i2c-test example
We don't need any extra setup and teardown functions for i2c-test, so let's
simply register test for `i2c_read_field` and return from main value which is
output of Cmocka's runner (it returns number of tests that failed).
We don't need any extra setup and teardown functions for i2c-test, so
let's simply register test for `i2c_read_field` and return from main
value which is output of Cmocka's runner (it returns number of tests
that failed).
.. code-block:: c