hv: SMBIOS passthrough for prelaunched VM

This patch passthrough native SMBIOS information to prelaunched VM.
Currently this is enabled by default, the config-tool switch will be
added later.

Tracked-On: #6320
Signed-off-by: Yifan Liu <yifan1.liu@intel.com>
This commit is contained in:
Yifan Liu 2021-07-07 09:09:21 +08:00 committed by Xie, Nanlin
parent 796f11a064
commit 6030211122
6 changed files with 247 additions and 0 deletions

View File

@ -292,6 +292,8 @@ VP_BASE_C_SRCS += arch/x86/configs/pci_dev.c
VP_BASE_C_SRCS += arch/x86/configs/vacpi.c
VP_BASE_C_SRCS += quirks/acrn_vm_fixup.c
VP_BASE_C_SRCS += quirks/smbios.c
# virtual platform device model
VP_DM_C_SRCS += dm/vpic.c
VP_DM_C_SRCS += dm/vrtc.c

View File

@ -40,6 +40,7 @@
#include <asm/irq.h>
#include <uart16550.h>
#include <quirks/fixup.h>
#include <quirks/smbios.h>
/* Local variables */
@ -149,6 +150,16 @@ bool is_nvmx_configured(const struct acrn_vm *vm)
return ((vm_config->guest_flags & GUEST_FLAG_NVMX_ENABLED) != 0U);
}
/**
* @pre vm != NULL && vm_config != NULL && vm->vmid < CONFIG_MAX_VM_NUM
*/
bool is_smbios_pt_configured(__unused const struct acrn_vm *vm)
{
struct acrn_vm_config *vm_config = get_vm_config(vm->vm_id);
return ((vm_config->guest_flags & GUEST_FLAG_SMBIOS_PASSTHROUGH) != 0U);
}
/**
* @brief VT-d PI posted mode can possibly be used for PTDEVs assigned
* to this VM if platform supports VT-d PI AND lapic passthru is not configured
@ -592,6 +603,8 @@ int32_t create_vm(uint16_t vm_id, uint64_t pcpu_bitmap, struct acrn_vm_config *v
deny_hv_owned_devices(vm);
}
try_smbios_passthrough(vm, get_acrn_boot_info());
init_vpci(vm);
enable_iommu();

View File

@ -258,6 +258,7 @@ void vrtc_init(struct acrn_vm *vm);
bool is_lapic_pt_configured(const struct acrn_vm *vm);
bool is_rt_vm(const struct acrn_vm *vm);
bool is_nvmx_configured(const struct acrn_vm *vm);
bool is_smbios_pt_configured(const struct acrn_vm *vm);
bool is_pi_capable(const struct acrn_vm *vm);
bool has_rt_vm(void);
struct acrn_vm *get_highest_severity_vm(bool runtime);

View File

@ -53,6 +53,7 @@
#define GUEST_FLAG_HIDE_MTRR (1UL << 3U) /* Whether hide MTRR from VM */
#define GUEST_FLAG_RT (1UL << 4U) /* Whether the vm is RT-VM */
#define GUEST_FLAG_NVMX_ENABLED (1UL << 5U) /* Whether this VM supports nested virtualization */
#define GUEST_FLAG_SMBIOS_PASSTHROUGH (1UL << 6U) /* Whether to passthrough native SMBIOS information to this VM */
/* TODO: We may need to get this addr from guest ACPI instead of hardcode here */
#define VIRTUAL_SLEEP_CTL_ADDR 0x400U /* Pre-launched VM uses ACPI reduced HW mode and sleep control register */

218
hypervisor/quirks/smbios.c Normal file
View File

@ -0,0 +1,218 @@
/*
* Copyright (C) 2021 Intel Corporation.
*
* SPDX-License-Identifier: BSD-3-Clause
*/
#include <types.h>
#include <util.h>
#include <asm/guest/vm.h>
#include <asm/mmu.h>
#include <asm/pgtable.h>
#include <asm/guest/ept.h>
#include <acrn_common.h>
#include <debug/logmsg.h>
#define SMBIOS_TABLE_GUID {0xeb9d2d31, 0x2d88, 0x11d3, {0x9a,0x16,0x00,0x90,0x27,0x3f,0xc1,0x4d}}
#define SMBIOS3_TABLE_GUID {0xf2fd1544, 0x9794, 0x4a2c, {0x99,0x2e,0xe5,0xbb,0xcf,0x20,0xe3,0x94}}
struct smbios_entry_point {
char anchor[4]; /* "_SM_" */
uint8_t checksum;
uint8_t length; /* length of this entry point structure, currently 1Fh */
uint8_t major_ver; /* version major */
uint8_t minor_ver; /* version minor */
uint16_t max_struct_size; /* size of the largest SMBIOS structure */
uint8_t ep_rev; /* entry point structure revision */
uint8_t formatted[5];
char int_anchor[5]; /* "_DMI_" */
uint8_t int_checksum; /* intermediate checksum */
uint16_t st_length; /* total length of SMBIOS structure table */
uint32_t st_addr; /* structure table address */
uint16_t nstructs; /* number of SMBIOS structures */
uint8_t bcd_revision; /* BCD revision */
} __attribute__((packed));
struct smbios3_entry_point {
char anchor[5]; /* "_SM3_" */
uint8_t checksum;
uint8_t length; /* length of this entry point structure, currently 18h */
uint8_t major_ver;
uint8_t minor_ver;
uint8_t docrev;
uint8_t ep_rev;
uint8_t reserved;
uint32_t max_st_size; /* max structure table size. The actual size is guaranteed to be <= this value. */
uint64_t st_addr; /* structure table address */
} __attribute__((packed));
typedef struct {
uint32_t Data1;
uint16_t Data2;
uint16_t Data3;
uint8_t Data4[8];
} EFI_GUID;
typedef struct _EFI_CONFIGURATION_TABLE {
EFI_GUID VendorGuid;
void *VendorTable;
} EFI_CONFIGURATION_TABLE;
typedef struct _EFI_TABLE_HEADER {
uint64_t Signature;
uint32_t Revision;
uint32_t HeaderSize;
uint32_t CRC32;
uint32_t Reserved;
} EFI_TABLE_HEADER;
typedef struct _EFI_SYSTEM_TABLE {
EFI_TABLE_HEADER Hdr;
uint16_t *FirmwareVendor;
uint32_t FirmwareRevision;
void *ConsoleInHandle;
void *ConIn;
void *ConsoleOutHandle;
void *ConOut;
void *StandardErrorHandle;
void *StdErr;
void *RuntimeServices;
void *BootServices;
uint64_t NumberOfTableEntries;
EFI_CONFIGURATION_TABLE *ConfigurationTable;
} EFI_SYSTEM_TABLE;
union {
struct smbios_entry_point eps;
struct smbios3_entry_point eps3;
} smbios_eps;
size_t smbios_eps_size;
void *smbios_table;
size_t smbios_table_size;
static int memcmp(uint8_t *s1, uint8_t *s2, size_t len)
{
while (len-- > 0) {
if (*s1++ != *s2++) {
return s1[-1] < s2[-1] ? -1 : 1;
}
}
return 0;
}
static void *detect_smbios_eps(EFI_SYSTEM_TABLE *efi_system_tab, EFI_GUID *target_guid)
{
uint64_t i;
void *smbios_eps = NULL;
/* search EFI system table -> configuration table */
for (i = 0; i < efi_system_tab->NumberOfTableEntries; i++) {
EFI_CONFIGURATION_TABLE *conf_tab = &efi_system_tab->ConfigurationTable[i];
EFI_GUID guid = conf_tab->VendorGuid;
if (!memcmp((uint8_t *)&guid, (uint8_t *)target_guid, sizeof(EFI_GUID))) {
smbios_eps = hpa2hva((uint64_t)conf_tab->VendorTable);
break;
}
}
/* We don't search for legacy 0xf0000~0xfffff region because UEFI environment is assumed. */
return smbios_eps;
}
static void smbios_table_probe(struct acrn_boot_info *abi)
{
void *p;
EFI_GUID smbios3_guid = SMBIOS3_TABLE_GUID;
EFI_GUID smbios_guid = SMBIOS_TABLE_GUID;
uint64_t efi_system_tab_paddr = (uint64_t)abi->efi_info.system_table;
EFI_SYSTEM_TABLE *efi_system_tab = (EFI_SYSTEM_TABLE *)hpa2hva(efi_system_tab_paddr);
stac();
/* If both are present, SMBIOS3 takes precedence over SMBIOS */
p = detect_smbios_eps(efi_system_tab, &smbios3_guid);
if (p) {
struct smbios3_entry_point *eps = (struct smbios3_entry_point *)p;
smbios_eps_size = eps->length;
memcpy_s(&smbios_eps, smbios_eps_size, eps, smbios_eps_size);
smbios_table = hpa2hva(eps->st_addr);
smbios_table_size = eps->max_st_size;
} else {
p = detect_smbios_eps(efi_system_tab, &smbios_guid);
if (p) {
struct smbios_entry_point *eps = (struct smbios_entry_point *)p;
smbios_eps_size = eps->length;
memcpy_s(&smbios_eps, smbios_eps_size, eps, smbios_eps_size);
smbios_table = hpa2hva(eps->st_addr);
smbios_table_size = eps->st_length;
}
}
clac();
}
/* Recalculate checksum of n bytes starting from byte_start, and write checksum to checksum_pos */
static void recalc_checksum(uint8_t *byte_start, uint8_t nbytes, uint8_t *checksum_pos)
{
int i;
uint8_t sum = 0;
for (i = 0; i < nbytes; i++) {
if (byte_start + i != checksum_pos) {
sum += byte_start[i];
}
}
*checksum_pos = -sum;
}
static int copy_smbios_to_guest(struct acrn_vm *vm)
{
uint64_t gpa;
int ret = -1;
if (smbios_eps_size) {
/* SMBIOS (32-bit entry point) requires the table to be below 4GB, whereas the
* SMBIOS3 (64-bit entry point) requires the table to be in anywhere in 64-bit
* memory address. So we put it below 4GB.
* Note that find_space_from_ve820 does not mark space as reserved as it gives out
* address ranges, so we need to check with other usages of this API (basically
* kernel and module loading).
*/
uint64_t klend = (uint64_t)vm->sw.kernel_info.kernel_load_addr + vm->sw.kernel_info.kernel_size + MEM_2M;
uint64_t mlend = max((uint64_t)vm->sw.ramdisk_info.load_addr + vm->sw.ramdisk_info.size, MEM_1M);
uint64_t high = max(klend, mlend);
gpa = high + 0x1000;
if (gpa != INVALID_GPA) {
ret = copy_to_gpa(vm, smbios_table, gpa, smbios_table_size);
if (ret == 0) {
if (smbios_eps.eps.anchor[3] == '_') {
/* SMBIOS (_SM_) */
struct smbios_entry_point *eps = &smbios_eps.eps;
eps->st_addr = (uint32_t)gpa;
/* the intermediate checksum covers the structure table field */
recalc_checksum((uint8_t *)eps->int_anchor, 0xf, &eps->int_checksum);
} else {
/* SMBIOS3 (_SM3_) */
struct smbios3_entry_point *eps3 = &smbios_eps.eps3;
eps3->st_addr = (uint32_t)gpa;
recalc_checksum((uint8_t *)eps3, eps3->length, &eps3->checksum);
}
/* The SMBIOS entry point structure should reside in between 0xf0000~0xfffff */
/* The rsdp starts from 0xf2400 so 0xf1000 should be OK. This structure is at most 31 bytes. */
gpa = 0xf1000UL;
ret = copy_to_gpa(vm, &smbios_eps, gpa, smbios_eps_size);
}
}
}
return ret;
}
void try_smbios_passthrough(struct acrn_vm *vm, struct acrn_boot_info *abi)
{
/* TODO: Add config and guest flag to disable it by default */
if (is_prelaunched_vm(vm)) {
smbios_table_probe(abi);
(void)copy_smbios_to_guest(vm);
}
}

View File

@ -0,0 +1,12 @@
/*
* Copyright (C) 2021 Intel Corporation. All rights reserved.
*
* SPDX-License-Identifier: BSD-3-Clause
*/
#ifndef QUIRKS_SMBIOS_H
#define QUIRKS_SMBIOS_H
void try_smbios_passthrough(struct acrn_vm *vm, struct acrn_boot_info *abi);
#endif