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.