Booting the BIOS
After building our BIOS, we want to make sure we can boot it normally before we add our fuzzing harness. This time, we'll add our harness to the boot flow, before any UEFI shell, so it is prudent to make sure everything looks OK first.
Before this step, you'll need to have the TSFFS SIMICS package installed in your system
by following the setup steps or by installing a prebuilt ispm
package. You'll also need the SIMICS base package (1000), the QSP-x86 package (2096),
and the QSP-CPU (8112) package. All three are available in the public simics release.
You can check that you have the package installed by running:
ispm packages --list-installed
You should see (at least, but likely more packages):
Installed Base Packages
Package Number Name Version Installed Paths
1000 Simics-Base 6.0.169 /home/rhart/simics/simics-6.0.169
Installed Addon Packages
Package Number Name Version Installed Paths
2096 QSP-x86 6.0.70 /home/rhart/simics/simics-qsp-x86-6.0.70
8112 QSP-CPU 6.0.17 /home/rhart/simics/simics-qsp-cpu-6.0.17
31337 TSFFS 6.0.1 /home/rhart/simics/simics-tsffs-6.0.1
in the list!
Create the Project
We already created the project
directory when we built our image, but we need to go
ahead and initialize it and add the packages we need with ispm
.
ispm projects project --create 1000-latest 2096-latest 8112-latest 31337-latest \
--ignore-existing-files
We won't be using any custom UEFI applications, so we can skip the boot disk we used in other tutorials. We will, however, need to customize our boot script slightly.
In the previous tutorials, we used the QSP-x86 package provided qsp-x86/uefi-shell
target to boot directly to the UEFI shell without any extra steps. That target uses
a script to choose the boot device for us, but because the included BIOS is both
different from the one we're using and boots in release mode without debug output, we
need to modify it somewhat to work with our custom BIOS.
Add SIMICS Targets
A SIMICS "target" is a YAML file which declares configuration options that are ultimately passed to a script. It provides an easy way to configure and override options without digging through scripts to find the right configuration options. You can read more about targets here.
We'll create a new target in project/targets/qsp-x86/qsp-uefi-custom.target.yml
:
%YAML 1.2
---
description: QSP booting to EFI shell, defaults to empty disks
params:
machine:
system_info:
type: str
description: A short string describing what this system is.
default: "QSP x86 - UEFI Shell"
hardware:
import: "%simics%/targets/qsp-x86/hardware.yml"
defaults:
name: qsp
rtc:
time: auto
usb_tablet:
create: true
firmware:
bios: ^machine:software:firmware:bios
lan_bios:
spi_flash: ^machine:software:firmware:spi_flash
uefi_device:
advanced: 2
name:
type: str
default: simics_uefi
description: |
Name of a simics-uefi device added under the top component.
video_mode:
type: int
default: 5
description: |
Bochs GFX Mode to be set by UEFI BIOS during boot before OS handover.
software:
firmware:
description: Firmware images
advanced: 2
bios:
type: file
description: BIOS file.
default: "%simics%/workspace/Build/SimicsOpenBoardPkg/BoardX58Ich10/DEBUG_GCC/FV/BOARDX58ICH10.fd"
lan_bios:
type: file
required: false
description: ROM BIOS file for the ICH10 LAN Ethernet adaptor
spi_flash:
type: file
default: "%simics%/targets/qsp-x86/images/spi-flash.bin"
description: The ICH10 SPI flash file to use.
script_delay:
type: int
default: 1
description: Script delay multiplier during UEFI boot
network:
switch:
import: "%simics%/targets/common/ethernet-setup.yml"
service_node:
import: "%simics%/targets/common/sn-setup.yml"
defaults:
ethernet_switch: ^network:switch:ethernet_switch:name
output:
system:
type: str
output: yes
default: ^machine:hardware:output:system
script: "%script%/qsp-uefi-custom.target.yml.include"
...
This target is copied more or less wholesale from the uefi-shell.target.yml
file in
your SIMICS QSP-x86 installation, but is modified to use a different default BIOS file,
a different .include
script, and uses a different path to import the top level
hardware.yml
script.
We also need to provide a custom .include
script, which is (as the name may suggest)
included by the target and run on startup to configure the system. Most of this script
is also copied from the uefi-shell.target.yml.include
script with the exception of the
final script-branch
. This script-branch
enters the BIOS boot menu and selects the
UEFI shell from it after waiting for a print message that indicates the boot menu is
visible.
run-script "%simics%/targets/qsp-x86/hardware.yml" namespace = machine:hardware
local $system = (params.get machine:hardware:output:system)
instantiate-components $system
# Add Simics UEFI meta-data device
if (params.get machine:uefi_device:name) {
@name = f"{simenv.system}.{params['machine:uefi_device:name']}"
@dev = SIM_create_object("simics-uefi", name, [])
@getattr(conf, simenv.system).mb.nb.pci_bus.devices.append([0, 7, dev])
@dev.video_mode = params['machine:uefi_device:video_mode']
}
## Name system
$system->system_info = (params.get machine:system_info)
## Set a time quantum that provides reasonable performance
set-time-quantum cell = $system.cell seconds = 0.0001
## Set up Ethernet
run-script "%simics%/targets/common/ethernet-setup.yml" namespace = network:switch
if (params.get network:switch:create_network) {
local $ethernet_switch = (params.get network:switch:ethernet_switch:name)
connect ($ethernet_switch.get-free-connector) (params.get machine:hardware:output:eth_slot)
instantiate-components (params.get network:switch:ethernet_switch:name)
}
run-script "%simics%/targets/common/sn-setup.yml" namespace = network:service_node
local $system = (params.get machine:hardware:output:system)
local $system = (params.get machine:hardware:output:system)
script-branch {
local $con = $system.serconsole.con
# NOTE: We have to modify this from the included target because
# the custom BIOS doesn't print the original message until the menu appears
bp.console_string.wait-for $con "End Load Options Dumping"
bp.time.wait-for seconds = 5.0
echo "Got load options dump"
echo "Opening EFI shell"
$con.input -e Esc
bp.time.wait-for seconds = 5.0
$con.input -e Down
$con.input -e Down
$con.input -e Enter
bp.time.wait-for seconds = 5.0
foreach $i in (range 6) {
$con.input -e Down
}
$con.input -e Enter
$con.input -e Enter
}
Save this file as project/targets/qsp-x86/qsp-uefi-custom.target.yml.include
.
Test Booting The BIOS
With our files all in place, we can create a tiny SIMICS script, and save it as
project/run.simics
:
load-target "qsp-x86/qsp-uefi-custom" namespace = qsp machine:hardware:firmware:bios = "%simics%/workspace/Build/SimicsOpenBoardPkg/BoardX58Ich10/DEBUG_GCC/FV/BOARDX58ICH10.fd"
script-branch {
local $con = qsp.serconsole.con
bp.console_string.wait-for $con "Shell>"
bp.time.wait-for seconds = .5
qsp.serconsole.con.input "help\n"
bp.time.wait-for seconds = .5
}
run
Then run the script:
./simics -no-gui --no-win ./run.simics
Somewhere in the output you should see:
<qsp.serconsole.con>Shell> help\r\n
<qsp.serconsole.con>alias - Displays, creates, or deletes UEFI Shell aliases.\r\n
<qsp.serconsole.con>attrib - Displays or modifies the attributes of files or directories.\r\n
<qsp.serconsole.con>bcfg - Manages the boot and driver options that are stored in NVRAM.\r\n
<qsp.serconsole.con>cd - Displays or changes the current directory.\r\n
If you do, all is well! Notice that there is quite a bit more output due to being a debug build of the BIOS.