diff --git a/hypervisor/Makefile b/hypervisor/Makefile index 068e20c22..dcc252729 100644 --- a/hypervisor/Makefile +++ b/hypervisor/Makefile @@ -162,6 +162,8 @@ INCLUDE_PATH += $(HV_OBJDIR)/include INCLUDE_PATH += $(BOARD_CFG_DIR) INCLUDE_PATH += $(SCENARIO_CFG_DIR) +override INCLUDE_PATH := $(realpath $(INCLUDE_PATH)) + CC ?= gcc AS ?= as AR ?= ar @@ -343,8 +345,6 @@ endif ifneq ($(FIRMWARE),uefi) CFLAGS += -DCONFIG_LAST_LEVEL_EPT_AT_BOOT endif -PRE_BUILD_SRCS += pre_build/static_checks.c -PRE_BUILD_OBJS := $(patsubst %.c,$(HV_OBJDIR)/%.o,$(PRE_BUILD_SRCS)) MODULES += $(LIB_MOD) MODULES += $(BOOT_MOD) @@ -412,8 +412,13 @@ install-debug: $(HV_OBJDIR)/$(HV_FILE).map $(HV_OBJDIR)/$(HV_FILE).out install -D $(HV_OBJDIR)/$(HV_FILE).out $(DESTDIR)$(libdir)/acrn/$(HV_FILE).$(BOARD).$(FIRMWARE).$(SCENARIO).out install -D $(HV_OBJDIR)/$(HV_FILE).map $(DESTDIR)$(libdir)/acrn/$(HV_FILE).$(BOARD).$(FIRMWARE).$(SCENARIO).map +PRE_BUILD_DIR := ../misc/hv_prebuild + .PHONY: pre_build -pre_build: $(PRE_BUILD_OBJS) +pre_build: $(HV_OBJDIR)/$(HV_CONFIG_H) + @echo "Start pre-build static check ..." + $(MAKE) -C $(PRE_BUILD_DIR) BOARD=$(BOARD) SCENARIO=$(SCENARIO) TARGET_DIR=$(realpath $(TARGET_DIR)) + @$(HV_OBJDIR)/hv_prebuild_check.out .PHONY: header header: $(VERSION) $(HV_OBJDIR)/$(HV_CONFIG_H) $(TARGET_ACPI_INFO_HEADER) @@ -492,19 +497,6 @@ distclean: PHONY: (VERSION) $(VERSION): $(HV_OBJDIR)/$(HV_CONFIG_H) - @if [ "$(SCENARIO)" = "" ]; then echo "Please specify SCENARIO for the build!"; exit 1; fi; - @if [ "$(BOARD)" = "" ]; then echo "Please specify BOARD for the build!"; exit 1; fi; - @echo "SCENARIO <$(SCENARIO)> for BOARD <$(BOARD)> is specified." - @if [ ! -d $(BOARD_CFG_DIR) ]; then \ - echo "Configurations for BOARD $(BOARD) is not found."; exit 1; \ - else \ - echo "Found BOARD configurations under $(BOARD_CFG_DIR)"; \ - fi; - @if [ ! -d $(SCENARIO_CFG_DIR) ]; then \ - echo "Configurations for SCENARIO $(SCENARIO) is not found."; exit 1; \ - else \ - echo "Found SCENARIO configurations under $(SCENARIO_CFG_DIR)"; \ - fi; touch $(VERSION) @COMMIT=`git rev-parse --verify --short HEAD 2>/dev/null`;\ DIRTY=`git diff-index --name-only HEAD`;\ diff --git a/hypervisor/arch/x86/configs/vm_config.c b/hypervisor/arch/x86/configs/vm_config.c index df313c8ad..2fd7fef76 100644 --- a/hypervisor/arch/x86/configs/vm_config.c +++ b/hypervisor/arch/x86/configs/vm_config.c @@ -23,7 +23,7 @@ uint8_t get_vm_severity(uint16_t vm_id) return vm_configs[vm_id].severity; } -static inline bool uuid_is_equal(const uint8_t *uuid1, const uint8_t *uuid2) +bool uuid_is_equal(const uint8_t *uuid1, const uint8_t *uuid2) { uint64_t uuid1_h = *(const uint64_t *)uuid1; uint64_t uuid1_l = *(const uint64_t *)(uuid1 + 8); diff --git a/misc/hv_prebuild/Makefile b/misc/hv_prebuild/Makefile new file mode 100644 index 000000000..3558750ba --- /dev/null +++ b/misc/hv_prebuild/Makefile @@ -0,0 +1,49 @@ +HV_OBJDIR ?= $(CURDIR)/build +HV_CONFIG_H := $(HV_OBJDIR)/include/config.h +HV_SRC_DIR := ../../hypervisor + +ifneq ($(HV_CONFIG_H), $(wildcard $(HV_CONFIG_H))) + $(error $(HV_CONFIG_H) does not exist) +endif + +ifeq ($(BOARD),) + $(error please specify BOARD for the build!) +endif + +ifeq ($(SCENARIO),) + $(error please specify SCENARIO for the build!) +endif + +ifeq ($(TARGET_DIR),) + BOARD_CFG_DIR := $(HV_SRC_DIR)/arch/x86/configs/$(BOARD) + SCENARIO_CFG_DIR := $(HV_SRC_DIR)/scenarios/$(SCENARIO) +else + BOARD_CFG_DIR := $(TARGET_DIR)/$(BOARD) + SCENARIO_CFG_DIR := $(TARGET_DIR)/$(SCENARIO) +endif + +PRE_BUILD_SRCS += main.c +PRE_BUILD_SRCS += static_checks.c +PRE_BUILD_SRCS += vm_cfg_checks.c +PRE_BUILD_SRCS += $(HV_SRC_DIR)/arch/x86/configs/vm_config.c +PRE_BUILD_SRCS += $(SCENARIO_CFG_DIR)/vm_configurations.c +ifneq (,$(wildcard $(SCENARIO_CFG_DIR)/pci_dev.c)) +PRE_BUILD_SRCS += $(SCENARIO_CFG_DIR)/pci_dev.c +endif +PRE_BUILD_CFLAGS += -fno-stack-protector -fno-builtin -W -Wall +PRE_BUILD_INCLUDE := $(patsubst %, -I %, $(INCLUDE_PATH)) -include $(HV_CONFIG_H) -I . + +.PHONY: default +default: $(PRE_BUILD_SRCS) + @echo "SCENARIO <$(SCENARIO)> for BOARD <$(BOARD)> is specified." + @if [ ! -d $(SCENARIO_CFG_DIR) ]; then \ + echo "Configurations for SCENARIO $(SCENARIO) is not found."; exit 1; \ + else \ + echo "Found SCENARIO $(SCENARIO) configurations under $(SCENARIO_CFG_DIR)"; \ + fi; + @if [ ! -d $(BOARD_CFG_DIR) ]; then \ + echo "$(BOARD) configuration for SCENARIO $(SCENARIO) is not found."; exit 1; \ + else \ + echo "Found $(BOARD) configuration for SCENARIO $(SCENARIO) under $(BOARD_CFG_DIR)"; \ + fi; + $(CC) $(PRE_BUILD_SRCS) $(PRE_BUILD_INCLUDE) $(PRE_BUILD_CFLAGS) -o $(HV_OBJDIR)/hv_prebuild_check.out diff --git a/misc/hv_prebuild/hv_prebuild.h b/misc/hv_prebuild/hv_prebuild.h new file mode 100644 index 000000000..acf8efd51 --- /dev/null +++ b/misc/hv_prebuild/hv_prebuild.h @@ -0,0 +1,13 @@ +/* + * Copyright (C) 2020 Intel Corporation. All rights reserved. + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +/* typedef size_t in types.h is conflicted with stdio.h, use below method as WR */ +#define size_t new_size_t +#include +#undef size_t + +bool uuid_is_equal(const uint8_t *uuid1, const uint8_t *uuid2); +bool sanitize_vm_config(void); diff --git a/misc/hv_prebuild/main.c b/misc/hv_prebuild/main.c new file mode 100644 index 000000000..5221339ef --- /dev/null +++ b/misc/hv_prebuild/main.c @@ -0,0 +1,22 @@ +/* + * Copyright (C) 2020 Intel Corporation. All rights reserved. + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +#include +#include + +int32_t main(void) +{ + int32_t ret = 0; + + if (!sanitize_vm_config()) { + printf("VM configuration check fail!\n"); + ret = -1; + } else { + printf("VM configuration check pass!\n"); + ret = 0; + } + return ret; +} diff --git a/hypervisor/pre_build/static_checks.c b/misc/hv_prebuild/static_checks.c similarity index 96% rename from hypervisor/pre_build/static_checks.c rename to misc/hv_prebuild/static_checks.c index 950df9957..971d216be 100644 --- a/hypervisor/pre_build/static_checks.c +++ b/misc/hv_prebuild/static_checks.c @@ -29,6 +29,10 @@ typedef int32_t CAT_(CTA_DummyType,__LINE__)[(expr) ? 1 : -1] #error "CONFIG_HV_RAM_SIZE must be integral multiple of 2MB" #endif +#if ((CONFIG_MAX_IR_ENTRIES < 256U) || (CONFIG_MAX_IR_ENTRIES & (CONFIG_MAX_IR_ENTRIES -1)) != 0U) +#error "CONFIG_MAX_IR_ENTRIES must >=256 and be 2^n" +#endif + /* Build time sanity checks to make sure hard-coded offset * is matching the actual offset! */ diff --git a/misc/hv_prebuild/vm_cfg_checks.c b/misc/hv_prebuild/vm_cfg_checks.c new file mode 100644 index 000000000..ea16631df --- /dev/null +++ b/misc/hv_prebuild/vm_cfg_checks.c @@ -0,0 +1,200 @@ +/* + * Copyright (C) 2020 Intel Corporation. All rights reserved. + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +#include +#include +#include +#include +#include +#include +#include + +static uint8_t rtvm_uuid1[16] = POST_RTVM_UUID1; +static uint8_t safety_vm_uuid1[16] = SAFETY_VM_UUID1; + +/* sanity check for below structs is not needed, so use a empty struct instead */ +struct acrn_vm_pci_dev_config sos_pci_devs[CONFIG_MAX_PCI_DEV_NUM]; +const struct pci_vdev_ops vhostbridge_ops; + +#define PLATFORM_CPUS_MASK ((1UL << MAX_PCPU_NUM) - 1UL) + +/** + * return true if the input uuid is for RTVM + * + * @pre vmid < CONFIG_MAX_VM_NUM + */ +static bool is_safety_vm_uuid(const uint8_t *uuid) +{ + /* TODO: Extend to check more safety VM uuid if we have more than one safety VM. */ + return uuid_is_equal(uuid, safety_vm_uuid1); +} + +/** + * return true if the input uuid is for RTVM + * + * @pre vmid < CONFIG_MAX_VM_NUM + */ +static bool is_rtvm_uuid(const uint8_t *uuid) +{ + /* TODO: Extend to check more rtvm uuid if we have more than one RTVM. */ + return uuid_is_equal(uuid, rtvm_uuid1); +} + +/** + * return true if no UUID collision is found in vm configs array start from vm_configs[vm_id] + * + * @pre vm_id < CONFIG_MAX_VM_NUM + */ +static bool check_vm_uuid_collision(uint16_t vm_id) +{ + uint16_t i; + bool ret = true; + struct acrn_vm_config *start_config = get_vm_config(vm_id); + struct acrn_vm_config *following_config; + + for (i = vm_id + 1U; i < CONFIG_MAX_VM_NUM; i++) { + following_config = get_vm_config(i); + if (uuid_is_equal(&start_config->uuid[0], &following_config->uuid[0])) { + ret = false; + break; + } + } + return ret; +} + +static bool check_vm_clos_config(uint16_t vm_id) +{ + uint16_t i; + uint16_t platform_clos_num = MAX_PLATFORM_CLOS_NUM; + bool ret = true; + struct acrn_vm_config *vm_config = get_vm_config(vm_id); + uint16_t vcpu_num = bitmap_weight(vm_config->cpu_affinity); + + for (i = 0U; i < vcpu_num; i++) { + if (((platform_clos_num != 0U) && (vm_config->clos[i] == platform_clos_num)) + || (vm_config->clos[i] > platform_clos_num)) { + printf("vm%u: vcpu%u clos(%u) exceed the max clos(%u).", + vm_id, i, vm_config->clos[i], platform_clos_num); + ret = false; + break; + } + } + return ret; +} + +/** + * @pre vm_config != NULL + */ +bool sanitize_vm_config(void) +{ + bool ret = true; + uint16_t vm_id, vuart_idx; + uint64_t pre_launch_pcpu_bitmap = 0UL; + struct acrn_vm_config *vm_config; + + /* All physical CPUs except ocuppied by Pre-launched VMs are all + * belong to SOS_VM. i.e. The cpu_affinity of a SOS_VM is decided + * by cpu_affinity status in PRE_LAUNCHED_VMs. + * We need to setup a rule, that the vm_configs[] array should follow + * the order of PRE_LAUNCHED_VM first, and then SOS_VM. + */ + for (vm_id = 0U; vm_id < CONFIG_MAX_VM_NUM; vm_id++) { + vm_config = get_vm_config(vm_id); + + if ((vm_config->cpu_affinity & ~PLATFORM_CPUS_MASK) != 0UL) { + printf("%s: vm%u assigns invalid PCPU (0x%llx)", __func__, vm_id, vm_config->cpu_affinity); + ret = false; + } + + switch (vm_config->load_order) { + case PRE_LAUNCHED_VM: + if (vm_config->cpu_affinity == 0UL) { + printf("%s: Pre-launch VM has no pcpus!\n", __func__); + ret = false; + /* GUEST_FLAG_RT must be set if we have GUEST_FLAG_LAPIC_PASSTHROUGH set in guest_flags */ + } else if (((vm_config->guest_flags & GUEST_FLAG_LAPIC_PASSTHROUGH) != 0U) + && ((vm_config->guest_flags & GUEST_FLAG_RT) == 0U)) { + ret = false; + } else if (vm_config->epc.size != 0UL) { + ret = false; + } else if (is_safety_vm_uuid(vm_config->uuid) && (vm_config->severity != (uint8_t)SEVERITY_SAFETY_VM)) { + ret = false; + } else { + pre_launch_pcpu_bitmap |= vm_config->cpu_affinity; + } + break; + case SOS_VM: + /* Deduct pcpus of PRE_LAUNCHED_VMs */ + vm_config->cpu_affinity = PLATFORM_CPUS_MASK ^ pre_launch_pcpu_bitmap; + if ((vm_config->cpu_affinity == 0UL) || (vm_config->severity != (uint8_t)SEVERITY_SOS) || + ((vm_config->guest_flags & GUEST_FLAG_LAPIC_PASSTHROUGH) != 0U)) { + ret = false; + } + break; + case POST_LAUNCHED_VM: + if ((vm_config->cpu_affinity == 0UL) || + ((vm_config->cpu_affinity & pre_launch_pcpu_bitmap) != 0UL)) { + printf("%s: Post-launch VM has no pcpus or share pcpu with Pre-launch VM!\n", __func__); + ret = false; + } + + if ((vm_config->severity == (uint8_t)SEVERITY_SAFETY_VM) || + (vm_config->severity == (uint8_t)SEVERITY_SOS)) { + ret = false; + } + + /* VM with RTVM uuid must have RTVM severity */ + if (is_rtvm_uuid(vm_config->uuid) && (vm_config->severity != (uint8_t)SEVERITY_RTVM)) { + ret = false; + } + + /* VM WITHOUT RTVM uuid must NOT have RTVM severity */ + if (!is_rtvm_uuid(vm_config->uuid) && (vm_config->severity == (uint8_t)SEVERITY_RTVM)) { + ret = false; + } + + break; + default: + /* Nothing to do for a unknown VM, break directly. */ + break; + } + + if (ret) { + ret = check_vm_clos_config(vm_id); + } + + if (ret && + (((vm_config->epc.size | vm_config->epc.base) & ~PAGE_MASK) != 0UL)) { + ret = false; + } + + if (ret) { + /* make sure no identical UUID in following VM configurations */ + ret = check_vm_uuid_collision(vm_id); + } + + if (ret) { + /* vuart[1+] are used for VM communications */ + for (vuart_idx = 1U; vuart_idx < MAX_VUART_NUM_PER_VM; vuart_idx++) { + const struct vuart_config *vu_config = &vm_config->vuart[vuart_idx]; + + if (!(vu_config->type == VUART_LEGACY_PIO) && + (vu_config->addr.port_base == INVALID_COM_BASE)) { + if ((vu_config->t_vuart.vm_id >= CONFIG_MAX_VM_NUM) || + (vu_config->t_vuart.vm_id == vm_id)) { + printf("%s invalid vuart configuration for VM %d\n", __func__, vm_id); + ret = false; + } + } + } + } + + if (!ret) { + break; + } + } + return ret; +}