Skip to content

Commit

Permalink
igvmbld: Allow firmware to be built into IGVM file
Browse files Browse the repository at this point in the history
This commit adds parameters to the IGVM builder command line that allow
a firmware binary file to be specified along with either the firmware
base address or top address. The firmware is then populated into the
IGVM file at the specified address.

If the firmware contains metadata in OVMF format then this is parsed and
the relevant fields of the IGVM parameter block that is passed to the
guest.

This allows OVMF (and potentially other) firmware to be embedded in the
IGVM file and launched from the SVSM.

Signed-off-by: Roy Hopkins <roy.hopkins@suse.com>
  • Loading branch information
roy-hopkins committed Jan 4, 2024
1 parent 6d1e2d5 commit 658f023
Show file tree
Hide file tree
Showing 4 changed files with 378 additions and 4 deletions.
13 changes: 10 additions & 3 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,13 @@ KERNEL_ELF = "target/x86_64-unknown-none/${TARGET_PATH}/svsm"
TEST_KERNEL_ELF = target/x86_64-unknown-none/${TARGET_PATH}/svsm-test
FS_FILE ?= none

FW_FILE ?= none
ifneq ($(FW_FILE), none)
BUILD_FW = --firmware ${FW_FILE}
else
BUILD_FW =
endif

C_BIT_POS ?= 51

STAGE1_OBJS = stage1/stage1.o stage1/reset.o
Expand All @@ -29,12 +36,12 @@ all: stage1/kernel.elf svsm.bin igvm

igvm: $(IGVM_FILES)

$(IGVMBLD): igvmbld/igvmbld.c igvmbld/igvm_defs.h igvmbld/sev-snp.h
$(IGVMBLD): igvmbld/igvmbld.c igvmbld/ovmfmeta.c igvmbld/ovmfmeta.h igvmbld/igvm_defs.h igvmbld/sev-snp.h
mkdir -v -p bin
$(CC) -o $@ -O -Iigvmbld igvmbld/igvmbld.c
$(CC) -o $@ -O -Iigvmbld igvmbld/igvmbld.c igvmbld/ovmfmeta.c

bin/coconut-qemu.igvm: $(IGVMBLD) stage1/kernel.elf stage1/stage2.bin
$(IGVMBLD) --output $@ --stage2 stage1/stage2.bin --kernel stage1/kernel.elf --qemu
$(IGVMBLD) --output $@ --stage2 stage1/stage2.bin --kernel stage1/kernel.elf --qemu ${BUILD_FW}

bin/coconut-hyperv.igvm: $(IGVMBLD) stage1/kernel.elf stage1/stage2.bin
$(IGVMBLD) --output $@ --stage2 stage1/stage2.bin --kernel stage1/kernel.elf --hyperv --com-port 3
Expand Down
119 changes: 118 additions & 1 deletion igvmbld/igvmbld.c
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@
#include <unistd.h>
#include "sev-snp.h"
#include "igvm_defs.h"
#include "ovmfmeta.h"

#define PAGE_SIZE 0x1000

Expand Down Expand Up @@ -67,9 +68,13 @@ const char *stage2_filename;
const char *kernel_filename;
const char *filesystem_filename;
const char *output_filename;
const char *fw_filename;
uint32_t fw_size;
uint32_t fw_base;
int is_qemu;
int is_hyperv;
int com_port = 1;
int is_verbose;

const uint16_t com_io_ports[] = { 0x3f8, 0x2f8, 0x3e8, 0x2e8 };

Expand Down Expand Up @@ -669,6 +674,50 @@ int generate_igvm_file(const char *filename)
return 0;
}

static int check_firmware_options()
{
if (is_qemu)
{
FILE *fp;

if (!fw_filename)
{
// Firmware image is optional.
return 0;
}

// Get the firmware file size so we can determine the top and bottom address
// range.
fp = fopen(fw_filename, "rb");
if (!fp)
{
fprintf(stderr, "Firmware file cannot be opened: \"%s\"\n", fw_filename);
return 1;

}
if (fseek(fp, 0, SEEK_END) != 0)
{
fprintf(stderr, "Failed to read firmware file: \"%s\"\n", fw_filename);
fclose(fp);
return 1;
}
fw_size = ftell(fp);
fclose(fp);

// OVMF firmware must be aligned with the top at 4GB.
fw_base = 0xffffffff - fw_size + 1;
}
else
{
if (fw_filename)
{
fprintf(stderr, "The --firmware parameter is not currently supported for Hyper-V\n");
return 1;
}
}
return 0;
}

int parse_options(int argc, const char *argv[])
{
while (argc != 0)
Expand Down Expand Up @@ -766,6 +815,27 @@ int parse_options(int argc, const char *argv[])
{
is_hyperv = 1;
}
else if ((0 == strcmp(argv[0], "--verbose")) ||
(0 == strcmp(argv[0], "-v")))
{
is_verbose = 1;
}
else if (0 == strcmp(argv[0], "--firmware"))
{
if (fw_filename != NULL)
{
fprintf(stderr, "--firmware specified more than once\n");
return 1;
}
if (argc == 1)
{
fprintf(stderr, "missing argument for --firmware\n");
return 1;
}
fw_filename = argv[1];
argc -= 1;
argv += 1;
}
else
{
fprintf(stderr, "unknown option %s\n", argv[0]);
Expand Down Expand Up @@ -796,11 +866,36 @@ int parse_options(int argc, const char *argv[])
{
fprintf(stderr, "exactly one of --qemu and --hyperv must be specified\n");
return 1;
}
}

if (check_firmware_options() != 0)
{
return 1;
}

return 0;
}

static void print_fw_metadata(IgvmParamBlock *igvm_parameter_block)
{
uint32_t i;

printf("Firmware configuration\n======================\n");
printf(" start: %X\n", igvm_parameter_block->firmware.start);
printf(" size: %X\n", igvm_parameter_block->firmware.size);
printf(" metadata: %X\n", igvm_parameter_block->firmware.metadata);
printf(" secrets_page: %X\n", igvm_parameter_block->firmware.secrets_page);
printf(" caa_page: %X\n", igvm_parameter_block->firmware.caa_page);
printf(" cpuid_page: %X\n", igvm_parameter_block->firmware.cpuid_page);
printf(" reset_addr: %X\n", igvm_parameter_block->firmware.reset_addr);
printf(" prevalidated_count: %X\n", igvm_parameter_block->firmware.prevalidated_count);
for (i = 0; i < igvm_parameter_block->firmware.prevalidated_count; ++i)
{
printf(" prevalidated[%d].base: %X\n", i, igvm_parameter_block->firmware.prevalidated[i].base);
printf(" prevalidated[%d].len: %X\n", i, igvm_parameter_block->firmware.prevalidated[i].len);
}
}

int main(int argc, const char *argv[])
{
uint32_t address;
Expand Down Expand Up @@ -965,6 +1060,28 @@ int main(int argc, const char *argv[])
address += PAGE_SIZE;
}

// If a firmware file has been specified then add it and set the relevant
// parameter block entries.
if (fw_filename)
{
igvm_parameter_block->firmware.size = fw_size;
igvm_parameter_block->firmware.start = fw_base;
if (!construct_file_data_object(fw_filename, fw_base))
{
return 1;
}

// If the firmware file is an OVMF binary then we can extract the OVMF
// metadata from it. If the firmware is not OVMF then this function has
// no effect.
parse_ovmf_metadata(fw_filename, igvm_parameter_block);

if (is_verbose)
{
print_fw_metadata(igvm_parameter_block);
}
}

// Generate a header to describe the memory that will be used as the
// SVSM range. This tells the loader that this GPA range must be populated
// or else the image will not run.
Expand Down
Loading

0 comments on commit 658f023

Please sign in to comment.