Skip to content

Commit 1a8372e

Browse files
authored
Tutorial custom bios (#41)
1 parent 4c5c67e commit 1a8372e

File tree

25 files changed

+1383
-4
lines changed

25 files changed

+1383
-4
lines changed

docs/src/tutorials/README.md

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,4 +3,5 @@
33
We have several tutorials for harnessing and fuzzing various target software.
44

55
- [Fuzzing an EDK2 UEFI Application](edk2-uefi/README.md)
6-
- [Fuzzing a Kernel Module](kernel-module/README.md)
6+
- [Fuzzing a Kernel Module](kernel-module/README.md)
7+
- [Fuzzing a Custom BIOS](edk2-simics-platform-bios/README.md)
Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
# EDK2 SIMICS Platform BIOS Tutorial
2+
3+
This tutorial will walk you through the entire process of creating, building, and
4+
fuzzing a platform BIOS image. You can read about what exactly a platform BIOS FD image
5+
contains
6+
[here](https://tianocore-docs.github.io/edk2-BuildSpecification/release-1.28/10_post-build_imagegen_stage_-_flash/105_create_the_fd_image_files.html#105-create-the-fd-image-files).
7+
8+
- [Obtaining Sources](obtaining-sources.md)
9+
- [Building the BIOS](building-bios.md)
10+
- [Booting the BIOS](booting.md)
11+
- [Harnessing the BIOS Boot Logo Parser](harnessing.md)
12+
- [Configuring the Fuzzer](configuring.md)
13+
- [Running the Fuzzer](running.md)
14+
- [Optimizing the Fuzzer](optimizing.md)
Lines changed: 244 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,244 @@
1+
# Booting the BIOS
2+
3+
After building our BIOS, we want to make sure we can boot it normally before we
4+
add our fuzzing harness. This time, we'll add our harness to the boot flow, before any
5+
UEFI shell, so it is prudent to make sure everything looks OK first.
6+
7+
Before this step, you'll need to have the TSFFS SIMICS package installed in your system
8+
by following the [setup steps](../setup/README.md) or by installing a prebuilt `ispm`
9+
package. You'll also need the SIMICS base package (1000), the QSP-x86 package (2096),
10+
and the QSP-CPU (8112) package. All three are available in the public simics release.
11+
12+
You can check that you have the package installed by running:
13+
14+
```sh
15+
ispm packages --list-installed
16+
```
17+
18+
You should see (at least, but likely more packages):
19+
20+
```txt
21+
Installed Base Packages
22+
Package Number Name Version Installed Paths
23+
1000 Simics-Base 6.0.169 /home/rhart/simics/simics-6.0.169
24+
25+
Installed Addon Packages
26+
Package Number Name Version Installed Paths
27+
2096 QSP-x86 6.0.70 /home/rhart/simics/simics-qsp-x86-6.0.70
28+
8112 QSP-CPU 6.0.17 /home/rhart/simics/simics-qsp-cpu-6.0.17
29+
31337 TSFFS 6.0.1 /home/rhart/simics/simics-tsffs-6.0.1
30+
```
31+
32+
in the list!
33+
34+
## Create the Project
35+
36+
We already created the `project` directory when we built our image, but we need to go
37+
ahead and initialize it and add the packages we need with `ispm`.
38+
39+
```sh
40+
ispm projects project --create 1000-latest 2096-latest 8112-latest 31337-latest \
41+
--ignore-existing-files
42+
```
43+
44+
We won't be using any custom UEFI applications, so we can skip the boot disk we used in
45+
other tutorials. We will, however, need to customize our boot script slightly.
46+
47+
In the previous tutorials, we used the QSP-x86 package provided `qsp-x86/uefi-shell`
48+
target to boot directly to the UEFI shell without any extra steps. That target uses
49+
a script to choose the boot device for us, but because the included BIOS is both
50+
different from the one we're using and boots in release mode without debug output, we
51+
need to modify it somewhat to work with our custom BIOS.
52+
53+
## Add SIMICS Targets
54+
55+
A SIMICS "target" is a YAML file which declares configuration options that are
56+
ultimately passed to a script. It provides an easy way to configure and override options
57+
without digging through scripts to find the right configuration options. You can read
58+
more about targets
59+
[here](https://intel.github.io/tsffs/simics/simics-user-guide/targets.html).
60+
61+
We'll create a new target in `project/targets/qsp-x86/qsp-uefi-custom.target.yml`:
62+
63+
```yaml
64+
%YAML 1.2
65+
---
66+
description: QSP booting to EFI shell, defaults to empty disks
67+
params:
68+
machine:
69+
system_info:
70+
type: str
71+
description: A short string describing what this system is.
72+
default: "QSP x86 - UEFI Shell"
73+
hardware:
74+
import: "%simics%/targets/qsp-x86/hardware.yml"
75+
defaults:
76+
name: qsp
77+
rtc:
78+
time: auto
79+
usb_tablet:
80+
create: true
81+
firmware:
82+
bios: ^machine:software:firmware:bios
83+
lan_bios:
84+
spi_flash: ^machine:software:firmware:spi_flash
85+
uefi_device:
86+
advanced: 2
87+
name:
88+
type: str
89+
default: simics_uefi
90+
description: |
91+
Name of a simics-uefi device added under the top component.
92+
video_mode:
93+
type: int
94+
default: 5
95+
description: |
96+
Bochs GFX Mode to be set by UEFI BIOS during boot before OS handover.
97+
software:
98+
firmware:
99+
description: Firmware images
100+
advanced: 2
101+
bios:
102+
type: file
103+
description: BIOS file.
104+
default: "%simics%/workspace/Build/SimicsOpenBoardPkg/BoardX58Ich10/DEBUG_GCC/FV/BOARDX58ICH10.fd"
105+
lan_bios:
106+
type: file
107+
required: false
108+
description: ROM BIOS file for the ICH10 LAN Ethernet adaptor
109+
spi_flash:
110+
type: file
111+
default: "%simics%/targets/qsp-x86/images/spi-flash.bin"
112+
description: The ICH10 SPI flash file to use.
113+
script_delay:
114+
type: int
115+
default: 1
116+
description: Script delay multiplier during UEFI boot
117+
118+
network:
119+
switch:
120+
import: "%simics%/targets/common/ethernet-setup.yml"
121+
service_node:
122+
import: "%simics%/targets/common/sn-setup.yml"
123+
defaults:
124+
ethernet_switch: ^network:switch:ethernet_switch:name
125+
126+
output:
127+
system:
128+
type: str
129+
output: yes
130+
default: ^machine:hardware:output:system
131+
script: "%script%/qsp-uefi-custom.target.yml.include"
132+
...
133+
```
134+
135+
This target is copied more or less wholesale from the `uefi-shell.target.yml` file in
136+
your SIMICS QSP-x86 installation, but is modified to use a different default BIOS file,
137+
a different `.include` script, and uses a different path to import the top level
138+
`hardware.yml` script.
139+
140+
We also need to provide a custom `.include` script, which is (as the name may suggest)
141+
included by the target and run on startup to configure the system. Most of this script
142+
is also copied from the `uefi-shell.target.yml.include` script with the exception of the
143+
final `script-branch`. This `script-branch` enters the BIOS boot menu and selects the
144+
UEFI shell from it after waiting for a print message that indicates the boot menu is
145+
visible.
146+
147+
```simics
148+
run-script "%simics%/targets/qsp-x86/hardware.yml" namespace = machine:hardware
149+
150+
local $system = (params.get machine:hardware:output:system)
151+
152+
instantiate-components $system
153+
154+
# Add Simics UEFI meta-data device
155+
if (params.get machine:uefi_device:name) {
156+
@name = f"{simenv.system}.{params['machine:uefi_device:name']}"
157+
@dev = SIM_create_object("simics-uefi", name, [])
158+
@getattr(conf, simenv.system).mb.nb.pci_bus.devices.append([0, 7, dev])
159+
@dev.video_mode = params['machine:uefi_device:video_mode']
160+
}
161+
162+
## Name system
163+
$system->system_info = (params.get machine:system_info)
164+
165+
## Set a time quantum that provides reasonable performance
166+
set-time-quantum cell = $system.cell seconds = 0.0001
167+
168+
## Set up Ethernet
169+
run-script "%simics%/targets/common/ethernet-setup.yml" namespace = network:switch
170+
if (params.get network:switch:create_network) {
171+
local $ethernet_switch = (params.get network:switch:ethernet_switch:name)
172+
connect ($ethernet_switch.get-free-connector) (params.get machine:hardware:output:eth_slot)
173+
instantiate-components (params.get network:switch:ethernet_switch:name)
174+
}
175+
run-script "%simics%/targets/common/sn-setup.yml" namespace = network:service_node
176+
177+
local $system = (params.get machine:hardware:output:system)
178+
179+
local $system = (params.get machine:hardware:output:system)
180+
181+
script-branch {
182+
local $con = $system.serconsole.con
183+
# NOTE: We have to modify this from the included target because
184+
# the custom BIOS doesn't print the original message until the menu appears
185+
bp.console_string.wait-for $con "End Load Options Dumping"
186+
bp.time.wait-for seconds = 5.0
187+
echo "Got load options dump"
188+
echo "Opening EFI shell"
189+
$con.input -e Esc
190+
bp.time.wait-for seconds = 5.0
191+
192+
$con.input -e Down
193+
$con.input -e Down
194+
$con.input -e Enter
195+
bp.time.wait-for seconds = 5.0
196+
197+
foreach $i in (range 6) {
198+
$con.input -e Down
199+
}
200+
201+
$con.input -e Enter
202+
$con.input -e Enter
203+
}
204+
```
205+
206+
Save this file as `project/targets/qsp-x86/qsp-uefi-custom.target.yml.include`.
207+
208+
## Test Booting The BIOS
209+
210+
With our files all in place, we can create a tiny SIMICS script, and save it as
211+
`project/run.simics`:
212+
213+
```simics
214+
load-target "qsp-x86/qsp-uefi-custom" namespace = qsp machine:hardware:firmware:bios = "%simics%/workspace/Build/SimicsOpenBoardPkg/BoardX58Ich10/DEBUG_GCC/FV/BOARDX58ICH10.fd"
215+
216+
script-branch {
217+
local $con = qsp.serconsole.con
218+
bp.console_string.wait-for $con "Shell>"
219+
bp.time.wait-for seconds = .5
220+
qsp.serconsole.con.input "help\n"
221+
bp.time.wait-for seconds = .5
222+
}
223+
224+
run
225+
```
226+
227+
Then run the script:
228+
229+
```sh
230+
./simics -no-gui --no-win ./run.simics
231+
```
232+
233+
Somewhere in the output you should see:
234+
235+
```txt
236+
<qsp.serconsole.con>Shell> help\r\n
237+
<qsp.serconsole.con>alias - Displays, creates, or deletes UEFI Shell aliases.\r\n
238+
<qsp.serconsole.con>attrib - Displays or modifies the attributes of files or directories.\r\n
239+
<qsp.serconsole.con>bcfg - Manages the boot and driver options that are stored in NVRAM.\r\n
240+
<qsp.serconsole.con>cd - Displays or changes the current directory.\r\n
241+
```
242+
243+
If you do, all is well! Notice that there is quite a bit more output due to being a
244+
debug build of the BIOS.
Lines changed: 74 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,74 @@
1+
# Building the BIOS
2+
3+
Working from the same `Dockerfile` we obtained sources into, we'll set things up to
4+
build the BIOS image. First, we need to set up a few environment variables:
5+
6+
```dockerfile
7+
ENV EDK_TOOLS_PATH="${PROJECT}/edk2/BaseTools/"
8+
ENV PACKAGES_PATH="${PROJECT}/edk2:${PROJECT}/edk2-platforms:${PROJECT}/edk2-non-osi"
9+
ENV WORKSPACE="${PROJECT}"
10+
```
11+
12+
These variables are used by EDK2 to find its own sources and binaries.
13+
14+
Next, we'll build the *Base Tools*. You can read more about the *Base Tools* in the EDK2
15+
[build
16+
system](https://tianocore-docs.github.io/edk2-BuildSpecification/release-1.28/4_edk_ii_build_process_overview/41_edk_ii_build_system.html#41-edk-ii-build-system)
17+
documentation.
18+
19+
```dockerfile
20+
RUN source edk2/edksetup.sh && \
21+
make -C edk2/BaseTools/
22+
```
23+
24+
With the *Base Tools* built, we can build the BIOS. We directly follow the directions
25+
provided, and you can read more about the process, what settings are available for the
26+
BIOS (in particular, how to change the BIOS stages) [in the
27+
repo](https://github.com/tianocore/edk2-platforms/blob/f446fff05003f69a4396b2ec375301ecb5f63a2a/Platform/Intel/Readme.md).
28+
29+
First, we'll change into the tree containing the platform code for all the Intel
30+
platforms, then use the Intel-provided build script to select our board, toolchain,
31+
and debug mode (in this case, enabled).
32+
33+
```dockerfile
34+
WORKDIR "${PROJECT}/edk2-platforms/Platform/Intel"
35+
36+
# Build SimicsOpenBoardPkg
37+
RUN source "${PROJECT}/edk2/edksetup.sh" && \
38+
python build_bios.py -p BoardX58Ich10X64 -d -t GCC
39+
```
40+
41+
We'll use a build script to manage building the container and copying the relevant
42+
artifacts out of it. Place this script `build.sh` next to your `Dockerfile`.
43+
44+
```sh
45+
#!/bin/bash
46+
47+
SCRIPT_DIR=$(cd -- "$(dirname -- "${BASH_SOURCE[0]}")" &>/dev/null && pwd)
48+
IMAGE_NAME="edk2-simics-platform"
49+
DOCKERFILE="${SCRIPT_DIR}/Dockerfile"
50+
CONTAINER_UID=$(echo "${RANDOM}" | sha256sum | head -c 8)
51+
CONTAINER_NAME="${IMAGE_NAME}-tmp-${CONTAINER_UID}"
52+
53+
mkdir -p "${SCRIPT_DIR}/project/"
54+
docker build -t "${IMAGE_NAME}" -f "${DOCKERFILE}" --build-arg "PROJECT=${SCRIPT_DIR}/project/workspace/" "${SCRIPT_DIR}"
55+
docker create --name "${CONTAINER_NAME}" "${IMAGE_NAME}" bash
56+
rm -rf "${SCRIPT_DIR}/project/workspace/"
57+
docker cp "${CONTAINER_NAME}:${SCRIPT_DIR}/project/workspace/" "${SCRIPT_DIR}/project/workspace/"
58+
docker rm -f "${CONTAINER_NAME}"
59+
```
60+
61+
Now run the script:
62+
63+
```sh
64+
chmod +x build.sh
65+
./build.sh
66+
```
67+
68+
If all goes well, you'll have a directory
69+
`project/workspace/Build/SimicsOpenBoardPkg/BoardX58Ich10/DEBUG_GCC/FV` containing our
70+
BIOS image (`BOARDX58ICH10.fd`).
71+
72+
```sh
73+
ls project/workspace/Build/SimicsOpenBoardPkg/BoardX58Ich10/DEBUG_GCC/FV/BOARDX58ICH10.fd
74+
```

0 commit comments

Comments
 (0)