Skip to content

Commit

Permalink
Support loading IGVM-based Hyper-V firmware into the SVSM IGVM file
Browse files Browse the repository at this point in the history
Signed-off-by: Jon Lange <jlange@microsoft.com>
  • Loading branch information
msft-jlange committed Jan 5, 2024
1 parent 6a3888e commit 606ed09
Show file tree
Hide file tree
Showing 6 changed files with 599 additions and 42 deletions.
4 changes: 2 additions & 2 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -36,9 +36,9 @@ all: stage1/kernel.elf svsm.bin igvm

igvm: $(IGVM_FILES)

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

bin/coconut-qemu.igvm: $(IGVMBLD) stage1/kernel.elf stage1/stage2.bin
$(IGVMBLD) --output $@ --stage2 stage1/stage2.bin --kernel stage1/kernel.elf --qemu ${BUILD_FW}
Expand Down
4 changes: 4 additions & 0 deletions igvmbld/igvm_defs.h
Original file line number Diff line number Diff line change
Expand Up @@ -7,13 +7,17 @@

typedef enum {
IGVM_VHT_SUPPORTED_PLATFORM = 0x1,
IGVM_VHT_SNP_POLICY = 0x101,
IGVM_VHT_PARAMETER_AREA = 0x301,
IGVM_VHT_PAGE_DATA = 0x302,
IGVM_VHT_PARAMETER_INSERT = 0x303,
IGVM_VHT_VP_CONTEXT = 0x304,
IGVM_VHT_REQUIRED_MEMORY = 0x305,
IGVM_VHT_VP_COUNT_PARMETER = 0x307,
IGVM_VHT_SRAT = 0x308,
IGVM_VHT_MADT = 0x309,
IGVM_VHT_MEMORY_MAP = 0x30C,
IGVM_VHT_COMMAND_LINE = 0x30E,
IGVM_VHT_ENVIRONMENT_INFO_PARAMETER = 0x313,
} IGVM_VHT;

Expand Down
105 changes: 65 additions & 40 deletions igvmbld/igvmbld.c
Original file line number Diff line number Diff line change
Expand Up @@ -25,34 +25,12 @@ typedef struct {
uint32_t reserved;
} Stage2Stack;

typedef enum {
parameter_page_general = 0,
parameter_page_memory_map,
} ParameterPageIndex;

typedef struct _data_obj {
struct _data_obj *next;
void *data;
uint64_t address;
uint32_t size;
uint16_t page_type;
uint16_t data_type;
IGVM_VHS_PAGE_DATA *page_data_headers;
} DATA_OBJ;

typedef struct _param_page {
struct _param_page *next;
uint32_t address;
ParameterPageIndex index;
} PARAM_PAGE;

typedef struct _igvm_vhs {
struct _igvm_vhs *next;
IGVM_VHT header_type;
uint32_t header_size;
void *data;
} IGVM_VHS;

const char *stage2_filename;
const char *kernel_filename;
const char *filesystem_filename;
Expand Down Expand Up @@ -144,6 +122,7 @@ DATA_OBJ *allocate_data_object(uint64_t address, uint32_t size, uint32_t data_si
data_object->data = NULL;
data_object->data_type = IGVM_VHT_PAGE_DATA;
data_object->page_type = IgvmPageType_Normal;
data_object->page_data_flags = 0;
return data_object;
}

Expand Down Expand Up @@ -508,6 +487,7 @@ void generate_data_headers(void)
{
page_data[i].GPA = address;
page_data[i].CompatibilityMask = 1;
page_data[i].Flags = data_obj->page_data_flags;
address += PAGE_SIZE;
}

Expand Down Expand Up @@ -708,14 +688,7 @@ static int check_firmware_options()
// 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;
}

Expand Down Expand Up @@ -902,13 +875,15 @@ int main(int argc, const char *argv[])
DATA_OBJ *cpuid_page;
int err;
DATA_OBJ *filesystem_data;
FirmwareIgvmInfo fw_info;
DATA_OBJ *igvm_parameter_object;
IgvmParamBlock *igvm_parameter_block;
DATA_OBJ *initial_stack;
DATA_OBJ *kernel_data;
IGVM_VHS_SUPPORTED_PLATFORM *platform;
DATA_OBJ *secrets_page;
DATA_OBJ *stage2_data;
uint32_t stage2_top;
Stage2Stack *stage2_stack;
uint64_t vmsa_address;
DATA_OBJ *vmsa_data;
Expand Down Expand Up @@ -948,25 +923,51 @@ int main(int argc, const char *argv[])
return 1;
}

address = (stage2_data->address + stage2_data->size + PAGE_SIZE - 1) &
~(PAGE_SIZE - 1);
if (address > 0x9F000)
stage2_top = (stage2_data->address + stage2_data->size + PAGE_SIZE - 1) &
~(PAGE_SIZE - 1);
if (stage2_top > 0x9F000)
{
fprintf(stderr, "stage 2 image is too large\n");
return 1;
}
else if (address < 0x9F000)
else if (stage2_top < 0x9F000)
{
construct_empty_data_object(address, 0x9F000 - address);
construct_empty_data_object(stage2_top, 0x9F000 - stage2_top);
}

cpuid_page = construct_mem_data_object(0x9F000, 0x1000);
cpuid_page->page_type = IgvmPageType_Cpuid;
fill_cpuid_page((SNP_CPUID_PAGE *)cpuid_page->data);

// Load the kernel data at a base address of 1 MB.
// Plan to load the kernel image at a base address of 1 MB unless it must
// be relocated due to firmware.
address = 1 << 20;

memset(&fw_info, 0, sizeof(FirmwareIgvmInfo));

// If a hyper-v firmware file was specified, then load it.
if (is_hyperv)
{
if (fw_filename != NULL)
{
if (is_hyperv)
{
err = read_hyperv_igvm_file(fw_filename, &fw_info);
if (err != 0)
{
return err;
}

address = fw_info.fw_info.start + fw_info.fw_info.size;
}
else
{
fprintf(stderr, "--firmware only supported for hyperv targets\n");
return 1;
}
}
}

// Construct a data object for the kernel.
kernel_data = construct_file_data_object(kernel_filename, address);
if (kernel_data == NULL)
Expand Down Expand Up @@ -1032,6 +1033,14 @@ int main(int argc, const char *argv[])
construct_parameter_page(address, parameter_page_memory_map);
address += PAGE_SIZE;

// If the firmware has supplied a guest context page, then assign it an address now.
if (fw_info.guest_context != NULL)
{
fw_info.guest_context->address = address;
igvm_parameter_block->guest_context_offset = address - (uint32_t)igvm_parameter_object->address;
address += fw_info.guest_context->size;
}

// Populate the rest of the parameter block.
igvm_parameter_block->param_area_size = address - (uint32_t)igvm_parameter_object->address;
igvm_parameter_block->cpuid_page = (uint32_t)cpuid_page->address;
Expand All @@ -1049,9 +1058,25 @@ int main(int argc, const char *argv[])
vmsa_address = igvm_parameter_block->kernel_base;
igvm_parameter_block->kernel_reserved_size = 0x1000;

// Set the shared GPA boundary at bit 46, below the lowest possible
// C-bit position.
igvm_parameter_block->vtom = 0x0000400000000000;
// Add additional information if firmware is being launched.
if (fw_info.fw_info.size != 0)
{
// Mark the range between the top of stage 2 and the base of
// memory as a range that needs to be validated.
fw_info.fw_info.prevalidated_count = 1;
fw_info.fw_info.prevalidated[0].base = stage2_top;
fw_info.fw_info.prevalidated[0].size = fw_info.fw_info.start - stage2_top;

igvm_parameter_block->firmware = fw_info.fw_info;
igvm_parameter_block->vtom = fw_info.vtom;
}
else
{
// Set the shared GPA boundary at bit 46, below the lowest possible
// C-bit position.
igvm_parameter_block->vtom = 0x0000400000000000;
}

platform->SharedGpaBoundary = igvm_parameter_block->vtom;
}
else
Expand All @@ -1068,7 +1093,7 @@ int main(int argc, const char *argv[])

// If a firmware file has been specified then add it and set the relevant
// parameter block entries.
if (fw_filename)
if (fw_filename && is_qemu)
{
igvm_parameter_block->firmware.size = fw_size;
igvm_parameter_block->firmware.start = fw_base;
Expand Down
41 changes: 41 additions & 0 deletions igvmbld/igvmbld.h
Original file line number Diff line number Diff line change
Expand Up @@ -50,4 +50,45 @@ typedef struct {
uint64_t vtom;
} IgvmParamBlock;

typedef enum {
parameter_page_general = 0,
parameter_page_memory_map,
num_parameter_pages,
} ParameterPageIndex;

typedef struct _igvm_vhs {
struct _igvm_vhs *next;
IGVM_VHT header_type;
uint32_t header_size;
void *data;
} IGVM_VHS;

typedef struct _data_obj {
struct _data_obj *next;
void *data;
uint64_t address;
uint32_t size;
uint16_t page_type;
uint16_t data_type;
uint32_t page_data_flags;
IGVM_VHS_PAGE_DATA *page_data_headers;
} DATA_OBJ;

typedef struct {
IgvmParamBlockFwInfo fw_info;
uint64_t vtom;
DATA_OBJ *guest_context;
} FirmwareIgvmInfo;

IGVM_VHS *allocate_var_headers(
IGVM_VHT header_type,
uint32_t struct_size,
uint32_t header_size,
int count);

DATA_OBJ *construct_empty_data_object(uint64_t address, uint32_t size);
DATA_OBJ *construct_mem_data_object(uint64_t address, uint32_t size);

int read_hyperv_igvm_file(const char *file_name, FirmwareIgvmInfo *fw_info);

int parse_ovmf_metadata(const char *ovmf_filename, IgvmParamBlock *params);
Loading

0 comments on commit 606ed09

Please sign in to comment.