diff options
277 files changed, 9896 insertions, 2321 deletions
diff --git a/.gitlab-ci.d/check-units.py b/.gitlab-ci.d/check-units.py index 268a411..cebef0e 100755 --- a/.gitlab-ci.d/check-units.py +++ b/.gitlab-ci.d/check-units.py @@ -8,8 +8,10 @@ # SPDX-License-Identifier: GPL-2.0-or-later from os import access, R_OK, path -from sys import argv, exit +from sys import exit import json +import argparse +from pathlib import Path from collections import Counter @@ -28,7 +30,7 @@ def extract_build_units(cc_path): return build_units -def analyse_units(build_units): +def analyse_units(build_units, top_n): """ Analyse the build units and report stats and the top 10 rebuilds """ @@ -42,7 +44,7 @@ def analyse_units(build_units): reverse=True) print("Most rebuilt units:") - for unit, count in sorted_build_units[:20]: + for unit, count in sorted_build_units[:top_n]: print(f" {unit} built {count} times") print("Least rebuilt units:") @@ -51,16 +53,19 @@ def analyse_units(build_units): if __name__ == "__main__": - if len(argv) != 2: - script_name = path.basename(argv[0]) - print(f"Usage: {script_name} <path_to_compile_commands.json>") - exit(1) + parser = argparse.ArgumentParser( + description="analyse number of build units in compile_commands.json") + parser.add_argument("cc_path", type=Path, default=None, + help="Path to compile_commands.json") + parser.add_argument("-n", type=int, default=20, + help="Dump the top <n> entries") + + args = parser.parse_args() - cc_path = argv[1] - if path.isfile(cc_path) and access(cc_path, R_OK): - units = extract_build_units(cc_path) - analyse_units(units) + if path.isfile(args.cc_path) and access(args.cc_path, R_OK): + units = extract_build_units(args.cc_path) + analyse_units(units, args.n) exit(0) else: - print(f"{cc_path} doesn't exist or isn't readable") + print(f"{args.cc_path} doesn't exist or isn't readable") exit(1) diff --git a/accel/kvm/kvm-all.c b/accel/kvm/kvm-all.c index a106d1b..78fc2d2 100644 --- a/accel/kvm/kvm-all.c +++ b/accel/kvm/kvm-all.c @@ -2606,8 +2606,7 @@ static int kvm_init(AccelState *as, MachineState *ms) { MachineClass *mc = MACHINE_GET_CLASS(ms); static const char upgrade_note[] = - "Please upgrade to at least kernel 2.6.29 or recent kvm-kmod\n" - "(see http://sourceforge.net/projects/kvm).\n"; + "Please upgrade to at least kernel 4.5.\n"; const struct { const char *name; int num; diff --git a/backends/confidential-guest-support.c b/backends/confidential-guest-support.c index 8ff7bfa..156dd15 100644 --- a/backends/confidential-guest-support.c +++ b/backends/confidential-guest-support.c @@ -14,15 +14,58 @@ #include "qemu/osdep.h" #include "system/confidential-guest-support.h" +#include "qapi/error.h" OBJECT_DEFINE_ABSTRACT_TYPE(ConfidentialGuestSupport, confidential_guest_support, CONFIDENTIAL_GUEST_SUPPORT, OBJECT) +static bool check_support(ConfidentialGuestPlatformType platform, + uint16_t platform_version, uint8_t highest_vtl, + uint64_t shared_gpa_boundary) +{ + /* Default: no support. */ + return false; +} + +static int set_guest_state(hwaddr gpa, uint8_t *ptr, uint64_t len, + ConfidentialGuestPageType memory_type, + uint16_t cpu_index, Error **errp) +{ + error_setg(errp, + "Setting confidential guest state is not supported for this platform"); + return -1; +} + +static int set_guest_policy(ConfidentialGuestPolicyType policy_type, + uint64_t policy, + void *policy_data1, uint32_t policy_data1_size, + void *policy_data2, uint32_t policy_data2_size, + Error **errp) +{ + error_setg(errp, + "Setting confidential guest policy is not supported for this platform"); + return -1; +} + +static int get_mem_map_entry(int index, ConfidentialGuestMemoryMapEntry *entry, + Error **errp) +{ + error_setg( + errp, + "Obtaining the confidential guest memory map is not supported for this platform"); + return -1; +} + static void confidential_guest_support_class_init(ObjectClass *oc, const void *data) { + ConfidentialGuestSupportClass *cgsc = CONFIDENTIAL_GUEST_SUPPORT_CLASS(oc); + cgsc->check_support = check_support; + cgsc->set_guest_state = set_guest_state; + cgsc->set_guest_policy = set_guest_policy; + cgsc->get_mem_map_entry = get_mem_map_entry; } static void confidential_guest_support_init(Object *obj) diff --git a/backends/igvm-cfg.c b/backends/igvm-cfg.c new file mode 100644 index 0000000..45df63e --- /dev/null +++ b/backends/igvm-cfg.c @@ -0,0 +1,51 @@ +/* + * QEMU IGVM interface + * + * Copyright (C) 2023-2024 SUSE + * + * Authors: + * Roy Hopkins <roy.hopkins@randomman.co.uk> + * + * SPDX-License-Identifier: GPL-2.0-or-later + */ + +#include "qemu/osdep.h" + +#include "system/igvm-cfg.h" +#include "igvm.h" +#include "qom/object_interfaces.h" + +static char *get_igvm(Object *obj, Error **errp) +{ + IgvmCfg *igvm = IGVM_CFG(obj); + return g_strdup(igvm->filename); +} + +static void set_igvm(Object *obj, const char *value, Error **errp) +{ + IgvmCfg *igvm = IGVM_CFG(obj); + g_free(igvm->filename); + igvm->filename = g_strdup(value); +} + +OBJECT_DEFINE_TYPE_WITH_INTERFACES(IgvmCfg, igvm_cfg, IGVM_CFG, OBJECT, + { TYPE_USER_CREATABLE }, { NULL }) + +static void igvm_cfg_class_init(ObjectClass *oc, const void *data) +{ + IgvmCfgClass *igvmc = IGVM_CFG_CLASS(oc); + + object_class_property_add_str(oc, "file", get_igvm, set_igvm); + object_class_property_set_description(oc, "file", + "Set the IGVM filename to use"); + + igvmc->process = qigvm_process_file; +} + +static void igvm_cfg_init(Object *obj) +{ +} + +static void igvm_cfg_finalize(Object *obj) +{ +} diff --git a/backends/igvm.c b/backends/igvm.c new file mode 100644 index 0000000..9ad4158 --- /dev/null +++ b/backends/igvm.c @@ -0,0 +1,988 @@ +/* + * QEMU IGVM configuration backend for guests + * + * Copyright (C) 2023-2024 SUSE + * + * Authors: + * Roy Hopkins <roy.hopkins@randomman.co.uk> + * + * SPDX-License-Identifier: GPL-2.0-or-later + */ + +#include "qemu/osdep.h" + +#include "igvm.h" +#include "qapi/error.h" +#include "system/memory.h" +#include "system/address-spaces.h" +#include "hw/core/cpu.h" + +#include <igvm/igvm.h> +#include <igvm/igvm_defs.h> + +typedef struct QIgvmParameterData { + QTAILQ_ENTRY(QIgvmParameterData) next; + uint8_t *data; + uint32_t size; + uint32_t index; +} QIgvmParameterData; + +/* + * Some directives are specific to particular confidential computing platforms. + * Define required types for each of those platforms here. + */ + +/* SEV/SEV-ES/SEV-SNP */ + +/* + * These structures are defined in "SEV Secure Nested Paging Firmware ABI + * Specification" Rev 1.58, section 8.18. + */ +struct QEMU_PACKED sev_id_block { + uint8_t ld[48]; + uint8_t family_id[16]; + uint8_t image_id[16]; + uint32_t version; + uint32_t guest_svn; + uint64_t policy; +}; + +struct QEMU_PACKED sev_id_authentication { + uint32_t id_key_alg; + uint32_t auth_key_algo; + uint8_t reserved[56]; + uint8_t id_block_sig[512]; + uint8_t id_key[1028]; + uint8_t reserved2[60]; + uint8_t id_key_sig[512]; + uint8_t author_key[1028]; + uint8_t reserved3[892]; +}; + +#define IGVM_SEV_ID_BLOCK_VERSION 1 + +/* + * QIgvm contains the information required during processing + * of a single IGVM file. + */ +typedef struct QIgvm { + IgvmHandle file; + ConfidentialGuestSupport *cgs; + ConfidentialGuestSupportClass *cgsc; + uint32_t compatibility_mask; + unsigned current_header_index; + QTAILQ_HEAD(, QIgvmParameterData) parameter_data; + IgvmPlatformType platform_type; + + /* + * SEV-SNP platforms can contain an ID block and authentication + * that should be verified by the guest. + */ + struct sev_id_block *id_block; + struct sev_id_authentication *id_auth; + + /* Define the guest policy for SEV guests */ + uint64_t sev_policy; + + /* These variables keep track of contiguous page regions */ + IGVM_VHS_PAGE_DATA region_prev_page_data; + uint64_t region_start; + unsigned region_start_index; + unsigned region_last_index; + unsigned region_page_count; +} QIgvm; + +static int qigvm_directive_page_data(QIgvm *ctx, const uint8_t *header_data, + Error **errp); +static int qigvm_directive_vp_context(QIgvm *ctx, const uint8_t *header_data, + Error **errp); +static int qigvm_directive_parameter_area(QIgvm *ctx, + const uint8_t *header_data, + Error **errp); +static int qigvm_directive_parameter_insert(QIgvm *ctx, + const uint8_t *header_data, + Error **errp); +static int qigvm_directive_memory_map(QIgvm *ctx, const uint8_t *header_data, + Error **errp); +static int qigvm_directive_vp_count(QIgvm *ctx, const uint8_t *header_data, + Error **errp); +static int qigvm_directive_environment_info(QIgvm *ctx, + const uint8_t *header_data, + Error **errp); +static int qigvm_directive_required_memory(QIgvm *ctx, + const uint8_t *header_data, + Error **errp); +static int qigvm_directive_snp_id_block(QIgvm *ctx, const uint8_t *header_data, + Error **errp); +static int qigvm_initialization_guest_policy(QIgvm *ctx, + const uint8_t *header_data, + Error **errp); + +struct QIGVMHandler { + uint32_t type; + uint32_t section; + int (*handler)(QIgvm *ctx, const uint8_t *header_data, Error **errp); +}; + +static struct QIGVMHandler handlers[] = { + { IGVM_VHT_PAGE_DATA, IGVM_HEADER_SECTION_DIRECTIVE, + qigvm_directive_page_data }, + { IGVM_VHT_VP_CONTEXT, IGVM_HEADER_SECTION_DIRECTIVE, + qigvm_directive_vp_context }, + { IGVM_VHT_PARAMETER_AREA, IGVM_HEADER_SECTION_DIRECTIVE, + qigvm_directive_parameter_area }, + { IGVM_VHT_PARAMETER_INSERT, IGVM_HEADER_SECTION_DIRECTIVE, + qigvm_directive_parameter_insert }, + { IGVM_VHT_MEMORY_MAP, IGVM_HEADER_SECTION_DIRECTIVE, + qigvm_directive_memory_map }, + { IGVM_VHT_VP_COUNT_PARAMETER, IGVM_HEADER_SECTION_DIRECTIVE, + qigvm_directive_vp_count }, + { IGVM_VHT_ENVIRONMENT_INFO_PARAMETER, IGVM_HEADER_SECTION_DIRECTIVE, + qigvm_directive_environment_info }, + { IGVM_VHT_REQUIRED_MEMORY, IGVM_HEADER_SECTION_DIRECTIVE, + qigvm_directive_required_memory }, + { IGVM_VHT_SNP_ID_BLOCK, IGVM_HEADER_SECTION_DIRECTIVE, + qigvm_directive_snp_id_block }, + { IGVM_VHT_GUEST_POLICY, IGVM_HEADER_SECTION_INITIALIZATION, + qigvm_initialization_guest_policy }, +}; + +static int qigvm_handler(QIgvm *ctx, uint32_t type, Error **errp) +{ + size_t handler; + IgvmHandle header_handle; + const uint8_t *header_data; + int result; + + for (handler = 0; handler < G_N_ELEMENTS(handlers); handler++) { + if (handlers[handler].type != type) { + continue; + } + header_handle = igvm_get_header(ctx->file, handlers[handler].section, + ctx->current_header_index); + if (header_handle < 0) { + error_setg( + errp, + "IGVM file is invalid: Failed to read directive header (code: %d)", + (int)header_handle); + return -1; + } + header_data = igvm_get_buffer(ctx->file, header_handle) + + sizeof(IGVM_VHS_VARIABLE_HEADER); + result = handlers[handler].handler(ctx, header_data, errp); + igvm_free_buffer(ctx->file, header_handle); + return result; + } + error_setg(errp, + "IGVM: Unknown header type encountered when processing file: " + "(type 0x%X)", + type); + return -1; +} + +static void *qigvm_prepare_memory(QIgvm *ctx, uint64_t addr, uint64_t size, + int region_identifier, Error **errp) +{ + ERRP_GUARD(); + MemoryRegion *igvm_pages = NULL; + Int128 gpa_region_size; + MemoryRegionSection mrs = + memory_region_find(get_system_memory(), addr, size); + if (mrs.mr) { + if (!memory_region_is_ram(mrs.mr)) { + memory_region_unref(mrs.mr); + error_setg( + errp, + "Processing of IGVM file failed: Could not prepare memory " + "at address 0x%lX due to existing non-RAM region", + addr); + return NULL; + } + + gpa_region_size = int128_make64(size); + if (int128_lt(mrs.size, gpa_region_size)) { + memory_region_unref(mrs.mr); + error_setg( + errp, + "Processing of IGVM file failed: Could not prepare memory " + "at address 0x%lX: region size exceeded", + addr); + return NULL; + } + return qemu_map_ram_ptr(mrs.mr->ram_block, mrs.offset_within_region); + } else { + /* + * The region_identifier is the is the index of the IGVM directive that + * contains the page with the lowest GPA in the region. This will + * generate a unique region name. + */ + g_autofree char *region_name = + g_strdup_printf("igvm.%X", region_identifier); + igvm_pages = g_new0(MemoryRegion, 1); + if (ctx->cgs && ctx->cgs->require_guest_memfd) { + if (!memory_region_init_ram_guest_memfd(igvm_pages, NULL, + region_name, size, errp)) { + return NULL; + } + } else { + if (!memory_region_init_ram(igvm_pages, NULL, region_name, size, + errp)) { + return NULL; + } + } + memory_region_add_subregion(get_system_memory(), addr, igvm_pages); + return memory_region_get_ram_ptr(igvm_pages); + } +} + +static int qigvm_type_to_cgs_type(IgvmPageDataType memory_type, bool unmeasured, + bool zero) +{ + switch (memory_type) { + case IGVM_PAGE_DATA_TYPE_NORMAL: { + if (unmeasured) { + return CGS_PAGE_TYPE_UNMEASURED; + } else { + return zero ? CGS_PAGE_TYPE_ZERO : CGS_PAGE_TYPE_NORMAL; + } + } + case IGVM_PAGE_DATA_TYPE_SECRETS: + return CGS_PAGE_TYPE_SECRETS; + case IGVM_PAGE_DATA_TYPE_CPUID_DATA: + return CGS_PAGE_TYPE_CPUID; + case IGVM_PAGE_DATA_TYPE_CPUID_XF: + return CGS_PAGE_TYPE_CPUID; + default: + return -1; + } +} + +static bool qigvm_page_attrs_equal(IgvmHandle igvm, unsigned header_index, + const IGVM_VHS_PAGE_DATA *page_1, + const IGVM_VHS_PAGE_DATA *page_2) +{ + IgvmHandle data_handle1, data_handle2; + + /* + * If one page has data and the other doesn't then this results in different + * page types: NORMAL vs ZERO. + */ + data_handle1 = igvm_get_header_data(igvm, IGVM_HEADER_SECTION_DIRECTIVE, + header_index - 1); + data_handle2 = + igvm_get_header_data(igvm, IGVM_HEADER_SECTION_DIRECTIVE, header_index); + if ((data_handle1 == IGVMAPI_NO_DATA || + data_handle2 == IGVMAPI_NO_DATA) && + data_handle1 != data_handle2) { + return false; + } + return ((*(const uint32_t *)&page_1->flags == + *(const uint32_t *)&page_2->flags) && + (page_1->data_type == page_2->data_type) && + (page_1->compatibility_mask == page_2->compatibility_mask)); +} + +static int qigvm_process_mem_region(QIgvm *ctx, unsigned start_index, + uint64_t gpa_start, unsigned page_count, + const IgvmPageDataFlags *flags, + const IgvmPageDataType page_type, + Error **errp) +{ + uint8_t *region; + IgvmHandle data_handle; + const void *data; + uint32_t data_size; + unsigned page_index; + bool zero = true; + const uint64_t page_size = flags->is_2mb_page ? 0x200000 : 0x1000; + int result; + int cgs_page_type; + + region = qigvm_prepare_memory(ctx, gpa_start, page_count * page_size, + start_index, errp); + if (!region) { + return -1; + } + + for (page_index = 0; page_index < page_count; page_index++) { + data_handle = igvm_get_header_data( + ctx->file, IGVM_HEADER_SECTION_DIRECTIVE, page_index + start_index); + if (data_handle == IGVMAPI_NO_DATA) { + /* No data indicates a zero page */ + memset(®ion[page_index * page_size], 0, page_size); + } else if (data_handle < 0) { + error_setg( + errp, + "IGVM file contains invalid page data for directive with " + "index %d", + page_index + start_index); + return -1; + } else { + zero = false; + data_size = igvm_get_buffer_size(ctx->file, data_handle); + if (data_size < page_size) { + memset(®ion[page_index * page_size], 0, page_size); + } else if (data_size > page_size) { + error_setg(errp, + "IGVM file contains page data with invalid size for " + "directive with index %d", + page_index + start_index); + return -1; + } + data = igvm_get_buffer(ctx->file, data_handle); + memcpy(®ion[page_index * page_size], data, data_size); + igvm_free_buffer(ctx->file, data_handle); + } + } + + /* + * If a confidential guest support object is provided then use it to set the + * guest state. + */ + if (ctx->cgs) { + cgs_page_type = + qigvm_type_to_cgs_type(page_type, flags->unmeasured, zero); + if (cgs_page_type < 0) { + error_setg(errp, + "Invalid page type in IGVM file. Directives: %d to %d, " + "page type: %d", + start_index, start_index + page_count, page_type); + return -1; + } + + result = ctx->cgsc->set_guest_state( + gpa_start, region, page_size * page_count, cgs_page_type, 0, errp); + if (result < 0) { + return result; + } + } + return 0; +} + +static int qigvm_process_mem_page(QIgvm *ctx, + const IGVM_VHS_PAGE_DATA *page_data, + Error **errp) +{ + if (page_data) { + if (ctx->region_page_count == 0) { + ctx->region_start = page_data->gpa; + ctx->region_start_index = ctx->current_header_index; + } else { + if (!qigvm_page_attrs_equal(ctx->file, ctx->current_header_index, + page_data, + &ctx->region_prev_page_data) || + ((ctx->region_prev_page_data.gpa + + (ctx->region_prev_page_data.flags.is_2mb_page ? 0x200000 : + 0x1000)) != + page_data->gpa) || + (ctx->region_last_index != (ctx->current_header_index - 1))) { + /* End of current region */ + if (qigvm_process_mem_region( + ctx, ctx->region_start_index, ctx->region_start, + ctx->region_page_count, + &ctx->region_prev_page_data.flags, + ctx->region_prev_page_data.data_type, errp) < 0) { + return -1; + } + ctx->region_page_count = 0; + ctx->region_start = page_data->gpa; + ctx->region_start_index = ctx->current_header_index; + } + } + memcpy(&ctx->region_prev_page_data, page_data, + sizeof(ctx->region_prev_page_data)); + ctx->region_last_index = ctx->current_header_index; + ctx->region_page_count++; + } else { + if (ctx->region_page_count > 0) { + if (qigvm_process_mem_region( + ctx, ctx->region_start_index, ctx->region_start, + ctx->region_page_count, &ctx->region_prev_page_data.flags, + ctx->region_prev_page_data.data_type, errp) < 0) { + return -1; + } + ctx->region_page_count = 0; + } + } + return 0; +} + +static int qigvm_directive_page_data(QIgvm *ctx, const uint8_t *header_data, + Error **errp) +{ + const IGVM_VHS_PAGE_DATA *page_data = + (const IGVM_VHS_PAGE_DATA *)header_data; + if (page_data->compatibility_mask & ctx->compatibility_mask) { + return qigvm_process_mem_page(ctx, page_data, errp); + } + return 0; +} + +static int qigvm_directive_vp_context(QIgvm *ctx, const uint8_t *header_data, + Error **errp) +{ + const IGVM_VHS_VP_CONTEXT *vp_context = + (const IGVM_VHS_VP_CONTEXT *)header_data; + IgvmHandle data_handle; + uint8_t *data; + int result; + + if (!(vp_context->compatibility_mask & ctx->compatibility_mask)) { + return 0; + } + + /* + * A confidential guest support object must be provided for setting + * a VP context. + */ + if (!ctx->cgs) { + error_setg( + errp, + "A VP context is present in the IGVM file but is not supported " + "by the current system."); + return -1; + } + + data_handle = igvm_get_header_data(ctx->file, IGVM_HEADER_SECTION_DIRECTIVE, + ctx->current_header_index); + if (data_handle < 0) { + error_setg(errp, "Invalid VP context in IGVM file. Error code: %X", + data_handle); + return -1; + } + + data = (uint8_t *)igvm_get_buffer(ctx->file, data_handle); + result = ctx->cgsc->set_guest_state( + vp_context->gpa, data, igvm_get_buffer_size(ctx->file, data_handle), + CGS_PAGE_TYPE_VMSA, vp_context->vp_index, errp); + igvm_free_buffer(ctx->file, data_handle); + if (result < 0) { + return result; + } + return 0; +} + +static int qigvm_directive_parameter_area(QIgvm *ctx, + const uint8_t *header_data, + Error **errp) +{ + const IGVM_VHS_PARAMETER_AREA *param_area = + (const IGVM_VHS_PARAMETER_AREA *)header_data; + QIgvmParameterData *param_entry; + + param_entry = g_new0(QIgvmParameterData, 1); + param_entry->size = param_area->number_of_bytes; + param_entry->index = param_area->parameter_area_index; + param_entry->data = g_malloc0(param_entry->size); + + QTAILQ_INSERT_TAIL(&ctx->parameter_data, param_entry, next); + return 0; +} + +static int qigvm_directive_parameter_insert(QIgvm *ctx, + const uint8_t *header_data, + Error **errp) +{ + const IGVM_VHS_PARAMETER_INSERT *param = + (const IGVM_VHS_PARAMETER_INSERT *)header_data; + QIgvmParameterData *param_entry; + int result; + void *region; + + if (!(param->compatibility_mask & ctx->compatibility_mask)) { + return 0; + } + + QTAILQ_FOREACH(param_entry, &ctx->parameter_data, next) + { + if (param_entry->index == param->parameter_area_index) { + region = qigvm_prepare_memory(ctx, param->gpa, param_entry->size, + ctx->current_header_index, errp); + if (!region) { + return -1; + } + memcpy(region, param_entry->data, param_entry->size); + g_free(param_entry->data); + param_entry->data = NULL; + + /* + * If a confidential guest support object is provided then use it to + * set the guest state. + */ + if (ctx->cgs) { + result = ctx->cgsc->set_guest_state(param->gpa, region, + param_entry->size, + CGS_PAGE_TYPE_UNMEASURED, 0, + errp); + if (result < 0) { + return -1; + } + } + } + } + return 0; +} + +static int qigvm_cmp_mm_entry(const void *a, const void *b) +{ + const IGVM_VHS_MEMORY_MAP_ENTRY *entry_a = + (const IGVM_VHS_MEMORY_MAP_ENTRY *)a; + const IGVM_VHS_MEMORY_MAP_ENTRY *entry_b = + (const IGVM_VHS_MEMORY_MAP_ENTRY *)b; + if (entry_a->starting_gpa_page_number < entry_b->starting_gpa_page_number) { + return -1; + } else if (entry_a->starting_gpa_page_number > + entry_b->starting_gpa_page_number) { + return 1; + } else { + return 0; + } +} + +static int qigvm_directive_memory_map(QIgvm *ctx, const uint8_t *header_data, + Error **errp) +{ + const IGVM_VHS_PARAMETER *param = (const IGVM_VHS_PARAMETER *)header_data; + QIgvmParameterData *param_entry; + int max_entry_count; + int entry = 0; + IGVM_VHS_MEMORY_MAP_ENTRY *mm_entry; + ConfidentialGuestMemoryMapEntry cgmm_entry; + int retval = 0; + + if (!ctx->cgs) { + error_setg(errp, + "IGVM file contains a memory map but this is not supported " + "by the current system."); + return -1; + } + + /* Find the parameter area that should hold the memory map */ + QTAILQ_FOREACH(param_entry, &ctx->parameter_data, next) + { + if (param_entry->index == param->parameter_area_index) { + max_entry_count = + param_entry->size / sizeof(IGVM_VHS_MEMORY_MAP_ENTRY); + mm_entry = (IGVM_VHS_MEMORY_MAP_ENTRY *)param_entry->data; + + retval = ctx->cgsc->get_mem_map_entry(entry, &cgmm_entry, errp); + while (retval == 0) { + if (entry > max_entry_count) { + error_setg( + errp, + "IGVM: guest memory map size exceeds parameter area defined in IGVM file"); + return -1; + } + mm_entry[entry].starting_gpa_page_number = cgmm_entry.gpa >> 12; + mm_entry[entry].number_of_pages = cgmm_entry.size >> 12; + + switch (cgmm_entry.type) { + case CGS_MEM_RAM: + mm_entry[entry].entry_type = + IGVM_MEMORY_MAP_ENTRY_TYPE_MEMORY; + break; + case CGS_MEM_RESERVED: + mm_entry[entry].entry_type = + IGVM_MEMORY_MAP_ENTRY_TYPE_PLATFORM_RESERVED; + break; + case CGS_MEM_ACPI: + mm_entry[entry].entry_type = + IGVM_MEMORY_MAP_ENTRY_TYPE_PLATFORM_RESERVED; + break; + case CGS_MEM_NVS: + mm_entry[entry].entry_type = + IGVM_MEMORY_MAP_ENTRY_TYPE_PERSISTENT; + break; + case CGS_MEM_UNUSABLE: + mm_entry[entry].entry_type = + IGVM_MEMORY_MAP_ENTRY_TYPE_PLATFORM_RESERVED; + break; + } + retval = + ctx->cgsc->get_mem_map_entry(++entry, &cgmm_entry, errp); + } + if (retval < 0) { + return retval; + } + /* The entries need to be sorted */ + qsort(mm_entry, entry, sizeof(IGVM_VHS_MEMORY_MAP_ENTRY), + qigvm_cmp_mm_entry); + + break; + } + } + return 0; +} + +static int qigvm_directive_vp_count(QIgvm *ctx, const uint8_t *header_data, + Error **errp) +{ + const IGVM_VHS_PARAMETER *param = (const IGVM_VHS_PARAMETER *)header_data; + QIgvmParameterData *param_entry; + uint32_t *vp_count; + CPUState *cpu; + + QTAILQ_FOREACH(param_entry, &ctx->parameter_data, next) + { + if (param_entry->index == param->parameter_area_index) { + vp_count = (uint32_t *)(param_entry->data + param->byte_offset); + *vp_count = 0; + CPU_FOREACH(cpu) + { + (*vp_count)++; + } + break; + } + } + return 0; +} + +static int qigvm_directive_environment_info(QIgvm *ctx, + const uint8_t *header_data, + Error **errp) +{ + const IGVM_VHS_PARAMETER *param = (const IGVM_VHS_PARAMETER *)header_data; + QIgvmParameterData *param_entry; + IgvmEnvironmentInfo *environmental_state; + + QTAILQ_FOREACH(param_entry, &ctx->parameter_data, next) + { + if (param_entry->index == param->parameter_area_index) { + environmental_state = + (IgvmEnvironmentInfo *)(param_entry->data + param->byte_offset); + environmental_state->memory_is_shared = 1; + break; + } + } + return 0; +} + +static int qigvm_directive_required_memory(QIgvm *ctx, + const uint8_t *header_data, + Error **errp) +{ + const IGVM_VHS_REQUIRED_MEMORY *mem = + (const IGVM_VHS_REQUIRED_MEMORY *)header_data; + uint8_t *region; + int result; + + if (!(mem->compatibility_mask & ctx->compatibility_mask)) { + return 0; + } + + region = qigvm_prepare_memory(ctx, mem->gpa, mem->number_of_bytes, + ctx->current_header_index, errp); + if (!region) { + return -1; + } + if (ctx->cgs) { + result = + ctx->cgsc->set_guest_state(mem->gpa, region, mem->number_of_bytes, + CGS_PAGE_TYPE_REQUIRED_MEMORY, 0, errp); + if (result < 0) { + return result; + } + } + return 0; +} + +static int qigvm_directive_snp_id_block(QIgvm *ctx, const uint8_t *header_data, + Error **errp) +{ + const IGVM_VHS_SNP_ID_BLOCK *igvm_id = + (const IGVM_VHS_SNP_ID_BLOCK *)header_data; + + if (!(igvm_id->compatibility_mask & ctx->compatibility_mask)) { + return 0; + } + + if (ctx->id_block) { + error_setg(errp, "IGVM: Multiple ID blocks encountered " + "in IGVM file."); + return -1; + } + ctx->id_block = g_new0(struct sev_id_block, 1); + ctx->id_auth = g_new0(struct sev_id_authentication, 1); + + memcpy(ctx->id_block->family_id, igvm_id->family_id, + sizeof(ctx->id_block->family_id)); + memcpy(ctx->id_block->image_id, igvm_id->image_id, + sizeof(ctx->id_block->image_id)); + ctx->id_block->guest_svn = igvm_id->guest_svn; + ctx->id_block->version = IGVM_SEV_ID_BLOCK_VERSION; + memcpy(ctx->id_block->ld, igvm_id->ld, sizeof(ctx->id_block->ld)); + + ctx->id_auth->id_key_alg = igvm_id->id_key_algorithm; + assert(sizeof(igvm_id->id_key_signature) <= + sizeof(ctx->id_auth->id_block_sig)); + memcpy(ctx->id_auth->id_block_sig, &igvm_id->id_key_signature, + sizeof(igvm_id->id_key_signature)); + + ctx->id_auth->auth_key_algo = igvm_id->author_key_algorithm; + assert(sizeof(igvm_id->author_key_signature) <= + sizeof(ctx->id_auth->id_key_sig)); + memcpy(ctx->id_auth->id_key_sig, &igvm_id->author_key_signature, + sizeof(igvm_id->author_key_signature)); + + /* + * SEV and IGVM public key structure population are slightly different. + * See SEV Secure Nested Paging Firmware ABI Specification, Chapter 10. + */ + *((uint32_t *)ctx->id_auth->id_key) = igvm_id->id_public_key.curve; + memcpy(&ctx->id_auth->id_key[4], &igvm_id->id_public_key.qx, 72); + memcpy(&ctx->id_auth->id_key[76], &igvm_id->id_public_key.qy, 72); + + *((uint32_t *)ctx->id_auth->author_key) = + igvm_id->author_public_key.curve; + memcpy(&ctx->id_auth->author_key[4], &igvm_id->author_public_key.qx, + 72); + memcpy(&ctx->id_auth->author_key[76], &igvm_id->author_public_key.qy, + 72); + + return 0; +} + +static int qigvm_initialization_guest_policy(QIgvm *ctx, + const uint8_t *header_data, Error **errp) +{ + const IGVM_VHS_GUEST_POLICY *guest = + (const IGVM_VHS_GUEST_POLICY *)header_data; + + if (guest->compatibility_mask & ctx->compatibility_mask) { + ctx->sev_policy = guest->policy; + } + return 0; +} + +static int qigvm_supported_platform_compat_mask(QIgvm *ctx, Error **errp) +{ + int32_t header_count; + unsigned header_index; + IgvmHandle header_handle; + IGVM_VHS_SUPPORTED_PLATFORM *platform; + uint32_t compatibility_mask_sev = 0; + uint32_t compatibility_mask_sev_es = 0; + uint32_t compatibility_mask_sev_snp = 0; + uint32_t compatibility_mask = 0; + + header_count = igvm_header_count(ctx->file, IGVM_HEADER_SECTION_PLATFORM); + if (header_count < 0) { + error_setg(errp, + "Invalid platform header count in IGVM file. Error code: %X", + header_count); + return -1; + } + + for (header_index = 0; header_index < (unsigned)header_count; + header_index++) { + IgvmVariableHeaderType typ = igvm_get_header_type( + ctx->file, IGVM_HEADER_SECTION_PLATFORM, header_index); + if (typ == IGVM_VHT_SUPPORTED_PLATFORM) { + header_handle = igvm_get_header( + ctx->file, IGVM_HEADER_SECTION_PLATFORM, header_index); + if (header_handle < 0) { + error_setg(errp, + "Invalid platform header in IGVM file. " + "Index: %d, Error code: %X", + header_index, header_handle); + return -1; + } + platform = + (IGVM_VHS_SUPPORTED_PLATFORM *)(igvm_get_buffer(ctx->file, + header_handle) + + sizeof( + IGVM_VHS_VARIABLE_HEADER)); + if ((platform->platform_type == IGVM_PLATFORM_TYPE_SEV_ES) && + ctx->cgs) { + if (ctx->cgsc->check_support( + CGS_PLATFORM_SEV_ES, platform->platform_version, + platform->highest_vtl, platform->shared_gpa_boundary)) { + compatibility_mask_sev_es = platform->compatibility_mask; + } + } else if ((platform->platform_type == IGVM_PLATFORM_TYPE_SEV) && + ctx->cgs) { + if (ctx->cgsc->check_support( + CGS_PLATFORM_SEV, platform->platform_version, + platform->highest_vtl, platform->shared_gpa_boundary)) { + compatibility_mask_sev = platform->compatibility_mask; + } + } else if ((platform->platform_type == + IGVM_PLATFORM_TYPE_SEV_SNP) && + ctx->cgs) { + if (ctx->cgsc->check_support( + CGS_PLATFORM_SEV_SNP, platform->platform_version, + platform->highest_vtl, platform->shared_gpa_boundary)) { + compatibility_mask_sev_snp = platform->compatibility_mask; + } + } else if (platform->platform_type == IGVM_PLATFORM_TYPE_NATIVE) { + compatibility_mask = platform->compatibility_mask; + } + igvm_free_buffer(ctx->file, header_handle); + } + } + /* Choose the strongest supported isolation technology */ + if (compatibility_mask_sev_snp != 0) { + ctx->compatibility_mask = compatibility_mask_sev_snp; + ctx->platform_type = IGVM_PLATFORM_TYPE_SEV_SNP; + } else if (compatibility_mask_sev_es != 0) { + ctx->compatibility_mask = compatibility_mask_sev_es; + ctx->platform_type = IGVM_PLATFORM_TYPE_SEV_ES; + } else if (compatibility_mask_sev != 0) { + ctx->compatibility_mask = compatibility_mask_sev; + ctx->platform_type = IGVM_PLATFORM_TYPE_SEV; + } else if (compatibility_mask != 0) { + ctx->compatibility_mask = compatibility_mask; + ctx->platform_type = IGVM_PLATFORM_TYPE_NATIVE; + } else { + error_setg( + errp, + "IGVM file does not describe a compatible supported platform"); + return -1; + } + return 0; +} + +static int qigvm_handle_policy(QIgvm *ctx, Error **errp) +{ + if (ctx->platform_type == IGVM_PLATFORM_TYPE_SEV_SNP) { + int id_block_len = 0; + int id_auth_len = 0; + if (ctx->id_block) { + ctx->id_block->policy = ctx->sev_policy; + id_block_len = sizeof(struct sev_id_block); + id_auth_len = sizeof(struct sev_id_authentication); + } + return ctx->cgsc->set_guest_policy(GUEST_POLICY_SEV, ctx->sev_policy, + ctx->id_block, id_block_len, + ctx->id_auth, id_auth_len, errp); + } + return 0; +} + +static IgvmHandle qigvm_file_init(char *filename, Error **errp) +{ + IgvmHandle igvm; + g_autofree uint8_t *buf = NULL; + unsigned long len; + g_autoptr(GError) gerr = NULL; + + if (!g_file_get_contents(filename, (gchar **)&buf, &len, &gerr)) { + error_setg(errp, "Unable to load %s: %s", filename, gerr->message); + return -1; + } + + igvm = igvm_new_from_binary(buf, len); + if (igvm < 0) { + error_setg(errp, "Unable to parse IGVM file %s: %d", filename, igvm); + return -1; + } + return igvm; +} + +int qigvm_process_file(IgvmCfg *cfg, ConfidentialGuestSupport *cgs, + bool onlyVpContext, Error **errp) +{ + int32_t header_count; + QIgvmParameterData *parameter; + int retval = -1; + QIgvm ctx; + + memset(&ctx, 0, sizeof(ctx)); + ctx.file = qigvm_file_init(cfg->filename, errp); + if (ctx.file < 0) { + return -1; + } + + /* + * The ConfidentialGuestSupport object is optional and allows a confidential + * guest platform to perform extra processing, such as page measurement, on + * IGVM directives. + */ + ctx.cgs = cgs; + ctx.cgsc = cgs ? CONFIDENTIAL_GUEST_SUPPORT_GET_CLASS(cgs) : NULL; + + /* + * Check that the IGVM file provides configuration for the current + * platform + */ + if (qigvm_supported_platform_compat_mask(&ctx, errp) < 0) { + goto cleanup; + } + + header_count = igvm_header_count(ctx.file, IGVM_HEADER_SECTION_DIRECTIVE); + if (header_count <= 0) { + error_setg( + errp, "Invalid directive header count in IGVM file. Error code: %X", + header_count); + goto cleanup; + } + + QTAILQ_INIT(&ctx.parameter_data); + + for (ctx.current_header_index = 0; + ctx.current_header_index < (unsigned)header_count; + ctx.current_header_index++) { + IgvmVariableHeaderType type = igvm_get_header_type( + ctx.file, IGVM_HEADER_SECTION_DIRECTIVE, ctx.current_header_index); + if (!onlyVpContext || (type == IGVM_VHT_VP_CONTEXT)) { + if (qigvm_handler(&ctx, type, errp) < 0) { + goto cleanup_parameters; + } + } + } + + /* + * If only processing the VP context then we don't need to process + * any more of the file. + */ + if (onlyVpContext) { + retval = 0; + goto cleanup_parameters; + } + + header_count = + igvm_header_count(ctx.file, IGVM_HEADER_SECTION_INITIALIZATION); + if (header_count < 0) { + error_setg( + errp, + "Invalid initialization header count in IGVM file. Error code: %X", + header_count); + goto cleanup_parameters; + } + + for (ctx.current_header_index = 0; + ctx.current_header_index < (unsigned)header_count; + ctx.current_header_index++) { + IgvmVariableHeaderType type = + igvm_get_header_type(ctx.file, IGVM_HEADER_SECTION_INITIALIZATION, + ctx.current_header_index); + if (qigvm_handler(&ctx, type, errp) < 0) { + goto cleanup_parameters; + } + } + + /* + * Contiguous pages of data with compatible flags are grouped together in + * order to reduce the number of memory regions we create. Make sure the + * last group is processed with this call. + */ + retval = qigvm_process_mem_page(&ctx, NULL, errp); + + if (retval == 0) { + retval = qigvm_handle_policy(&ctx, errp); + } + +cleanup_parameters: + QTAILQ_FOREACH(parameter, &ctx.parameter_data, next) + { + g_free(parameter->data); + parameter->data = NULL; + } + g_free(ctx.id_block); + g_free(ctx.id_auth); + +cleanup: + igvm_free(ctx.file); + + return retval; +} diff --git a/backends/igvm.h b/backends/igvm.h new file mode 100644 index 0000000..a4abab0 --- /dev/null +++ b/backends/igvm.h @@ -0,0 +1,22 @@ +/* + * QEMU IGVM configuration backend for Confidential Guests + * + * Copyright (C) 2023-2024 SUSE + * + * Authors: + * Roy Hopkins <roy.hopkins@randomman.co.uk> + * + * SPDX-License-Identifier: GPL-2.0-or-later + */ + +#ifndef BACKENDS_IGVM_H +#define BACKENDS_IGVM_H + +#include "system/confidential-guest-support.h" +#include "system/igvm-cfg.h" +#include "qapi/error.h" + +int qigvm_process_file(IgvmCfg *igvm, ConfidentialGuestSupport *cgs, + bool onlyVpContext, Error **errp); + +#endif diff --git a/backends/meson.build b/backends/meson.build index 9b88d22..60021f4 100644 --- a/backends/meson.build +++ b/backends/meson.build @@ -34,6 +34,11 @@ if have_vhost_user_crypto endif system_ss.add(when: gio, if_true: files('dbus-vmstate.c')) system_ss.add(when: 'CONFIG_SGX', if_true: files('hostmem-epc.c')) +if igvm.found() + system_ss.add(igvm) + system_ss.add(files('igvm-cfg.c'), igvm) + system_ss.add(files('igvm.c'), igvm) +endif system_ss.add(when: 'CONFIG_SPDM_SOCKET', if_true: files('spdm-socket.c')) diff --git a/common-user/plugin-api.c.inc b/common-user/plugin-api.c.inc index 5b8a139..63f3983 100644 --- a/common-user/plugin-api.c.inc +++ b/common-user/plugin-api.c.inc @@ -13,6 +13,7 @@ #include "qemu/osdep.h" #include "qemu/main-loop.h" #include "qemu/plugin.h" +#include "accel/tcg/vcpu-state.h" #include "qemu.h" /* diff --git a/configs/targets/alpha-linux-user.mak b/configs/targets/alpha-linux-user.mak index ef8e365..aa25766 100644 --- a/configs/targets/alpha-linux-user.mak +++ b/configs/targets/alpha-linux-user.mak @@ -2,3 +2,4 @@ TARGET_ARCH=alpha TARGET_SYSTBL_ABI=common TARGET_SYSTBL=syscall.tbl TARGET_LONG_BITS=64 +TARGET_XML_FILES= gdb-xml/alpha-core.xml diff --git a/configs/targets/alpha-softmmu.mak b/configs/targets/alpha-softmmu.mak index 5275076..e31f059 100644 --- a/configs/targets/alpha-softmmu.mak +++ b/configs/targets/alpha-softmmu.mak @@ -1,2 +1,3 @@ TARGET_ARCH=alpha TARGET_LONG_BITS=64 +TARGET_XML_FILES= gdb-xml/alpha-core.xml diff --git a/configs/targets/sparc64-linux-user.mak b/configs/targets/sparc64-linux-user.mak index 64ea04e..7c2ecb7 100644 --- a/configs/targets/sparc64-linux-user.mak +++ b/configs/targets/sparc64-linux-user.mak @@ -4,4 +4,5 @@ TARGET_ABI_DIR=sparc TARGET_SYSTBL_ABI=common,64 TARGET_SYSTBL=syscall.tbl TARGET_BIG_ENDIAN=y +TARGET_XML_FILES=gdb-xml/sparc64-core.xml TARGET_LONG_BITS=64 diff --git a/configs/targets/sparc64-softmmu.mak b/configs/targets/sparc64-softmmu.mak index 2504e31..d9d51d2 100644 --- a/configs/targets/sparc64-softmmu.mak +++ b/configs/targets/sparc64-softmmu.mak @@ -1,4 +1,5 @@ TARGET_ARCH=sparc64 TARGET_BASE_ARCH=sparc TARGET_BIG_ENDIAN=y +TARGET_XML_FILES=gdb-xml/sparc64-core.xml TARGET_LONG_BITS=64 diff --git a/contrib/plugins/execlog.c b/contrib/plugins/execlog.c index d67d010..06ec76d 100644 --- a/contrib/plugins/execlog.c +++ b/contrib/plugins/execlog.c @@ -232,12 +232,15 @@ static void vcpu_tb_trans(qemu_plugin_id_t id, struct qemu_plugin_tb *tb) */ if (disas_assist && rmatches) { check_regs_next = false; - gchar *args = g_strstr_len(insn_disas, -1, " "); - for (int n = 0; n < all_reg_names->len; n++) { - gchar *reg = g_ptr_array_index(all_reg_names, n); - if (g_strrstr(args, reg)) { - check_regs_next = true; - skip = false; + g_auto(GStrv) args = g_strsplit_set(insn_disas, " \t", 2); + if (args && args[1]) { + for (int n = 0; n < all_reg_names->len; n++) { + const gchar *reg = g_ptr_array_index(all_reg_names, n); + if (g_strrstr(args[1], reg)) { + check_regs_next = true; + skip = false; + break; + } } } } diff --git a/cpu-target.c b/cpu-target.c index 1c90a30..20db5ff 100644 --- a/cpu-target.c +++ b/cpu-target.c @@ -22,7 +22,6 @@ #include "system/accel-ops.h" #include "system/cpus.h" #include "exec/cpu-common.h" -#include "exec/tswap.h" #include "exec/replay-core.h" #include "exec/log.h" #include "hw/core/cpu.h" @@ -85,9 +84,3 @@ void cpu_abort(CPUState *cpu, const char *fmt, ...) #endif abort(); } - -#undef target_big_endian -bool target_big_endian(void) -{ - return TARGET_BIG_ENDIAN; -} diff --git a/docs/devel/rust.rst b/docs/devel/rust.rst index dc8c441..b673753 100644 --- a/docs/devel/rust.rst +++ b/docs/devel/rust.rst @@ -351,7 +351,7 @@ Writing procedural macros ''''''''''''''''''''''''' By conventions, procedural macros are split in two functions, one -returning ``Result<proc_macro2::TokenStream, MacroError>`` with the body of +returning ``Result<proc_macro2::TokenStream, syn::Error>`` with the body of the procedural macro, and the second returning ``proc_macro::TokenStream`` which is the actual procedural macro. The former's name is the same as the latter with the ``_or_error`` suffix. The code for the latter is more @@ -361,18 +361,19 @@ from the type after ``as`` in the invocation of ``parse_macro_input!``:: #[proc_macro_derive(Object)] pub fn derive_object(input: TokenStream) -> TokenStream { let input = parse_macro_input!(input as DeriveInput); - let expanded = derive_object_or_error(input).unwrap_or_else(Into::into); - TokenStream::from(expanded) + derive_object_or_error(input) + .unwrap_or_else(syn::Error::into_compile_error) + .into() } The ``qemu_api_macros`` crate has utility functions to examine a ``DeriveInput`` and perform common checks (e.g. looking for a struct -with named fields). These functions return ``Result<..., MacroError>`` +with named fields). These functions return ``Result<..., syn::Error>`` and can be used easily in the procedural macro function:: fn derive_object_or_error(input: DeriveInput) -> - Result<proc_macro2::TokenStream, MacroError> + Result<proc_macro2::TokenStream, Error> { is_c_repr(&input, "#[derive(Object)]")?; diff --git a/docs/devel/testing/main.rst b/docs/devel/testing/main.rst index 6b18ed8..2b5cb0c 100644 --- a/docs/devel/testing/main.rst +++ b/docs/devel/testing/main.rst @@ -604,9 +604,9 @@ below steps to debug it: 2. Add "V=1" to the command line, try again, to see the verbose output. 3. Further add "DEBUG=1" to the command line. This will pause in a shell prompt in the container right before testing starts. You could either manually - build QEMU and run tests from there, or press Ctrl-D to let the Docker + build QEMU and run tests from there, or press :kbd:`Ctrl+d` to let the Docker testing continue. -4. If you press Ctrl-D, the same building and testing procedure will begin, and +4. If you press :kbd:`Ctrl+d`, the same building and testing procedure will begin, and will hopefully run into the error again. After that, you will be dropped to the prompt for debug. diff --git a/docs/devel/tracing.rst b/docs/devel/tracing.rst index 043bed7..f4557ee 100644 --- a/docs/devel/tracing.rst +++ b/docs/devel/tracing.rst @@ -76,7 +76,7 @@ The "io/trace.h" file must be created manually with an #include of the corresponding "trace/trace-<subdir>.h" file that will be generated in the builddir:: - $ echo '#include "trace/trace-io.h"' >io/trace.h + $ (echo '/* SPDX-License-Identifier: GPL-2.0-or-later */' ; echo '#include "trace/trace-io.h"') >io/trace.h While it is possible to include a trace.h file from outside a source file's own sub-directory, this is discouraged in general. It is strongly preferred that diff --git a/docs/interop/firmware.json b/docs/interop/firmware.json index 745d21d..0711b6f 100644 --- a/docs/interop/firmware.json +++ b/docs/interop/firmware.json @@ -57,10 +57,17 @@ # # @memory: The firmware is to be mapped into memory. # +# @igvm: The firmware is defined by a file conforming to the IGVM +# specification and mapped into memory according to directives +# defined in the file. This is similar to @memory but may +# include additional processing defined by the IGVM file +# including initial CPU state or population of metadata into +# the guest address space. Since: 10.1 +# # Since: 3.0 ## { 'enum' : 'FirmwareDevice', - 'data' : [ 'flash', 'kernel', 'memory' ] } + 'data' : [ 'flash', 'kernel', 'memory', 'igvm' ] } ## # @FirmwareArchitecture: @@ -378,6 +385,24 @@ 'data' : { 'filename' : 'str' } } ## +# @FirmwareMappingIgvm: +# +# Describes loading and mapping properties for the firmware executable, +# when @FirmwareDevice is @igvm. +# +# @filename: Identifies the IGVM file containing the firmware executable +# along with other information used to configure the initial +# state of the guest. The IGVM file may be shared by multiple +# virtual machine definitions. This corresponds to creating +# an object on the command line with "-object igvm-cfg, +# file=@filename". +# +# Since: 10.1 +## +{ 'struct' : 'FirmwareMappingIgvm', + 'data' : { 'filename' : 'str' } } + +## # @FirmwareMapping: # # Provides a discriminated structure for firmware to describe its @@ -393,7 +418,8 @@ 'discriminator' : 'device', 'data' : { 'flash' : 'FirmwareMappingFlash', 'kernel' : 'FirmwareMappingKernel', - 'memory' : 'FirmwareMappingMemory' } } + 'memory' : 'FirmwareMappingMemory', + 'igvm' : 'FirmwareMappingIgvm' } } ## # @Firmware: diff --git a/docs/system/devices/net.rst b/docs/system/devices/net.rst index a3efbdc..4d787c3 100644 --- a/docs/system/devices/net.rst +++ b/docs/system/devices/net.rst @@ -85,13 +85,59 @@ passt doesn't require any capability or privilege. passt has better performance than ``-net user``, full IPv6 support and better security as it's a daemon that is not executed in QEMU context. -passt can be connected to QEMU either by using a socket -(``-netdev stream``) or using the vhost-user interface (``-netdev vhost-user``). +passt_ can be used in the same way as the user backend (using ``-net passt``, +``-netdev passt`` or ``-nic passt``) or it can be launched manually and +connected to QEMU either by using a socket (``-netdev stream``) or by using +the vhost-user interface (``-netdev vhost-user``). + +Using ``-netdev stream`` or ``-netdev vhost-user`` will allow the user to +enable functionalities not available through the passt backend interface +(like migration). + See `passt(1)`_ for more details on passt. .. _passt: https://passt.top/ .. _passt(1): https://passt.top/builds/latest/web/passt.1.html +To use the passt backend interface +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +There is no need to start the daemon as QEMU will do it for you. + +By default, passt will be started in the socket-based mode. + +.. parsed-literal:: + |qemu_system| [...OPTIONS...] -nic passt + + (qemu) info network + e1000e.0: index=0,type=nic,model=e1000e,macaddr=52:54:00:12:34:56 + \ #net071: index=0,type=passt,stream,connected to pid 24846 + +.. parsed-literal:: + |qemu_system| [...OPTIONS...] -net nic -net passt,tcp-ports=10001,udp-ports=10001 + + (qemu) info network + hub 0 + \ hub0port1: #net136: index=0,type=passt,stream,connected to pid 25204 + \ hub0port0: e1000e.0: index=0,type=nic,model=e1000e,macaddr=52:54:00:12:34:56 + +.. parsed-literal:: + |qemu_system| [...OPTIONS...] -netdev passt,id=netdev0 -device virtio-net,mac=9a:2b:2c:2d:2e:2f,id=virtio0,netdev=netdev0 + + (qemu) info network + virtio0: index=0,type=nic,model=virtio-net-pci,macaddr=9a:2b:2c:2d:2e:2f + \ netdev0: index=0,type=passt,stream,connected to pid 25428 + +To use the vhost-based interface, add the ``vhost-user=on`` parameter and +select the virtio-net device: + +.. parsed-literal:: + |qemu_system| [...OPTIONS...] -nic passt,model=virtio,vhost-user=on + + (qemu) info network + virtio-net-pci.0: index=0,type=nic,model=virtio-net-pci,macaddr=52:54:00:12:34:56 + \ #net006: index=0,type=passt,vhost-user,connected to pid 25731 + To use socket based passt interface: ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ diff --git a/docs/system/i386/amd-memory-encryption.rst b/docs/system/i386/amd-memory-encryption.rst index 748f509..6c23f35 100644 --- a/docs/system/i386/amd-memory-encryption.rst +++ b/docs/system/i386/amd-memory-encryption.rst @@ -1,3 +1,5 @@ +.. _amd-sev: + AMD Secure Encrypted Virtualization (SEV) ========================================= diff --git a/docs/system/igvm.rst b/docs/system/igvm.rst new file mode 100644 index 0000000..79508d9 --- /dev/null +++ b/docs/system/igvm.rst @@ -0,0 +1,173 @@ +Independent Guest Virtual Machine (IGVM) support +================================================ + +IGVM files are designed to encapsulate all the information required to launch a +virtual machine on any given virtualization stack in a deterministic way. This +allows the cryptographic measurement of initial guest state for Confidential +Guests to be calculated when the IGVM file is built, allowing a relying party to +verify the initial state of a guest via a remote attestation. + +Although IGVM files are designed with Confidential Computing in mind, they can +also be used to configure non-confidential guests. Multiple platforms can be +defined by a single IGVM file, allowing a single IGVM file to configure a +virtual machine that can run on, for example, TDX, SEV and non-confidential +hosts. + +QEMU supports IGVM files through the user-creatable ``igvm-cfg`` object. This +object is used to define the filename of the IGVM file to process. A reference +to the object is added to the ``-machine`` to configure the virtual machine +to use the IGVM file for configuration. + +Confidential platform support is provided through the use of +the ``ConfidentialGuestSupport`` object. If the virtual machine provides an +instance of this object then this is used by the IGVM loader to configure the +isolation properties of the directives within the file. + +Further Information on IGVM +--------------------------- + +Information about the IGVM format, including links to the format specification +and documentation for the Rust and C libraries can be found at the project +repository: + +https://github.com/microsoft/igvm + + +Supported Platforms +------------------- + +Currently, IGVM files can be provided for Confidential Guests on host systems +that support AMD SEV, SEV-ES and SEV-SNP with KVM. IGVM files can also be +provided for non-confidential guests. + + +Limitations when using IGVM with AMD SEV, SEV-ES and SEV-SNP +------------------------------------------------------------ + +IGVM files configure the initial state of the guest using a set of directives. +Not every directive is supported by every Confidential Guest type. For example, +AMD SEV does not support encrypted save state regions, therefore setting the +initial CPU state using IGVM for SEV is not possible. When an IGVM file contains +directives that are not supported for the active platform, an error is generated +and the guest launch is aborted. + +The table below describes the list of directives that are supported for SEV, +SEV-ES, SEV-SNP and non-confidential platforms. + +.. list-table:: SEV, SEV-ES, SEV-SNP & non-confidential Supported Directives + :widths: 35 65 + :header-rows: 1 + + * - IGVM directive + - Notes + * - IGVM_VHT_PAGE_DATA + - ``NORMAL`` zero, measured and unmeasured page types are supported. Other + page types result in an error. + * - IGVM_VHT_PARAMETER_AREA + - + * - IGVM_VHT_PARAMETER_INSERT + - + * - IGVM_VHT_VP_COUNT_PARAMETER + - The guest parameter page is populated with the CPU count. + * - IGVM_VHT_ENVIRONMENT_INFO_PARAMETER + - The ``memory_is_shared`` parameter is set to 1 in the guest parameter + page. + +.. list-table:: Additional SEV, SEV-ES & SEV_SNP Supported Directives + :widths: 25 75 + :header-rows: 1 + + * - IGVM directive + - Notes + * - IGVM_VHT_MEMORY_MAP + - The memory map page is populated using entries from the E820 table. + * - IGVM_VHT_REQUIRED_MEMORY + - Ensures memory is available in the guest at the specified range. + +.. list-table:: Additional SEV-ES & SEV-SNP Supported Directives + :widths: 25 75 + :header-rows: 1 + + * - IGVM directive + - Notes + * - IGVM_VHT_VP_CONTEXT + - Setting of the initial CPU state for the boot CPU and additional CPUs is + supported with limitations on the fields that can be provided in the + VMSA. See below for details on which fields are supported. + +Initial CPU state with VMSA +--------------------------- + +The initial state of guest CPUs can be defined in the IGVM file for AMD SEV-ES +and SEV-SNP. The state data is provided as a VMSA structure as defined in Table +B-4 in the AMD64 Architecture Programmer's Manual, Volume 2 [1]. + +The IGVM VMSA is translated to CPU state in QEMU which is then synchronized +by KVM to the guest VMSA during the launch process where it contributes to the +launch measurement. See :ref:`amd-sev` for details on the launch process and +guest launch measurement. + +It is important that no information is lost or changed when translating the +VMSA provided by the IGVM file into the VSMA that is used to launch the guest. +Therefore, QEMU restricts the VMSA fields that can be provided in the IGVM +VMSA structure to the following registers: + +RAX, RCX, RDX, RBX, RBP, RSI, RDI, R8-R15, RSP, RIP, CS, DS, ES, FS, GS, SS, +CR0, CR3, CR4, XCR0, EFER, PAT, GDT, IDT, LDTR, TR, DR6, DR7, RFLAGS, X87_FCW, +MXCSR. + +When processing the IGVM file, QEMU will check if any fields other than the +above are non-zero and generate an error if this is the case. + +KVM uses a hardcoded GPA of 0xFFFFFFFFF000 for the VMSA. When an IGVM file +defines initial CPU state, the GPA for each VMSA must match this hardcoded +value. + +Firmware Images with IGVM +------------------------- + +When an IGVM filename is specified for a Confidential Guest Support object it +overrides the default handling of system firmware: the firmware image, such as +an OVMF binary should be contained as a payload of the IGVM file and not +provided as a flash drive or via the ``-bios`` parameter. The default QEMU +firmware is not automatically populated into the guest memory space. + +If an IGVM file is provided along with either the ``-bios`` parameter or pflash +devices then an error is displayed and the guest startup is aborted. + +Running a guest configured using IGVM +------------------------------------- + +To run a guest configured with IGVM you firstly need to generate an IGVM file +that contains a guest configuration compatible with the platform you are +targeting. + +The ``buildigvm`` tool [2] is an example of a tool that can be used to generate +IGVM files for non-confidential X86 platforms as well as for SEV, SEV-ES and +SEV-SNP confidential platforms. + +Example using this tool to generate an IGVM file for AMD SEV-SNP:: + + buildigvm --firmware /path/to/OVMF.fd --output sev-snp.igvm \ + --cpucount 4 sev-snp + +To run a guest configured with the generated IGVM you need to add an +``igvm-cfg`` object and refer to it from the ``-machine`` parameter: + +Example (for AMD SEV):: + + qemu-system-x86_64 \ + <other parameters> \ + -machine ...,confidential-guest-support=sev0,igvm-cfg=igvm0 \ + -object sev-guest,id=sev0,cbitpos=47,reduced-phys-bits=1 \ + -object igvm-cfg,id=igvm0,file=/path/to/sev-snp.igvm + +References +---------- + +[1] AMD64 Architecture Programmer's Manual, Volume 2: System Programming + Rev 3.41 + https://www.amd.com/content/dam/amd/en/documents/processor-tech-docs/programmer-references/24593.pdf + +[2] ``buildigvm`` - A tool to build example IGVM files containing OVMF firmware + https://github.com/roy-hopkins/buildigvm
\ No newline at end of file diff --git a/docs/system/images.rst b/docs/system/images.rst index a555117..4370696 100644 --- a/docs/system/images.rst +++ b/docs/system/images.rst @@ -30,7 +30,7 @@ Snapshot mode If you use the option ``-snapshot``, all disk images are considered as read only. When sectors in written, they are written in a temporary file created in ``/tmp``. You can however force the write back to the raw -disk images by using the ``commit`` monitor command (or C-a s in the +disk images by using the ``commit`` monitor command (or :kbd:`Ctrl+a s` in the serial console). .. _vm_005fsnapshots: diff --git a/docs/system/index.rst b/docs/system/index.rst index 718e9d3..427b020 100644 --- a/docs/system/index.rst +++ b/docs/system/index.rst @@ -38,5 +38,6 @@ or Hypervisor.Framework. security multi-process confidential-guest-support + igvm vm-templating sriov diff --git a/docs/system/introduction.rst b/docs/system/introduction.rst index 338d374..4cd46b5 100644 --- a/docs/system/introduction.rst +++ b/docs/system/introduction.rst @@ -81,7 +81,7 @@ may not be optimal for modern systems. For a non-x86 system where we emulate a broad range of machine types, the command lines are generally more explicit in defining the machine -and boot behaviour. You will find often find example command lines in +and boot behaviour. You will often find example command lines in the :ref:`system-targets-ref` section of the manual. While the project doesn't want to discourage users from using the diff --git a/docs/system/keys.rst.inc b/docs/system/keys.rst.inc index 59966a3..c28ae1a 100644 --- a/docs/system/keys.rst.inc +++ b/docs/system/keys.rst.inc @@ -1,36 +1,37 @@ During the graphical emulation, you can use special key combinations from -the following table to change modes. By default the modifier is Ctrl-Alt +the following table to change modes. By default the modifier is :kbd:`Ctrl+Alt` (used in the table below) which can be changed with ``-display`` suboption ``mod=`` where appropriate. For example, ``-display sdl, -grab-mod=lshift-lctrl-lalt`` changes the modifier key to Ctrl-Alt-Shift, -while ``-display sdl,grab-mod=rctrl`` changes it to the right Ctrl key. +grab-mod=lshift-lctrl-lalt`` changes the modifier key to :kbd:`Ctrl+Alt+Shift`, +while ``-display sdl,grab-mod=rctrl`` changes it to the right :kbd:`Ctrl` key. -Ctrl-Alt-f - Toggle full screen +.. list-table:: Multiplexer Keys + :widths: 10 90 + :header-rows: 1 -Ctrl-Alt-+ - Enlarge the screen + * - Key Sequence + - Action -Ctrl-Alt\-- - Shrink the screen + * - :kbd:`Ctrl+Alt+f` + - Toggle full screen -Ctrl-Alt-u - Restore the screen's un-scaled dimensions + * - :kbd:`Ctrl+Alt++` + - Enlarge the screen -Ctrl-Alt-n - Switch to virtual console 'n'. Standard console mappings are: + * - :kbd:`Ctrl+Alt+-` + - Shrink the screen - *1* - Target system display + * - :kbd:`Ctrl+Alt+u` + - Restore the screen's un-scaled dimensions - *2* - Monitor + * - :kbd:`Ctrl+Alt+n` + - Switch to virtual console 'n'. Standard console mappings are: - *3* - Serial port + - *1*: Target system display + - *2*: Monitor + - *3*: Serial port + * - :kbd:`Ctrl+Alt+g` + - Toggle mouse and keyboard grab. -Ctrl-Alt-g - Toggle mouse and keyboard grab. - -In the virtual consoles, you can use Ctrl-Up, Ctrl-Down, Ctrl-PageUp and -Ctrl-PageDown to move in the back log. +In the virtual consoles, you can use :kbd:`Ctrl+Up`, :kbd:`Ctrl+Down`, :kbd:`Ctrl+PageUp` and +:kbd:`Ctrl+PageDown` to move in the back log. diff --git a/docs/system/linuxboot.rst b/docs/system/linuxboot.rst index 2328b4a..f7573ab 100644 --- a/docs/system/linuxboot.rst +++ b/docs/system/linuxboot.rst @@ -26,5 +26,5 @@ virtual serial port and the QEMU monitor to the console with the |qemu_system| -kernel bzImage -drive file=rootdisk.img,format=raw \ -append "root=/dev/sda console=ttyS0" -nographic -Use Ctrl-a c to switch between the serial console and the monitor (see +Use :kbd:`Ctrl+a c` to switch between the serial console and the monitor (see :ref:`GUI_keys`). diff --git a/docs/system/mux-chardev.rst.inc b/docs/system/mux-chardev.rst.inc index 84ea12c..c87ba31 100644 --- a/docs/system/mux-chardev.rst.inc +++ b/docs/system/mux-chardev.rst.inc @@ -1,27 +1,33 @@ During emulation, if you are using a character backend multiplexer (which is the default if you are using ``-nographic``) then several commands are available via an escape sequence. These key sequences all -start with an escape character, which is Ctrl-a by default, but can be +start with an escape character, which is :kbd:`Ctrl+a` by default, but can be changed with ``-echr``. The list below assumes you're using the default. -Ctrl-a h - Print this help +.. list-table:: Multiplexer Keys + :widths: 20 80 + :header-rows: 1 -Ctrl-a x - Exit emulator + * - Key Sequence + - Action -Ctrl-a s - Save disk data back to file (if -snapshot) + * - :kbd:`Ctrl+a h` + - Print this help -Ctrl-a t - Toggle console timestamps + * - :kbd:`Ctrl+a x` + - Exit emulator -Ctrl-a b - Send break (magic sysrq in Linux) + * - :kbd:`Ctrl+a s` + - Save disk data back to file (if -snapshot) -Ctrl-a c - Rotate between the frontends connected to the multiplexer (usually - this switches between the monitor and the console) + * - :kbd:`Ctrl+a t` + - Toggle console timestamps -Ctrl-a Ctrl-a - Send the escape character to the frontend + * - :kbd:`Ctrl+a b` + - Send break (magic sysrq in Linux) + + * - :kbd:`Ctrl+a c` + - Rotate between the frontends connected to the multiplexer (usually this switches between the monitor and the console) + + * - :kbd:`Ctrl+a Ctrl+a` + - Send the escape character to the frontend diff --git a/docs/system/target-i386.rst b/docs/system/target-i386.rst index 43b09c7..2374391 100644 --- a/docs/system/target-i386.rst +++ b/docs/system/target-i386.rst @@ -37,6 +37,4 @@ OS requirements ~~~~~~~~~~~~~~~ On x86_64 hosts, the default set of CPU features enabled by the KVM -accelerator require the host to be running Linux v4.5 or newer. Red Hat -Enterprise Linux 7 is also supported, since the required -functionality was backported. +accelerator require the host to be running Linux v4.5 or newer. diff --git a/docs/system/virtio-net-failover.rst b/docs/system/virtio-net-failover.rst index 6002dc5..0cc4654 100644 --- a/docs/system/virtio-net-failover.rst +++ b/docs/system/virtio-net-failover.rst @@ -26,43 +26,48 @@ and standby devices are not plugged into the same PCIe slot. Usecase ------- - Virtio-net standby allows easy migration while using a passed-through fast - networking device by falling back to a virtio-net device for the duration of - the migration. It is like a simple version of a bond, the difference is that it - requires no configuration in the guest. When a guest is live-migrated to - another host QEMU will unplug the primary device via the PCIe based hotplug - handler and traffic will go through the virtio-net device. On the target - system the primary device will be automatically plugged back and the - net_failover module registers it again as the primary device. +Virtio-net standby allows easy migration while using a passed-through +fast networking device by falling back to a virtio-net device for the +duration of the migration. It is like a simple version of a bond, the +difference is that it requires no configuration in the guest. When a +guest is live-migrated to another host QEMU will unplug the primary +device via the PCIe based hotplug handler and traffic will go through +the virtio-net device. On the target system the primary device will be +automatically plugged back and the net_failover module registers it +again as the primary device. Usage ----- - The primary device can be hotplugged or be part of the startup configuration +The primary device can be hotplugged or be part of the startup configuration - -device virtio-net-pci,netdev=hostnet1,id=net1,mac=52:54:00:6f:55:cc, \ - bus=root2,failover=on +.. code-block:: shell - With the parameter failover=on the VIRTIO_NET_F_STANDBY feature will be enabled. + -device virtio-net-pci,netdev=hostnet1,id=net1,mac=52:54:00:6f:55:cc,bus=root2,failover=on + +With the parameter ``failover=on`` the VIRTIO_NET_F_STANDBY feature will be enabled. + +.. code-block:: shell -device vfio-pci,host=5e:00.2,id=hostdev0,bus=root1,failover_pair_id=net1 - failover_pair_id references the id of the virtio-net standby device. This - is only for pairing the devices within QEMU. The guest kernel module - net_failover will match devices with identical MAC addresses. +``failover_pair_id`` references the id of the virtio-net standby device. +This is only for pairing the devices within QEMU. The guest kernel +module net_failover will match devices with identical MAC addresses. Hotplug ------- - Both primary and standby device can be hotplugged via the QEMU monitor. Note - that if the virtio-net device is plugged first a warning will be issued that it - couldn't find the primary device. +Both primary and standby device can be hotplugged via the QEMU +monitor. Note that if the virtio-net device is plugged first a warning +will be issued that it couldn't find the primary device. Migration --------- - A new migration state wait-unplug was added for this feature. If failover primary - devices are present in the configuration, migration will go into this state. - It will wait until the device unplug is completed in the guest and then move into - active state. On the target system the primary devices will be automatically hotplugged - when the feature bit was negotiated for the virtio-net standby device. +A new migration state wait-unplug was added for this feature. If +failover primary devices are present in the configuration, migration +will go into this state. It will wait until the device unplug is +completed in the guest and then move into active state. On the target +system the primary devices will be automatically hotplugged when the +feature bit was negotiated for the virtio-net standby device. diff --git a/gdb-xml/alpha-core.xml b/gdb-xml/alpha-core.xml new file mode 100644 index 0000000..c9e12f4 --- /dev/null +++ b/gdb-xml/alpha-core.xml @@ -0,0 +1,136 @@ +<?xml version="1.0"?> +<!-- Copyright (C) 2025 Free Software Foundation, Inc. + + Copying and distribution of this file, with or without modification, + are permitted in any medium without royalty provided the copyright + notice and this notice are preserved. --> + +<!DOCTYPE feature SYSTEM "gdb-target.dtd"> +<feature name="org.gnu.gdb.alpha.core"> + <!-- IEEE rounding mode values --> + <enum id="dyn_rm_enum" size="8"> + <!-- Chopped rounding mode --> + <evalue name="chop" value="0"/> + <!-- Minus infinity --> + <evalue name="-inf" value="1"/> + <!-- Normal rounding --> + <evalue name="norm" value="2"/> + <!-- Plus infinity --> + <evalue name="+inf" value="3"/> + </enum> + + <!-- Floating-Point Control Register Flags --> + <flags id="fpcr_flags" size="8"> + <!-- Denormal Operand Exception Disable --> + <field name="DNOD" start="47" end="47"/> + <!-- Denormal Operands to Zero --> + <field name="DNZ" start="48" end="48"/> + <!-- Invalid Operation Disable --> + <field name="INVD" start="49" end="49"/> + <!-- Division by Zero Disable --> + <field name="DZED" start="50" end="50"/> + <!-- Overflow Disable --> + <field name="OVFD" start="51" end="51"/> + <!-- Invalid Operation --> + <field name="INV" start="52" end="52"/> + <!-- Division by Zero --> + <field name="DZE" start="53" end="53"/> + <!-- Overflow --> + <field name="OVF" start="54" end="54"/> + <!-- Underflow --> + <field name="UNF" start="55" end="55"/> + <!-- Inexact Result --> + <field name="INE" start="56" end="56"/> + <!-- Integer Overflow --> + <field name="IOV" start="57" end="57"/> + <!-- Dynamic Rounding Mode --> + <field name="DYN_RM" start="58" end="59" type="dyn_rm_enum"/> + <!-- Underflow to Zero --> + <field name="UNDZ" start="60" end="60"/> + <!-- Underflow Disable --> + <field name="UNFD" start="61" end="61"/> + <!-- Inexact Disable --> + <field name="INED" start="62" end="62"/> + <!-- Summary Bit --> + <field name="SUM" start="63" end="63"/> + </flags> + + <!-- Integer Registers --> + <reg name="v0" bitsize="64" type="int64"/> + <reg name="t0" bitsize="64" type="int64"/> + <reg name="t1" bitsize="64" type="int64"/> + <reg name="t2" bitsize="64" type="int64"/> + <reg name="t3" bitsize="64" type="int64"/> + <reg name="t4" bitsize="64" type="int64"/> + <reg name="t5" bitsize="64" type="int64"/> + <reg name="t6" bitsize="64" type="int64"/> + <reg name="t7" bitsize="64" type="int64"/> + <reg name="s0" bitsize="64" type="int64"/> + <reg name="s1" bitsize="64" type="int64"/> + <reg name="s2" bitsize="64" type="int64"/> + <reg name="s3" bitsize="64" type="int64"/> + <reg name="s4" bitsize="64" type="int64"/> + <reg name="s5" bitsize="64" type="int64"/> + <reg name="fp" bitsize="64" type="int64"/> + <reg name="a0" bitsize="64" type="int64"/> + <reg name="a1" bitsize="64" type="int64"/> + <reg name="a2" bitsize="64" type="int64"/> + <reg name="a3" bitsize="64" type="int64"/> + <reg name="a4" bitsize="64" type="int64"/> + <reg name="a5" bitsize="64" type="int64"/> + <reg name="t8" bitsize="64" type="int64"/> + <reg name="t9" bitsize="64" type="int64"/> + <reg name="t10" bitsize="64" type="int64"/> + <reg name="t11" bitsize="64" type="int64"/> + <reg name="ra" bitsize="64" type="int64"/> + <reg name="t12" bitsize="64" type="int64"/> + <reg name="at" bitsize="64" type="int64"/> + <reg name="gp" bitsize="64" type="data_ptr"/> + <reg name="sp" bitsize="64" type="data_ptr"/> + <reg name="zero" bitsize="64" type="int64" save-restore="no"/> + + <!-- Floating-Point Registers --> + <reg name="f0" bitsize="64" type="float" group="float"/> + <reg name="f1" bitsize="64" type="float" group="float"/> + <reg name="f2" bitsize="64" type="float" group="float"/> + <reg name="f3" bitsize="64" type="float" group="float"/> + <reg name="f4" bitsize="64" type="float" group="float"/> + <reg name="f5" bitsize="64" type="float" group="float"/> + <reg name="f6" bitsize="64" type="float" group="float"/> + <reg name="f7" bitsize="64" type="float" group="float"/> + <reg name="f8" bitsize="64" type="float" group="float"/> + <reg name="f9" bitsize="64" type="float" group="float"/> + <reg name="f10" bitsize="64" type="float" group="float"/> + <reg name="f11" bitsize="64" type="float" group="float"/> + <reg name="f12" bitsize="64" type="float" group="float"/> + <reg name="f13" bitsize="64" type="float" group="float"/> + <reg name="f14" bitsize="64" type="float" group="float"/> + <reg name="f15" bitsize="64" type="float" group="float"/> + <reg name="f16" bitsize="64" type="float" group="float"/> + <reg name="f17" bitsize="64" type="float" group="float"/> + <reg name="f18" bitsize="64" type="float" group="float"/> + <reg name="f19" bitsize="64" type="float" group="float"/> + <reg name="f20" bitsize="64" type="float" group="float"/> + <reg name="f21" bitsize="64" type="float" group="float"/> + <reg name="f22" bitsize="64" type="float" group="float"/> + <reg name="f23" bitsize="64" type="float" group="float"/> + <reg name="f24" bitsize="64" type="float" group="float"/> + <reg name="f25" bitsize="64" type="float" group="float"/> + <reg name="f26" bitsize="64" type="float" group="float"/> + <reg name="f27" bitsize="64" type="float" group="float"/> + <reg name="f28" bitsize="64" type="float" group="float"/> + <reg name="f29" bitsize="64" type="float" group="float"/> + <reg name="f30" bitsize="64" type="float" group="float"/> + + <!-- Floating-Point Control Register --> + <reg name="fpcr" bitsize="64" type="fpcr_flags" group="float"/> + + <!-- Program Counter --> + <reg name="pc" bitsize="64" type="code_ptr"/> + + <!-- Reserved Index for Former Virtual Register --> + <reg name="" bitsize="64" type="int64" save-restore="no"/> + + <!-- PALcode Memory Slot --> + <reg name="unique" bitsize="64" type="int64" group="system"/> +</feature> diff --git a/gdb-xml/sparc64-core.xml b/gdb-xml/sparc64-core.xml new file mode 100644 index 0000000..375b9bb --- /dev/null +++ b/gdb-xml/sparc64-core.xml @@ -0,0 +1,99 @@ +<?xml version="1.0"?> +<!-- Copyright (C) 2013-2025 Free Software Foundation, Inc. + + Copying and distribution of this file, with or without modification, + are permitted in any medium without royalty provided the copyright + notice and this notice are preserved. --> + +<!DOCTYPE feature SYSTEM "gdb-target.dtd"> +<feature name="org.gnu.gdb.sparc.core"> + <reg name="g0" bitsize="64" type="uint64" regnum="0"/> + <reg name="g1" bitsize="64" type="uint64" regnum="1"/> + <reg name="g2" bitsize="64" type="uint64" regnum="2"/> + <reg name="g3" bitsize="64" type="uint64" regnum="3"/> + <reg name="g4" bitsize="64" type="uint64" regnum="4"/> + <reg name="g5" bitsize="64" type="uint64" regnum="5"/> + <reg name="g6" bitsize="64" type="uint64" regnum="6"/> + <reg name="g7" bitsize="64" type="uint64" regnum="7"/> + <reg name="o0" bitsize="64" type="uint64" regnum="8"/> + <reg name="o1" bitsize="64" type="uint64" regnum="9"/> + <reg name="o2" bitsize="64" type="uint64" regnum="10"/> + <reg name="o3" bitsize="64" type="uint64" regnum="11"/> + <reg name="o4" bitsize="64" type="uint64" regnum="12"/> + <reg name="o5" bitsize="64" type="uint64" regnum="13"/> + <reg name="sp" bitsize="64" type="uint64" regnum="14"/> + <reg name="o7" bitsize="64" type="uint64" regnum="15"/> + <reg name="l0" bitsize="64" type="uint64" regnum="16"/> + <reg name="l1" bitsize="64" type="uint64" regnum="17"/> + <reg name="l2" bitsize="64" type="uint64" regnum="18"/> + <reg name="l3" bitsize="64" type="uint64" regnum="19"/> + <reg name="l4" bitsize="64" type="uint64" regnum="20"/> + <reg name="l5" bitsize="64" type="uint64" regnum="21"/> + <reg name="l6" bitsize="64" type="uint64" regnum="22"/> + <reg name="l7" bitsize="64" type="uint64" regnum="23"/> + <reg name="i0" bitsize="64" type="uint64" regnum="24"/> + <reg name="i1" bitsize="64" type="uint64" regnum="25"/> + <reg name="i2" bitsize="64" type="uint64" regnum="26"/> + <reg name="i3" bitsize="64" type="uint64" regnum="27"/> + <reg name="i4" bitsize="64" type="uint64" regnum="28"/> + <reg name="i5" bitsize="64" type="uint64" regnum="29"/> + <reg name="fp" bitsize="64" type="uint64" regnum="30"/> + <reg name="i7" bitsize="64" type="uint64" regnum="31"/> + + <reg name="f0" bitsize="32" type="ieee_single" regnum="32"/> + <reg name="f1" bitsize="32" type="ieee_single" regnum="33"/> + <reg name="f2" bitsize="32" type="ieee_single" regnum="34"/> + <reg name="f3" bitsize="32" type="ieee_single" regnum="35"/> + <reg name="f4" bitsize="32" type="ieee_single" regnum="36"/> + <reg name="f5" bitsize="32" type="ieee_single" regnum="37"/> + <reg name="f6" bitsize="32" type="ieee_single" regnum="38"/> + <reg name="f7" bitsize="32" type="ieee_single" regnum="39"/> + <reg name="f8" bitsize="32" type="ieee_single" regnum="40"/> + <reg name="f9" bitsize="32" type="ieee_single" regnum="41"/> + <reg name="f10" bitsize="32" type="ieee_single" regnum="42"/> + <reg name="f11" bitsize="32" type="ieee_single" regnum="43"/> + <reg name="f12" bitsize="32" type="ieee_single" regnum="44"/> + <reg name="f13" bitsize="32" type="ieee_single" regnum="45"/> + <reg name="f14" bitsize="32" type="ieee_single" regnum="46"/> + <reg name="f15" bitsize="32" type="ieee_single" regnum="47"/> + <reg name="f16" bitsize="32" type="ieee_single" regnum="48"/> + <reg name="f17" bitsize="32" type="ieee_single" regnum="49"/> + <reg name="f18" bitsize="32" type="ieee_single" regnum="50"/> + <reg name="f19" bitsize="32" type="ieee_single" regnum="51"/> + <reg name="f20" bitsize="32" type="ieee_single" regnum="52"/> + <reg name="f21" bitsize="32" type="ieee_single" regnum="53"/> + <reg name="f22" bitsize="32" type="ieee_single" regnum="54"/> + <reg name="f23" bitsize="32" type="ieee_single" regnum="55"/> + <reg name="f24" bitsize="32" type="ieee_single" regnum="56"/> + <reg name="f25" bitsize="32" type="ieee_single" regnum="57"/> + <reg name="f26" bitsize="32" type="ieee_single" regnum="58"/> + <reg name="f27" bitsize="32" type="ieee_single" regnum="59"/> + <reg name="f28" bitsize="32" type="ieee_single" regnum="60"/> + <reg name="f29" bitsize="32" type="ieee_single" regnum="61"/> + <reg name="f30" bitsize="32" type="ieee_single" regnum="62"/> + <reg name="f31" bitsize="32" type="ieee_single" regnum="63"/> + + <reg name="f32" bitsize="64" type="ieee_double" regnum="64"/> + <reg name="f34" bitsize="64" type="ieee_double" regnum="65"/> + <reg name="f36" bitsize="64" type="ieee_double" regnum="66"/> + <reg name="f38" bitsize="64" type="ieee_double" regnum="67"/> + <reg name="f40" bitsize="64" type="ieee_double" regnum="68"/> + <reg name="f42" bitsize="64" type="ieee_double" regnum="69"/> + <reg name="f44" bitsize="64" type="ieee_double" regnum="70"/> + <reg name="f46" bitsize="64" type="ieee_double" regnum="71"/> + <reg name="f48" bitsize="64" type="ieee_double" regnum="72"/> + <reg name="f50" bitsize="64" type="ieee_double" regnum="73"/> + <reg name="f52" bitsize="64" type="ieee_double" regnum="74"/> + <reg name="f54" bitsize="64" type="ieee_double" regnum="75"/> + <reg name="f56" bitsize="64" type="ieee_double" regnum="76"/> + <reg name="f58" bitsize="64" type="ieee_double" regnum="77"/> + <reg name="f60" bitsize="64" type="ieee_double" regnum="78"/> + <reg name="f62" bitsize="64" type="ieee_double" regnum="79"/> + + <reg name="pc" bitsize="64" type="code_ptr" regnum="80"/> + <reg name="npc" bitsize="64" type="code_ptr" regnum="81"/> + <reg name="state" bitsize="64" type="uint64" regnum="82"/> + <reg name="fsr" bitsize="64" type="uint64" regnum="83"/> + <reg name="fprs" bitsize="64" type="uint64" regnum="84"/> + <reg name="y" bitsize="64" type="uint64" regnum="85"/> +</feature> diff --git a/hmp-commands.hx b/hmp-commands.hx index 06746f0..d0e4f35 100644 --- a/hmp-commands.hx +++ b/hmp-commands.hx @@ -1287,6 +1287,9 @@ ERST .name = "netdev_add", .args_type = "netdev:O", .params = "[user|tap|socket|stream|dgram|vde|bridge|hubport|netmap|vhost-user" +#ifdef CONFIG_PASST + "|passt" +#endif #ifdef CONFIG_AF_XDP "|af-xdp" #endif diff --git a/hw/acpi/acpi-pci-hotplug-stub.c b/hw/acpi/acpi-pci-hotplug-stub.c index b7bc6e4..d58ea72 100644 --- a/hw/acpi/acpi-pci-hotplug-stub.c +++ b/hw/acpi/acpi-pci-hotplug-stub.c @@ -4,7 +4,7 @@ const VMStateDescription vmstate_acpi_pcihp_pci_status; -void acpi_pcihp_init(Object *owner, AcpiPciHpState *s, PCIBus *root_bus, +void acpi_pcihp_init(Object *owner, AcpiPciHpState *s, MemoryRegion *address_space_io, uint16_t io_base) { } diff --git a/hw/acpi/aml-build.c b/hw/acpi/aml-build.c index f8f93a9..1e685f9 100644 --- a/hw/acpi/aml-build.c +++ b/hw/acpi/aml-build.c @@ -160,7 +160,7 @@ void crs_replace_with_free_ranges(GPtrArray *ranges, */ static void crs_range_merge(GPtrArray *range) { - GPtrArray *tmp = g_ptr_array_new_with_free_func(crs_range_free); + g_autoptr(GPtrArray) tmp = g_ptr_array_new_with_free_func(crs_range_free); CrsRangeEntry *entry; uint64_t range_base, range_limit; int i; @@ -191,7 +191,6 @@ static void crs_range_merge(GPtrArray *range) entry = g_ptr_array_index(tmp, i); crs_range_insert(range, entry->base, entry->limit); } - g_ptr_array_free(tmp, true); } static void @@ -2153,6 +2152,7 @@ void build_pptt(GArray *table_data, BIOSLinker *linker, MachineState *ms, int64_t socket_id = -1, cluster_id = -1, core_id = -1; uint32_t socket_offset = 0, cluster_offset = 0, core_offset = 0; uint32_t pptt_start = table_data->len; + uint32_t root_offset; int n; AcpiTable table = { .sig = "PPTT", .rev = 2, .oem_id = oem_id, .oem_table_id = oem_table_id }; @@ -2160,6 +2160,18 @@ void build_pptt(GArray *table_data, BIOSLinker *linker, MachineState *ms, acpi_table_begin(&table, table_data); /* + * Build a root node for all the processor nodes. Otherwise when + * building a multi-socket system each socket tree is separated + * and will be hard for the OS like Linux to know whether the + * system is homogeneous. + */ + root_offset = table_data->len - pptt_start; + build_processor_hierarchy_node(table_data, + (1 << 0) | /* Physical package */ + (1 << 4), /* Identical Implementation */ + 0, 0, NULL, 0); + + /* * This works with the assumption that cpus[n].props.*_id has been * sorted from top to down levels in mc->possible_cpu_arch_ids(). * Otherwise, the unexpected and duplicated containers will be @@ -2173,8 +2185,9 @@ void build_pptt(GArray *table_data, BIOSLinker *linker, MachineState *ms, core_id = -1; socket_offset = table_data->len - pptt_start; build_processor_hierarchy_node(table_data, - (1 << 0), /* Physical package */ - 0, socket_id, NULL, 0); + (1 << 0) | /* Physical package */ + (1 << 4), /* Identical Implementation */ + root_offset, socket_id, NULL, 0); } if (mc->smp_props.clusters_supported && mc->smp_props.has_clusters) { @@ -2184,7 +2197,8 @@ void build_pptt(GArray *table_data, BIOSLinker *linker, MachineState *ms, core_id = -1; cluster_offset = table_data->len - pptt_start; build_processor_hierarchy_node(table_data, - (0 << 0), /* Not a physical package */ + (0 << 0) | /* Not a physical package */ + (1 << 4), /* Identical Implementation */ socket_offset, cluster_id, NULL, 0); } } else { @@ -2202,7 +2216,8 @@ void build_pptt(GArray *table_data, BIOSLinker *linker, MachineState *ms, core_id = cpus->cpus[n].props.core_id; core_offset = table_data->len - pptt_start; build_processor_hierarchy_node(table_data, - (0 << 0), /* Not a physical package */ + (0 << 0) | /* Not a physical package */ + (1 << 4), /* Identical Implementation */ cluster_offset, core_id, NULL, 0); } diff --git a/hw/acpi/bios-linker-loader.c b/hw/acpi/bios-linker-loader.c index 1080618..c9ffe44 100644 --- a/hw/acpi/bios-linker-loader.c +++ b/hw/acpi/bios-linker-loader.c @@ -22,8 +22,6 @@ #include "hw/acpi/bios-linker-loader.h" #include "hw/nvram/fw_cfg.h" -#include "qemu/bswap.h" - /* * Linker/loader is a paravirtualized interface that passes commands to guest. * The commands can be used to request guest to diff --git a/hw/acpi/generic_event_device.c b/hw/acpi/generic_event_device.c index 7a62f8d..95682b7 100644 --- a/hw/acpi/generic_event_device.c +++ b/hw/acpi/generic_event_device.c @@ -12,10 +12,13 @@ #include "qemu/osdep.h" #include "qapi/error.h" #include "hw/acpi/acpi.h" +#include "hw/acpi/pcihp.h" #include "hw/acpi/generic_event_device.h" +#include "hw/pci/pci.h" #include "hw/irq.h" #include "hw/mem/pc-dimm.h" #include "hw/mem/nvdimm.h" +#include "hw/pci/pci_device.h" #include "hw/qdev-properties.h" #include "migration/vmstate.h" #include "qemu/error-report.h" @@ -26,6 +29,7 @@ static const uint32_t ged_supported_events[] = { ACPI_GED_PWR_DOWN_EVT, ACPI_GED_NVDIMM_HOTPLUG_EVT, ACPI_GED_CPU_HOTPLUG_EVT, + ACPI_GED_PCI_HOTPLUG_EVT, }; /* @@ -121,6 +125,12 @@ void build_ged_aml(Aml *table, const char *name, HotplugHandler *hotplug_dev, aml_notify(aml_name("\\_SB.NVDR"), aml_int(0x80))); break; + case ACPI_GED_PCI_HOTPLUG_EVT: + aml_append(if_ctx, + aml_acquire(aml_name("\\_SB.PCI0.BLCK"), 0xFFFF)); + aml_append(if_ctx, aml_call0("\\_SB.PCI0.PCNT")); + aml_append(if_ctx, aml_release(aml_name("\\_SB.PCI0.BLCK"))); + break; default: /* * Please make sure all the events in ged_supported_events[] @@ -227,6 +237,14 @@ static const MemoryRegionOps ged_regs_ops = { }, }; +static void acpi_ged_device_pre_plug_cb(HotplugHandler *hotplug_dev, + DeviceState *dev, Error **errp) +{ + if (object_dynamic_cast(OBJECT(dev), TYPE_PCI_DEVICE)) { + acpi_pcihp_device_pre_plug_cb(hotplug_dev, dev, errp); + } +} + static void acpi_ged_device_plug_cb(HotplugHandler *hotplug_dev, DeviceState *dev, Error **errp) { @@ -240,6 +258,8 @@ static void acpi_ged_device_plug_cb(HotplugHandler *hotplug_dev, } } else if (object_dynamic_cast(OBJECT(dev), TYPE_CPU)) { acpi_cpu_plug_cb(hotplug_dev, &s->cpuhp_state, dev, errp); + } else if (object_dynamic_cast(OBJECT(dev), TYPE_PCI_DEVICE)) { + acpi_pcihp_device_plug_cb(hotplug_dev, &s->pcihp_state, dev, errp); } else { error_setg(errp, "virt: device plug request for unsupported device" " type: %s", object_get_typename(OBJECT(dev))); @@ -256,6 +276,9 @@ static void acpi_ged_unplug_request_cb(HotplugHandler *hotplug_dev, acpi_memory_unplug_request_cb(hotplug_dev, &s->memhp_state, dev, errp); } else if (object_dynamic_cast(OBJECT(dev), TYPE_CPU)) { acpi_cpu_unplug_request_cb(hotplug_dev, &s->cpuhp_state, dev, errp); + } else if (object_dynamic_cast(OBJECT(dev), TYPE_PCI_DEVICE)) { + acpi_pcihp_device_unplug_request_cb(hotplug_dev, &s->pcihp_state, + dev, errp); } else { error_setg(errp, "acpi: device unplug request for unsupported device" " type: %s", object_get_typename(OBJECT(dev))); @@ -271,6 +294,8 @@ static void acpi_ged_unplug_cb(HotplugHandler *hotplug_dev, acpi_memory_unplug_cb(&s->memhp_state, dev, errp); } else if (object_dynamic_cast(OBJECT(dev), TYPE_CPU)) { acpi_cpu_unplug_cb(&s->cpuhp_state, dev, errp); + } else if (object_dynamic_cast(OBJECT(dev), TYPE_PCI_DEVICE)) { + acpi_pcihp_device_unplug_cb(hotplug_dev, &s->pcihp_state, dev, errp); } else { error_setg(errp, "acpi: device unplug for unsupported device" " type: %s", object_get_typename(OBJECT(dev))); @@ -299,6 +324,8 @@ static void acpi_ged_send_event(AcpiDeviceIf *adev, AcpiEventStatusBits ev) sel = ACPI_GED_NVDIMM_HOTPLUG_EVT; } else if (ev & ACPI_CPU_HOTPLUG_STATUS) { sel = ACPI_GED_CPU_HOTPLUG_EVT; + } else if (ev & ACPI_PCI_HOTPLUG_STATUS) { + sel = ACPI_GED_PCI_HOTPLUG_EVT; } else { /* Unknown event. Return without generating interrupt. */ warn_report("GED: Unsupported event %d. No irq injected", ev); @@ -318,6 +345,10 @@ static void acpi_ged_send_event(AcpiDeviceIf *adev, AcpiEventStatusBits ev) static const Property acpi_ged_properties[] = { DEFINE_PROP_UINT32("ged-event", AcpiGedState, ged_event_bitmap, 0), + DEFINE_PROP_BOOL(ACPI_PM_PROP_ACPI_PCIHP_BRIDGE, AcpiGedState, + pcihp_state.use_acpi_hotplug_bridge, 0), + DEFINE_PROP_LINK("bus", AcpiGedState, pcihp_state.root, + TYPE_PCI_BUS, PCIBus *), }; static const VMStateDescription vmstate_memhp_state = { @@ -386,6 +417,25 @@ static const VMStateDescription vmstate_ghes_state = { } }; +static bool pcihp_needed(void *opaque) +{ + AcpiGedState *s = opaque; + return s->pcihp_state.use_acpi_hotplug_bridge; +} + +static const VMStateDescription vmstate_pcihp_state = { + .name = "acpi-ged/pcihp", + .version_id = 1, + .minimum_version_id = 1, + .needed = pcihp_needed, + .fields = (const VMStateField[]) { + VMSTATE_PCI_HOTPLUG(pcihp_state, + AcpiGedState, + NULL, NULL), + VMSTATE_END_OF_LIST() + } +}; + static const VMStateDescription vmstate_acpi_ged = { .name = "acpi-ged", .version_id = 1, @@ -398,6 +448,7 @@ static const VMStateDescription vmstate_acpi_ged = { &vmstate_memhp_state, &vmstate_cpuhp_state, &vmstate_ghes_state, + &vmstate_pcihp_state, NULL } }; @@ -406,9 +457,13 @@ static void acpi_ged_realize(DeviceState *dev, Error **errp) { SysBusDevice *sbd = SYS_BUS_DEVICE(dev); AcpiGedState *s = ACPI_GED(dev); + AcpiPciHpState *pcihp_state = &s->pcihp_state; uint32_t ged_events; int i; + if (pcihp_state->use_acpi_hotplug_bridge) { + s->ged_event_bitmap |= ACPI_GED_PCI_HOTPLUG_EVT; + } ged_events = ctpop32(s->ged_event_bitmap); for (i = 0; i < ARRAY_SIZE(ged_supported_events) && ged_events; i++) { @@ -428,6 +483,13 @@ static void acpi_ged_realize(DeviceState *dev, Error **errp) cpu_hotplug_hw_init(&s->container_cpuhp, OBJECT(dev), &s->cpuhp_state, 0); break; + case ACPI_GED_PCI_HOTPLUG_EVT: + memory_region_init(&s->container_pcihp, OBJECT(dev), + ACPI_PCIHP_REGION_NAME, ACPI_PCIHP_SIZE); + sysbus_init_mmio(sbd, &s->container_pcihp); + acpi_pcihp_init(OBJECT(s), &s->pcihp_state, + &s->container_pcihp, 0); + qbus_set_hotplug_handler(BUS(s->pcihp_state.root), OBJECT(dev)); } ged_events--; } @@ -469,20 +531,34 @@ static void acpi_ged_initfn(Object *obj) sysbus_init_mmio(sbd, &ged_st->regs); } +static void ged_reset_hold(Object *obj, ResetType type) +{ + AcpiGedState *s = ACPI_GED(obj); + + if (s->pcihp_state.use_acpi_hotplug_bridge) { + acpi_pcihp_reset(&s->pcihp_state); + } +} + static void acpi_ged_class_init(ObjectClass *class, const void *data) { DeviceClass *dc = DEVICE_CLASS(class); HotplugHandlerClass *hc = HOTPLUG_HANDLER_CLASS(class); AcpiDeviceIfClass *adevc = ACPI_DEVICE_IF_CLASS(class); + ResettableClass *rc = RESETTABLE_CLASS(class); + AcpiGedClass *gedc = ACPI_GED_CLASS(class); dc->desc = "ACPI Generic Event Device"; device_class_set_props(dc, acpi_ged_properties); dc->vmsd = &vmstate_acpi_ged; dc->realize = acpi_ged_realize; + hc->pre_plug = acpi_ged_device_pre_plug_cb; hc->plug = acpi_ged_device_plug_cb; hc->unplug_request = acpi_ged_unplug_request_cb; hc->unplug = acpi_ged_unplug_cb; + resettable_class_set_parent_phases(rc, NULL, ged_reset_hold, NULL, + &gedc->parent_phases); adevc->ospm_status = acpi_ged_ospm_status; adevc->send_event = acpi_ged_send_event; @@ -494,6 +570,7 @@ static const TypeInfo acpi_ged_info = { .instance_size = sizeof(AcpiGedState), .instance_init = acpi_ged_initfn, .class_init = acpi_ged_class_init, + .class_size = sizeof(AcpiGedClass), .interfaces = (const InterfaceInfo[]) { { TYPE_HOTPLUG_HANDLER }, { TYPE_ACPI_DEVICE_IF }, diff --git a/hw/acpi/ich9.c b/hw/acpi/ich9.c index 967b674..2b3b493 100644 --- a/hw/acpi/ich9.c +++ b/hw/acpi/ich9.c @@ -322,9 +322,10 @@ void ich9_pm_init(PCIDevice *lpc_pci, ICH9LPCPMRegs *pm, qemu_irq sci_irq) } if (pm->acpi_pci_hotplug.use_acpi_hotplug_bridge) { + object_property_set_link(OBJECT(lpc_pci), "bus", + OBJECT(pci_get_bus(lpc_pci)), &error_abort); acpi_pcihp_init(OBJECT(lpc_pci), &pm->acpi_pci_hotplug, - pci_get_bus(lpc_pci), pci_address_space_io(lpc_pci), ACPI_PCIHP_ADDR_ICH9); @@ -428,6 +429,10 @@ void ich9_pm_add_properties(Object *obj, ICH9LPCPMRegs *pm) object_property_add_uint32_ptr(obj, ACPI_PM_PROP_PM_IO_BASE, &pm->pm_io_base, OBJ_PROP_FLAG_READ); + object_property_add_link(obj, "bus", TYPE_PCI_BUS, + (Object **)&pm->acpi_pci_hotplug.root, + object_property_allow_set_link, + OBJ_PROP_LINK_STRONG); object_property_add(obj, ACPI_PM_PROP_GPE0_BLK, "uint32", ich9_pm_get_gpe0_blk, NULL, NULL, pm); diff --git a/hw/acpi/pci-bridge.c b/hw/acpi/pci-bridge.c index 7baa703..394a919 100644 --- a/hw/acpi/pci-bridge.c +++ b/hw/acpi/pci-bridge.c @@ -35,3 +35,57 @@ void build_pci_bridge_aml(AcpiDevAmlIf *adev, Aml *scope) } } } + +Aml *build_pci_bridge_edsm(void) +{ + Aml *method, *ifctx; + Aml *zero = aml_int(0); + Aml *func = aml_arg(2); + Aml *ret = aml_local(0); + Aml *aidx = aml_local(1); + Aml *params = aml_arg(4); + + method = aml_method("EDSM", 5, AML_SERIALIZED); + + /* get supported functions */ + ifctx = aml_if(aml_equal(func, zero)); + { + /* 1: have supported functions */ + /* 7: support for function 7 */ + const uint8_t caps = 1 | BIT(7); + build_append_pci_dsm_func0_common(ifctx, ret); + aml_append(ifctx, aml_store(aml_int(caps), aml_index(ret, zero))); + aml_append(ifctx, aml_return(ret)); + } + aml_append(method, ifctx); + + /* handle specific functions requests */ + /* + * PCI Firmware Specification 3.1 + * 4.6.7. _DSM for Naming a PCI or PCI Express Device Under + * Operating Systems + */ + ifctx = aml_if(aml_equal(func, aml_int(7))); + { + Aml *pkg = aml_package(2); + aml_append(pkg, zero); + /* optional, if not impl. should return null string */ + aml_append(pkg, aml_string("%s", "")); + aml_append(ifctx, aml_store(pkg, ret)); + + /* + * IASL is fine when initializing Package with computational data, + * however it makes guest unhappy /it fails to process such AML/. + * So use runtime assignment to set acpi-index after initializer + * to make OSPM happy. + */ + aml_append(ifctx, + aml_store(aml_derefof(aml_index(params, aml_int(0))), aidx)); + aml_append(ifctx, aml_store(aidx, aml_index(ret, zero))); + aml_append(ifctx, aml_return(ret)); + } + aml_append(method, ifctx); + + return method; +} + diff --git a/hw/acpi/pci.c b/hw/acpi/pci.c index d511a85..2228f12 100644 --- a/hw/acpi/pci.c +++ b/hw/acpi/pci.c @@ -301,3 +301,53 @@ void build_srat_generic_affinity_structures(GArray *table_data) object_child_foreach_recursive(object_get_root(), build_acpi_generic_port, table_data); } + +Aml *build_pci_host_bridge_osc_method(bool enable_native_pcie_hotplug) +{ + Aml *if_ctx; + Aml *if_ctx2; + Aml *else_ctx; + Aml *method; + Aml *a_cwd1 = aml_name("CDW1"); + Aml *a_ctrl = aml_local(0); + + method = aml_method("_OSC", 4, AML_NOTSERIALIZED); + aml_append(method, aml_create_dword_field(aml_arg(3), aml_int(0), "CDW1")); + + if_ctx = aml_if(aml_equal( + aml_arg(0), aml_touuid("33DB4D5B-1FF7-401C-9657-7441C03DD766"))); + aml_append(if_ctx, aml_create_dword_field(aml_arg(3), aml_int(4), "CDW2")); + aml_append(if_ctx, aml_create_dword_field(aml_arg(3), aml_int(8), "CDW3")); + + aml_append(if_ctx, aml_store(aml_name("CDW3"), a_ctrl)); + + /* + * Always allow native PME, AER (no dependencies) + * Allow SHPC (PCI bridges can have SHPC controller) + * Disable PCIe Native Hot-plug if ACPI PCI Hot-plug is enabled. + */ + aml_append(if_ctx, aml_and(a_ctrl, + aml_int(0x1E | (enable_native_pcie_hotplug ? 0x1 : 0x0)), a_ctrl)); + + if_ctx2 = aml_if(aml_lnot(aml_equal(aml_arg(1), aml_int(1)))); + /* Unknown revision */ + aml_append(if_ctx2, aml_or(a_cwd1, aml_int(0x08), a_cwd1)); + aml_append(if_ctx, if_ctx2); + + if_ctx2 = aml_if(aml_lnot(aml_equal(aml_name("CDW3"), a_ctrl))); + /* Capabilities bits were masked */ + aml_append(if_ctx2, aml_or(a_cwd1, aml_int(0x10), a_cwd1)); + aml_append(if_ctx, if_ctx2); + + /* Update DWORD3 in the buffer */ + aml_append(if_ctx, aml_store(a_ctrl, aml_name("CDW3"))); + aml_append(method, if_ctx); + + else_ctx = aml_else(); + /* Unrecognized UUID */ + aml_append(else_ctx, aml_or(a_cwd1, aml_int(4), a_cwd1)); + aml_append(method, else_ctx); + + aml_append(method, aml_return(aml_arg(3))); + return method; +} diff --git a/hw/acpi/pcihp.c b/hw/acpi/pcihp.c index 497281a..4922bbc 100644 --- a/hw/acpi/pcihp.c +++ b/hw/acpi/pcihp.c @@ -26,7 +26,8 @@ #include "qemu/osdep.h" #include "hw/acpi/pcihp.h" - +#include "hw/acpi/aml-build.h" +#include "hw/acpi/acpi_aml_interface.h" #include "hw/pci-host/i440fx.h" #include "hw/pci/pci.h" #include "hw/pci/pci_bridge.h" @@ -39,9 +40,9 @@ #include "migration/vmstate.h" #include "qapi/error.h" #include "qom/qom-qobject.h" +#include "qobject/qnum.h" #include "trace.h" -#define ACPI_PCIHP_SIZE 0x0018 #define PCI_UP_BASE 0x0000 #define PCI_DOWN_BASE 0x0004 #define PCI_EJ_BASE 0x0008 @@ -97,10 +98,10 @@ static void *acpi_set_bsel(PCIBus *bus, void *opaque) return info; } -static void acpi_set_pci_info(bool has_bridge_hotplug) +static void acpi_set_pci_info(AcpiPciHpState *s) { static bool bsel_is_set; - Object *host = acpi_get_i386_pci_host(); + bool has_bridge_hotplug = s->use_acpi_hotplug_bridge; PCIBus *bus; BSELInfo info = { .bsel_alloc = ACPI_PCIHP_BSEL_DEFAULT, .has_bridge_hotplug = has_bridge_hotplug }; @@ -110,11 +111,8 @@ static void acpi_set_pci_info(bool has_bridge_hotplug) } bsel_is_set = true; - if (!host) { - return; - } - bus = PCI_HOST_BRIDGE(host)->bus; + bus = s->root; if (bus) { /* Scan all PCI buses. Set property to enable acpi based hotplug. */ pci_for_each_bus_depth_first(bus, acpi_set_bsel, NULL, &info); @@ -264,7 +262,7 @@ static void acpi_pcihp_update(AcpiPciHpState *s) void acpi_pcihp_reset(AcpiPciHpState *s) { - acpi_set_pci_info(s->use_acpi_hotplug_bridge); + acpi_set_pci_info(s); acpi_pcihp_update(s); } @@ -495,13 +493,13 @@ static const MemoryRegionOps acpi_pcihp_io_ops = { }, }; -void acpi_pcihp_init(Object *owner, AcpiPciHpState *s, PCIBus *root_bus, +void acpi_pcihp_init(Object *owner, AcpiPciHpState *s, MemoryRegion *io, uint16_t io_base) { s->io_len = ACPI_PCIHP_SIZE; s->io_base = io_base; - s->root = root_bus; + assert(s->root); memory_region_init_io(&s->io, owner, &acpi_pcihp_io_ops, s, "acpi-pci-hotplug", s->io_len); @@ -513,6 +511,425 @@ void acpi_pcihp_init(Object *owner, AcpiPciHpState *s, PCIBus *root_bus, OBJ_PROP_FLAG_READ); } +void build_append_pci_dsm_func0_common(Aml *ctx, Aml *retvar) +{ + Aml *UUID, *ifctx1; + uint8_t byte_list[1] = { 0 }; /* nothing supported yet */ + + aml_append(ctx, aml_store(aml_buffer(1, byte_list), retvar)); + /* + * PCI Firmware Specification 3.1 + * 4.6. _DSM Definitions for PCI + */ + UUID = aml_touuid("E5C937D0-3553-4D7A-9117-EA4D19C3434D"); + ifctx1 = aml_if(aml_lnot(aml_equal(aml_arg(0), UUID))); + { + /* call is for unsupported UUID, bail out */ + aml_append(ifctx1, aml_return(retvar)); + } + aml_append(ctx, ifctx1); + + ifctx1 = aml_if(aml_lless(aml_arg(1), aml_int(2))); + { + /* call is for unsupported REV, bail out */ + aml_append(ifctx1, aml_return(retvar)); + } + aml_append(ctx, ifctx1); +} + +static Aml *aml_pci_pdsm(void) +{ + Aml *method, *ifctx, *ifctx1; + Aml *ret = aml_local(0); + Aml *caps = aml_local(1); + Aml *acpi_index = aml_local(2); + Aml *zero = aml_int(0); + Aml *one = aml_int(1); + Aml *not_supp = aml_int(0xFFFFFFFF); + Aml *func = aml_arg(2); + Aml *params = aml_arg(4); + Aml *bnum = aml_derefof(aml_index(params, aml_int(0))); + Aml *sunum = aml_derefof(aml_index(params, aml_int(1))); + + method = aml_method("PDSM", 5, AML_SERIALIZED); + + /* get supported functions */ + ifctx = aml_if(aml_equal(func, zero)); + { + build_append_pci_dsm_func0_common(ifctx, ret); + + aml_append(ifctx, aml_store(zero, caps)); + aml_append(ifctx, + aml_store(aml_call2("AIDX", bnum, sunum), acpi_index)); + /* + * advertise function 7 if device has acpi-index + * acpi_index values: + * 0: not present (default value) + * FFFFFFFF: not supported (old QEMU without PIDX reg) + * other: device's acpi-index + */ + ifctx1 = aml_if(aml_lnot( + aml_or(aml_equal(acpi_index, zero), + aml_equal(acpi_index, not_supp), NULL) + )); + { + /* have supported functions */ + aml_append(ifctx1, aml_or(caps, one, caps)); + /* support for function 7 */ + aml_append(ifctx1, + aml_or(caps, aml_shiftleft(one, aml_int(7)), caps)); + } + aml_append(ifctx, ifctx1); + + aml_append(ifctx, aml_store(caps, aml_index(ret, zero))); + aml_append(ifctx, aml_return(ret)); + } + aml_append(method, ifctx); + + /* handle specific functions requests */ + /* + * PCI Firmware Specification 3.1 + * 4.6.7. _DSM for Naming a PCI or PCI Express Device Under + * Operating Systems + */ + ifctx = aml_if(aml_equal(func, aml_int(7))); + { + Aml *pkg = aml_package(2); + + aml_append(ifctx, aml_store(aml_call2("AIDX", bnum, sunum), acpi_index)); + aml_append(ifctx, aml_store(pkg, ret)); + /* + * Windows calls func=7 without checking if it's available, + * as workaround Microsoft has suggested to return invalid for func7 + * Package, so return 2 elements package but only initialize elements + * when acpi_index is supported and leave them uninitialized, which + * leads elements to being Uninitialized ObjectType and should trip + * Windows into discarding result as an unexpected and prevent setting + * bogus 'PCI Label' on the device. + */ + ifctx1 = aml_if(aml_lnot(aml_lor( + aml_equal(acpi_index, zero), aml_equal(acpi_index, not_supp) + ))); + { + aml_append(ifctx1, aml_store(acpi_index, aml_index(ret, zero))); + /* + * optional, if not impl. should return null string + */ + aml_append(ifctx1, aml_store(aml_string("%s", ""), + aml_index(ret, one))); + } + aml_append(ifctx, ifctx1); + + aml_append(ifctx, aml_return(ret)); + } + + aml_append(method, ifctx); + return method; +} + +void build_acpi_pci_hotplug(Aml *table, AmlRegionSpace rs, uint64_t pcihp_addr) +{ + Aml *scope; + Aml *field; + Aml *method; + + scope = aml_scope("_SB.PCI0"); + + aml_append(scope, + aml_operation_region("PCST", rs, aml_int(pcihp_addr), 0x08)); + field = aml_field("PCST", AML_DWORD_ACC, AML_NOLOCK, AML_WRITE_AS_ZEROS); + aml_append(field, aml_named_field("PCIU", 32)); + aml_append(field, aml_named_field("PCID", 32)); + aml_append(scope, field); + + aml_append(scope, + aml_operation_region("SEJ", rs, + aml_int(pcihp_addr + ACPI_PCIHP_SEJ_BASE), 0x04)); + field = aml_field("SEJ", AML_DWORD_ACC, AML_NOLOCK, AML_WRITE_AS_ZEROS); + aml_append(field, aml_named_field("B0EJ", 32)); + aml_append(scope, field); + + aml_append(scope, + aml_operation_region("BNMR", rs, + aml_int(pcihp_addr + ACPI_PCIHP_BNMR_BASE), 0x08)); + field = aml_field("BNMR", AML_DWORD_ACC, AML_NOLOCK, AML_WRITE_AS_ZEROS); + aml_append(field, aml_named_field("BNUM", 32)); + aml_append(field, aml_named_field("PIDX", 32)); + aml_append(scope, field); + + aml_append(scope, aml_mutex("BLCK", 0)); + + method = aml_method("PCEJ", 2, AML_NOTSERIALIZED); + aml_append(method, aml_acquire(aml_name("BLCK"), 0xFFFF)); + aml_append(method, aml_store(aml_arg(0), aml_name("BNUM"))); + aml_append(method, + aml_store(aml_shiftleft(aml_int(1), aml_arg(1)), aml_name("B0EJ"))); + aml_append(method, aml_release(aml_name("BLCK"))); + aml_append(method, aml_return(aml_int(0))); + aml_append(scope, method); + + method = aml_method("AIDX", 2, AML_NOTSERIALIZED); + aml_append(method, aml_acquire(aml_name("BLCK"), 0xFFFF)); + aml_append(method, aml_store(aml_arg(0), aml_name("BNUM"))); + aml_append(method, + aml_store(aml_shiftleft(aml_int(1), aml_arg(1)), aml_name("PIDX"))); + aml_append(method, aml_store(aml_name("PIDX"), aml_local(0))); + aml_append(method, aml_release(aml_name("BLCK"))); + aml_append(method, aml_return(aml_local(0))); + aml_append(scope, method); + + aml_append(scope, aml_pci_pdsm()); + + aml_append(table, scope); +} + +/* Reserve PCIHP resources */ +void build_append_pcihp_resources(Aml *scope /* \\_SB.PCI0 */, + uint64_t io_addr, uint64_t io_len) +{ + Aml *dev, *crs; + + dev = aml_device("PHPR"); + aml_append(dev, aml_name_decl("_HID", aml_string("PNP0A06"))); + aml_append(dev, + aml_name_decl("_UID", aml_string("PCI Hotplug resources"))); + /* device present, functioning, decoding, not shown in UI */ + aml_append(dev, aml_name_decl("_STA", aml_int(0xB))); + crs = aml_resource_template(); + aml_append(crs, aml_io(AML_DECODE16, io_addr, io_addr, 1, io_len)); + aml_append(dev, aml_name_decl("_CRS", crs)); + aml_append(scope, dev); +} + +bool build_append_notification_callback(Aml *parent_scope, const PCIBus *bus) +{ + Aml *method; + PCIBus *sec; + QObject *bsel; + int nr_notifiers = 0; + GQueue *pcnt_bus_list = g_queue_new(); + + QLIST_FOREACH(sec, &bus->child, sibling) { + Aml *br_scope = aml_scope("S%.02X", sec->parent_dev->devfn); + if (pci_bus_is_root(sec)) { + continue; + } + nr_notifiers = nr_notifiers + + build_append_notification_callback(br_scope, sec); + /* + * add new child scope to parent + * and keep track of bus that have PCNT, + * bus list is used later to call children PCNTs from this level PCNT + */ + if (nr_notifiers) { + g_queue_push_tail(pcnt_bus_list, sec); + aml_append(parent_scope, br_scope); + } + } + + /* + * Append PCNT method to notify about events on local and child buses. + * ps: hostbridge might not have hotplug (bsel) enabled but might have + * child bridges that do have bsel. + */ + method = aml_method("PCNT", 0, AML_NOTSERIALIZED); + + /* If bus supports hotplug select it and notify about local events */ + bsel = object_property_get_qobject(OBJECT(bus), ACPI_PCIHP_PROP_BSEL, NULL); + if (bsel) { + uint64_t bsel_val = qnum_get_uint(qobject_to(QNum, bsel)); + + aml_append(method, aml_store(aml_int(bsel_val), aml_name("BNUM"))); + aml_append(method, aml_call2("DVNT", aml_name("PCIU"), + aml_int(1))); /* Device Check */ + aml_append(method, aml_call2("DVNT", aml_name("PCID"), + aml_int(3))); /* Eject Request */ + nr_notifiers++; + } + + /* Notify about child bus events in any case */ + while ((sec = g_queue_pop_head(pcnt_bus_list))) { + aml_append(method, aml_name("^S%.02X.PCNT", sec->parent_dev->devfn)); + } + + aml_append(parent_scope, method); + qobject_unref(bsel); + g_queue_free(pcnt_bus_list); + return !!nr_notifiers; +} + +static Aml *aml_pci_device_dsm(void) +{ + Aml *method; + + method = aml_method("_DSM", 4, AML_SERIALIZED); + { + Aml *params = aml_local(0); + Aml *pkg = aml_package(2); + aml_append(pkg, aml_int(0)); + aml_append(pkg, aml_int(0)); + aml_append(method, aml_store(pkg, params)); + aml_append(method, + aml_store(aml_name("BSEL"), aml_index(params, aml_int(0)))); + aml_append(method, + aml_store(aml_name("ASUN"), aml_index(params, aml_int(1)))); + aml_append(method, + aml_return(aml_call5("PDSM", aml_arg(0), aml_arg(1), + aml_arg(2), aml_arg(3), params)) + ); + } + return method; +} + +static Aml *aml_pci_static_endpoint_dsm(PCIDevice *pdev) +{ + Aml *method; + + g_assert(pdev->acpi_index != 0); + method = aml_method("_DSM", 4, AML_SERIALIZED); + { + Aml *params = aml_local(0); + Aml *pkg = aml_package(1); + aml_append(pkg, aml_int(pdev->acpi_index)); + aml_append(method, aml_store(pkg, params)); + aml_append(method, + aml_return(aml_call5("EDSM", aml_arg(0), aml_arg(1), + aml_arg(2), aml_arg(3), params)) + ); + } + return method; +} + +static void build_append_pcihp_notify_entry(Aml *method, int slot) +{ + Aml *if_ctx; + int32_t devfn = PCI_DEVFN(slot, 0); + + if_ctx = aml_if(aml_and(aml_arg(0), aml_int(0x1U << slot), NULL)); + aml_append(if_ctx, aml_notify(aml_name("S%.02X", devfn), aml_arg(1))); + aml_append(method, if_ctx); +} + +static bool is_devfn_ignored_generic(const int devfn, const PCIBus *bus) +{ + const PCIDevice *pdev = bus->devices[devfn]; + + if (PCI_FUNC(devfn)) { + if (IS_PCI_BRIDGE(pdev)) { + /* + * Ignore only hotplugged PCI bridges on !0 functions, but + * allow describing cold plugged bridges on all functions + */ + if (DEVICE(pdev)->hotplugged) { + return true; + } + } + } + return false; +} + +static bool is_devfn_ignored_hotplug(const int devfn, const PCIBus *bus) +{ + PCIDevice *pdev = bus->devices[devfn]; + if (pdev) { + return is_devfn_ignored_generic(devfn, bus) || + !DEVICE_GET_CLASS(pdev)->hotpluggable || + /* Cold plugged bridges aren't themselves hot-pluggable */ + (IS_PCI_BRIDGE(pdev) && !DEVICE(pdev)->hotplugged); + } else { /* non populated slots */ + /* + * hotplug is supported only for non-multifunction device + * so generate device description only for function 0 + */ + if (PCI_FUNC(devfn) || + (pci_bus_is_express(bus) && PCI_SLOT(devfn) > 0)) { + return true; + } + } + return false; +} + +void build_append_pcihp_slots(Aml *parent_scope, PCIBus *bus) +{ + int devfn; + Aml *dev, *notify_method = NULL, *method; + QObject *bsel = object_property_get_qobject(OBJECT(bus), + ACPI_PCIHP_PROP_BSEL, NULL); + uint64_t bsel_val = qnum_get_uint(qobject_to(QNum, bsel)); + qobject_unref(bsel); + + aml_append(parent_scope, aml_name_decl("BSEL", aml_int(bsel_val))); + notify_method = aml_method("DVNT", 2, AML_NOTSERIALIZED); + + for (devfn = 0; devfn < ARRAY_SIZE(bus->devices); devfn++) { + int slot = PCI_SLOT(devfn); + int adr = slot << 16 | PCI_FUNC(devfn); + + if (is_devfn_ignored_hotplug(devfn, bus)) { + continue; + } + + if (bus->devices[devfn]) { + dev = aml_scope("S%.02X", devfn); + } else { + dev = aml_device("S%.02X", devfn); + aml_append(dev, aml_name_decl("_ADR", aml_int(adr))); + } + + /* + * Can't declare _SUN here for every device as it changes 'slot' + * enumeration order in linux kernel, so use another variable for it + */ + aml_append(dev, aml_name_decl("ASUN", aml_int(slot))); + aml_append(dev, aml_pci_device_dsm()); + + aml_append(dev, aml_name_decl("_SUN", aml_int(slot))); + /* add _EJ0 to make slot hotpluggable */ + method = aml_method("_EJ0", 1, AML_NOTSERIALIZED); + aml_append(method, + aml_call2("PCEJ", aml_name("BSEL"), aml_name("_SUN")) + ); + aml_append(dev, method); + + build_append_pcihp_notify_entry(notify_method, slot); + + /* device descriptor has been composed, add it into parent context */ + aml_append(parent_scope, dev); + } + aml_append(parent_scope, notify_method); +} + +void build_append_pci_bus_devices(Aml *parent_scope, PCIBus *bus) +{ + int devfn; + Aml *dev; + + for (devfn = 0; devfn < ARRAY_SIZE(bus->devices); devfn++) { + /* ACPI spec: 1.0b: Table 6-2 _ADR Object Bus Types, PCI type */ + int adr = PCI_SLOT(devfn) << 16 | PCI_FUNC(devfn); + PCIDevice *pdev = bus->devices[devfn]; + + if (!pdev || is_devfn_ignored_generic(devfn, bus)) { + continue; + } + + /* start to compose PCI device descriptor */ + dev = aml_device("S%.02X", devfn); + aml_append(dev, aml_name_decl("_ADR", aml_int(adr))); + + call_dev_aml_func(DEVICE(bus->devices[devfn]), dev); + /* add _DSM if device has acpi-index set */ + if (pdev->acpi_index && + !object_property_get_bool(OBJECT(pdev), "hotpluggable", + &error_abort)) { + aml_append(dev, aml_pci_static_endpoint_dsm(pdev)); + } + + /* device descriptor has been composed, add it into parent context */ + aml_append(parent_scope, dev); + } +} + const VMStateDescription vmstate_acpi_pcihp_pci_status = { .name = "acpi_pcihp_pci_status", .version_id = 1, diff --git a/hw/acpi/piix4.c b/hw/acpi/piix4.c index d98b80d..7a18f18 100644 --- a/hw/acpi/piix4.c +++ b/hw/acpi/piix4.c @@ -567,7 +567,8 @@ static void piix4_acpi_system_hot_add_init(MemoryRegion *parent, if (s->acpi_pci_hotplug.use_acpi_hotplug_bridge || s->acpi_pci_hotplug.use_acpi_root_pci_hotplug) { - acpi_pcihp_init(OBJECT(s), &s->acpi_pci_hotplug, bus, parent, + object_property_set_link(OBJECT(s), "bus", OBJECT(bus), &error_abort); + acpi_pcihp_init(OBJECT(s), &s->acpi_pci_hotplug, parent, ACPI_PCIHP_ADDR_PIIX4); qbus_set_hotplug_handler(BUS(pci_get_bus(PCI_DEVICE(s))), OBJECT(s)); } @@ -611,6 +612,8 @@ static const Property piix4_pm_properties[] = { acpi_pci_hotplug.use_acpi_hotplug_bridge, true), DEFINE_PROP_BOOL(ACPI_PM_PROP_ACPI_PCI_ROOTHP, PIIX4PMState, acpi_pci_hotplug.use_acpi_root_pci_hotplug, true), + DEFINE_PROP_LINK("bus", PIIX4PMState, acpi_pci_hotplug.root, + TYPE_PCI_BUS, PCIBus *), DEFINE_PROP_BOOL("memory-hotplug-support", PIIX4PMState, acpi_memory_hotplug.is_enabled, true), DEFINE_PROP_BOOL("smm-compat", PIIX4PMState, smm_compat, false), diff --git a/hw/arm/Kconfig b/hw/arm/Kconfig index 1634e26..2aa4b5d 100644 --- a/hw/arm/Kconfig +++ b/hw/arm/Kconfig @@ -34,6 +34,8 @@ config ARM_VIRT select ACPI_HW_REDUCED select ACPI_APEI select ACPI_VIOT + select ACPI_PCIHP + select ACPI_PCI_BRIDGE select VIRTIO_MEM_SUPPORTED select ACPI_CXL select ACPI_HMAT diff --git a/hw/arm/allwinner-r40.c b/hw/arm/allwinner-r40.c index 0bf7008..c8eda39 100644 --- a/hw/arm/allwinner-r40.c +++ b/hw/arm/allwinner-r40.c @@ -20,7 +20,6 @@ #include "qemu/osdep.h" #include "qapi/error.h" #include "qemu/error-report.h" -#include "qemu/bswap.h" #include "qemu/module.h" #include "qemu/units.h" #include "hw/boards.h" diff --git a/hw/arm/boot.c b/hw/arm/boot.c index becd827..d391cd0 100644 --- a/hw/arm/boot.c +++ b/hw/arm/boot.c @@ -15,6 +15,7 @@ #include "hw/arm/boot.h" #include "hw/arm/linux-boot-if.h" #include "cpu.h" +#include "exec/tswap.h" #include "exec/target_page.h" #include "system/kvm.h" #include "system/tcg.h" @@ -29,6 +30,7 @@ #include "qemu/config-file.h" #include "qemu/option.h" #include "qemu/units.h" +#include "qemu/bswap.h" /* Kernel boot protocol is specified in the kernel docs * Documentation/arm/Booting and Documentation/arm64/booting.txt diff --git a/hw/arm/npcm7xx.c b/hw/arm/npcm7xx.c index 2f30c49..ecfae32 100644 --- a/hw/arm/npcm7xx.c +++ b/hw/arm/npcm7xx.c @@ -24,7 +24,7 @@ #include "hw/qdev-clock.h" #include "hw/qdev-properties.h" #include "qapi/error.h" -#include "qemu/bswap.h" +#include "exec/tswap.h" #include "qemu/units.h" #include "system/system.h" #include "target/arm/cpu-qom.h" diff --git a/hw/arm/virt-acpi-build.c b/hw/arm/virt-acpi-build.c index 0dfb8ec..b01fc4f 100644 --- a/hw/arm/virt-acpi-build.c +++ b/hw/arm/virt-acpi-build.c @@ -34,6 +34,7 @@ #include "hw/core/cpu.h" #include "hw/acpi/acpi-defs.h" #include "hw/acpi/acpi.h" +#include "hw/acpi/pcihp.h" #include "hw/nvram/fw_cfg_acpi.h" #include "hw/acpi/bios-linker-loader.h" #include "hw/acpi/aml-build.h" @@ -144,12 +145,21 @@ static void acpi_dsdt_add_pci(Aml *scope, const MemMapEntry *memmap, int ecam_id = VIRT_ECAM_ID(vms->highmem_ecam); bool cxl_present = false; PCIBus *bus = vms->bus; + bool acpi_pcihp = false; + + if (vms->acpi_dev) { + acpi_pcihp = object_property_get_bool(OBJECT(vms->acpi_dev), + ACPI_PM_PROP_ACPI_PCIHP_BRIDGE, + NULL); + } + struct GPEXConfig cfg = { .mmio32 = memmap[VIRT_PCIE_MMIO], .pio = memmap[VIRT_PCIE_PIO], .ecam = memmap[ecam_id], .irq = irq, .bus = vms->bus, + .pci_native_hotplug = !acpi_pcihp, }; if (vms->highmem_mmio) { @@ -897,6 +907,7 @@ build_dsdt(GArray *table_data, BIOSLinker *linker, VirtMachineState *vms) const int *irqmap = vms->irqmap; AcpiTable table = { .sig = "DSDT", .rev = 2, .oem_id = vms->oem_id, .oem_table_id = vms->oem_table_id }; + Aml *pci0_scope; acpi_table_begin(&table, table_data); dsdt = init_aml_allocator(); @@ -950,6 +961,33 @@ build_dsdt(GArray *table_data, BIOSLinker *linker, VirtMachineState *vms) aml_append(dsdt, scope); + pci0_scope = aml_scope("\\_SB.PCI0"); + + aml_append(pci0_scope, build_pci_bridge_edsm()); + build_append_pci_bus_devices(pci0_scope, vms->bus); + if (object_property_find(OBJECT(vms->bus), ACPI_PCIHP_PROP_BSEL)) { + build_append_pcihp_slots(pci0_scope, vms->bus); + } + + if (vms->acpi_dev) { + bool acpi_pcihp; + + acpi_pcihp = object_property_get_bool(OBJECT(vms->acpi_dev), + ACPI_PM_PROP_ACPI_PCIHP_BRIDGE, + NULL); + + if (acpi_pcihp) { + build_acpi_pci_hotplug(dsdt, AML_SYSTEM_MEMORY, + memmap[VIRT_ACPI_PCIHP].base); + build_append_pcihp_resources(pci0_scope, + memmap[VIRT_ACPI_PCIHP].base, + memmap[VIRT_ACPI_PCIHP].size); + + build_append_notification_callback(pci0_scope, vms->bus); + } + } + aml_append(dsdt, pci0_scope); + /* copy AML table into ACPI tables blob */ g_array_append_vals(table_data, dsdt->buf->data, dsdt->buf->len); @@ -1023,7 +1061,10 @@ void virt_acpi_build(VirtMachineState *vms, AcpiBuildTables *tables) } acpi_add_table(table_offsets, tables_blob); - spcr_setup(tables_blob, tables->linker, vms); + + if (ms->acpi_spcr_enabled) { + spcr_setup(tables_blob, tables->linker, vms); + } acpi_add_table(table_offsets, tables_blob); build_dbg2(tables_blob, tables->linker, vms); diff --git a/hw/arm/virt.c b/hw/arm/virt.c index 8070ff7..ef6be36 100644 --- a/hw/arm/virt.c +++ b/hw/arm/virt.c @@ -76,6 +76,7 @@ #include "standard-headers/linux/input.h" #include "hw/arm/smmuv3.h" #include "hw/acpi/acpi.h" +#include "hw/acpi/pcihp.h" #include "target/arm/cpu-qom.h" #include "target/arm/internals.h" #include "target/arm/multiprocessing.h" @@ -186,6 +187,7 @@ static const MemMapEntry base_memmap[] = { [VIRT_NVDIMM_ACPI] = { 0x09090000, NVDIMM_ACPI_IO_LEN}, [VIRT_PVTIME] = { 0x090a0000, 0x00010000 }, [VIRT_SECURE_GPIO] = { 0x090b0000, 0x00001000 }, + [VIRT_ACPI_PCIHP] = { 0x090c0000, ACPI_PCIHP_SIZE }, [VIRT_MMIO] = { 0x0a000000, 0x00000200 }, /* ...repeating for a total of NUM_VIRTIO_TRANSPORTS, each of that size */ [VIRT_PLATFORM_BUS] = { 0x0c000000, 0x02000000 }, @@ -686,8 +688,10 @@ static inline DeviceState *create_acpi_ged(VirtMachineState *vms) { DeviceState *dev; MachineState *ms = MACHINE(vms); + SysBusDevice *sbdev; int irq = vms->irqmap[VIRT_ACPI_GED]; uint32_t event = ACPI_GED_PWR_DOWN_EVT; + bool acpi_pcihp; if (ms->ram_slots) { event |= ACPI_GED_MEM_HOTPLUG_EVT; @@ -699,11 +703,26 @@ static inline DeviceState *create_acpi_ged(VirtMachineState *vms) dev = qdev_new(TYPE_ACPI_GED); qdev_prop_set_uint32(dev, "ged-event", event); - sysbus_realize_and_unref(SYS_BUS_DEVICE(dev), &error_fatal); + object_property_set_link(OBJECT(dev), "bus", OBJECT(vms->bus), &error_abort); + sbdev = SYS_BUS_DEVICE(dev); + sysbus_realize_and_unref(sbdev, &error_fatal); + + sysbus_mmio_map_name(sbdev, TYPE_ACPI_GED, vms->memmap[VIRT_ACPI_GED].base); + sysbus_mmio_map_name(sbdev, ACPI_MEMHP_REGION_NAME, + vms->memmap[VIRT_PCDIMM_ACPI].base); + + acpi_pcihp = object_property_get_bool(OBJECT(dev), + ACPI_PM_PROP_ACPI_PCIHP_BRIDGE, NULL); + + if (acpi_pcihp) { + int pcihp_region_index; + + pcihp_region_index = sysbus_mmio_map_name(sbdev, ACPI_PCIHP_REGION_NAME, + vms->memmap[VIRT_ACPI_PCIHP].base); + assert(pcihp_region_index >= 0); + } - sysbus_mmio_map(SYS_BUS_DEVICE(dev), 0, vms->memmap[VIRT_ACPI_GED].base); - sysbus_mmio_map(SYS_BUS_DEVICE(dev), 1, vms->memmap[VIRT_PCDIMM_ACPI].base); - sysbus_connect_irq(SYS_BUS_DEVICE(dev), 0, qdev_get_gpio_in(vms->gic, irq)); + sysbus_connect_irq(sbdev, 0, qdev_get_gpio_in(vms->gic, irq)); return dev; } diff --git a/hw/block/hd-geometry.c b/hw/block/hd-geometry.c index f3939e7..db22190 100644 --- a/hw/block/hd-geometry.c +++ b/hw/block/hd-geometry.c @@ -33,7 +33,6 @@ #include "qemu/osdep.h" #include "system/block-backend.h" #include "qapi/qapi-types-block.h" -#include "qemu/bswap.h" #include "hw/block/block.h" #include "trace.h" diff --git a/hw/block/meson.build b/hw/block/meson.build index 6557044..43ed296 100644 --- a/hw/block/meson.build +++ b/hw/block/meson.build @@ -13,7 +13,9 @@ system_ss.add(when: 'CONFIG_SSI_M25P80', if_true: files('m25p80_sfdp.c')) system_ss.add(when: 'CONFIG_SWIM', if_true: files('swim.c')) system_ss.add(when: 'CONFIG_XEN_BUS', if_true: files('xen-block.c')) -specific_ss.add(when: 'CONFIG_VIRTIO_BLK', if_true: files('virtio-blk.c', 'virtio-blk-common.c')) -specific_ss.add(when: 'CONFIG_VHOST_USER_BLK', if_true: files('vhost-user-blk.c', 'virtio-blk-common.c')) +specific_ss.add(when: 'CONFIG_VIRTIO_BLK', if_true: files('virtio-blk.c')) +system_ss.add(when: 'CONFIG_VIRTIO_BLK', if_true: files('virtio-blk-common.c')) +specific_ss.add(when: 'CONFIG_VHOST_USER_BLK', if_true: files('vhost-user-blk.c')) +system_ss.add(when: 'CONFIG_VHOST_USER_BLK', if_true: files('virtio-blk-common.c')) subdir('dataplane') diff --git a/hw/block/vhost-user-blk.c b/hw/block/vhost-user-blk.c index 0eebbcd..c0cc5f6 100644 --- a/hw/block/vhost-user-blk.c +++ b/hw/block/vhost-user-blk.c @@ -210,6 +210,7 @@ static int vhost_user_blk_stop(VirtIODevice *vdev) BusState *qbus = BUS(qdev_get_parent_bus(DEVICE(vdev))); VirtioBusClass *k = VIRTIO_BUS_GET_CLASS(qbus); int ret; + bool force_stop = false; if (!s->started_vu) { return 0; @@ -220,7 +221,11 @@ static int vhost_user_blk_stop(VirtIODevice *vdev) return 0; } - ret = vhost_dev_stop(&s->dev, vdev, true); + force_stop = s->skip_get_vring_base_on_force_shutdown && + qemu_force_shutdown_requested(); + + ret = force_stop ? vhost_dev_force_stop(&s->dev, vdev, true) : + vhost_dev_stop(&s->dev, vdev, true); if (k->set_guest_notifiers(qbus->parent, s->dev.nvqs, false) < 0) { error_report("vhost guest notifier cleanup failed: %d", ret); @@ -584,6 +589,8 @@ static const Property vhost_user_blk_properties[] = { VIRTIO_BLK_F_DISCARD, true), DEFINE_PROP_BIT64("write-zeroes", VHostUserBlk, parent_obj.host_features, VIRTIO_BLK_F_WRITE_ZEROES, true), + DEFINE_PROP_BOOL("skip-get-vring-base-on-force-shutdown", VHostUserBlk, + skip_get_vring_base_on_force_shutdown, false), }; static void vhost_user_blk_class_init(ObjectClass *klass, const void *data) diff --git a/hw/char/riscv_htif.c b/hw/char/riscv_htif.c index c884be5..a78ea9b 100644 --- a/hw/char/riscv_htif.c +++ b/hw/char/riscv_htif.c @@ -29,7 +29,6 @@ #include "qemu/timer.h" #include "qemu/error-report.h" #include "system/address-spaces.h" -#include "exec/tswap.h" #include "system/dma.h" #include "system/runstate.h" #include "trace.h" diff --git a/hw/core/cpu-system.c b/hw/core/cpu-system.c index 3c84176..a975405 100644 --- a/hw/core/cpu-system.c +++ b/hw/core/cpu-system.c @@ -24,7 +24,7 @@ #include "exec/cputlb.h" #include "system/memory.h" #include "exec/tb-flush.h" -#include "exec/tswap.h" +#include "qemu/target-info.h" #include "hw/qdev-core.h" #include "hw/qdev-properties.h" #include "hw/core/sysemu-cpu-ops.h" diff --git a/hw/core/machine-qmp-cmds.c b/hw/core/machine-qmp-cmds.c index d82043e..cd98dae 100644 --- a/hw/core/machine-qmp-cmds.c +++ b/hw/core/machine-qmp-cmds.c @@ -19,7 +19,7 @@ #include "qapi/qobject-input-visitor.h" #include "qapi/type-helpers.h" #include "qemu/uuid.h" -#include "qemu/target-info.h" +#include "qemu/target-info-qapi.h" #include "qom/qom-qobject.h" #include "system/hostmem.h" #include "system/hw_accel.h" @@ -37,8 +37,7 @@ CpuInfoFastList *qmp_query_cpus_fast(Error **errp) MachineState *ms = MACHINE(qdev_get_machine()); MachineClass *mc = MACHINE_GET_CLASS(ms); CpuInfoFastList *head = NULL, **tail = &head; - SysEmuTarget target = qapi_enum_parse(&SysEmuTarget_lookup, target_name(), - -1, &error_abort); + SysEmuTarget target = target_arch(); CPUState *cpu; CPU_FOREACH(cpu) { @@ -139,8 +138,7 @@ QemuTargetInfo *qmp_query_target(Error **errp) { QemuTargetInfo *info = g_malloc0(sizeof(*info)); - info->arch = qapi_enum_parse(&SysEmuTarget_lookup, target_name(), -1, - &error_abort); + info->arch = target_arch(); return info; } diff --git a/hw/core/machine.c b/hw/core/machine.c index e869821..ceee058 100644 --- a/hw/core/machine.c +++ b/hw/core/machine.c @@ -577,6 +577,20 @@ static void machine_set_nvdimm(Object *obj, bool value, Error **errp) ms->nvdimms_state->is_enabled = value; } +static bool machine_get_spcr(Object *obj, Error **errp) +{ + MachineState *ms = MACHINE(obj); + + return ms->acpi_spcr_enabled; +} + +static void machine_set_spcr(Object *obj, bool value, Error **errp) +{ + MachineState *ms = MACHINE(obj); + + ms->acpi_spcr_enabled = value; +} + static bool machine_get_hmat(Object *obj, Error **errp) { MachineState *ms = MACHINE(obj); @@ -1281,6 +1295,14 @@ static void machine_initfn(Object *obj) "Table (HMAT)"); } + /* SPCR */ + ms->acpi_spcr_enabled = true; + object_property_add_bool(obj, "spcr", machine_get_spcr, machine_set_spcr); + object_property_set_description(obj, "spcr", + "Set on/off to enable/disable " + "ACPI Serial Port Console Redirection " + "Table (spcr)"); + /* default to mc->default_cpus */ ms->smp.cpus = mc->default_cpus; ms->smp.max_cpus = mc->default_cpus; diff --git a/hw/core/qdev-properties.c b/hw/core/qdev-properties.c index 147b3ff..b7e8a89 100644 --- a/hw/core/qdev-properties.c +++ b/hw/core/qdev-properties.c @@ -2,6 +2,7 @@ #include "hw/qdev-properties.h" #include "qapi/error.h" #include "qapi/qapi-types-misc.h" +#include "qapi/qapi-visit-common.h" #include "qobject/qlist.h" #include "qemu/ctype.h" #include "qemu/error-report.h" @@ -180,7 +181,8 @@ const PropertyInfo qdev_prop_bit = { static uint64_t qdev_get_prop_mask64(const Property *prop) { - assert(prop->info == &qdev_prop_bit64); + assert(prop->info == &qdev_prop_bit64 || + prop->info == &qdev_prop_on_off_auto_bit64); return 0x1ull << prop->bitnr; } @@ -225,6 +227,69 @@ const PropertyInfo qdev_prop_bit64 = { .set_default_value = set_default_value_bool, }; +static void prop_get_on_off_auto_bit64(Object *obj, Visitor *v, + const char *name, void *opaque, + Error **errp) +{ + Property *prop = opaque; + OnOffAutoBit64 *p = object_field_prop_ptr(obj, prop); + OnOffAuto value; + uint64_t mask = qdev_get_prop_mask64(prop); + + if (p->auto_bits & mask) { + value = ON_OFF_AUTO_AUTO; + } else if (p->on_bits & mask) { + value = ON_OFF_AUTO_ON; + } else { + value = ON_OFF_AUTO_OFF; + } + + visit_type_OnOffAuto(v, name, &value, errp); +} + +static void prop_set_on_off_auto_bit64(Object *obj, Visitor *v, + const char *name, void *opaque, + Error **errp) +{ + Property *prop = opaque; + OnOffAutoBit64 *p = object_field_prop_ptr(obj, prop); + OnOffAuto value; + uint64_t mask = qdev_get_prop_mask64(prop); + + if (!visit_type_OnOffAuto(v, name, &value, errp)) { + return; + } + + switch (value) { + case ON_OFF_AUTO_AUTO: + p->on_bits &= ~mask; + p->auto_bits |= mask; + break; + + case ON_OFF_AUTO_ON: + p->on_bits |= mask; + p->auto_bits &= ~mask; + break; + + case ON_OFF_AUTO_OFF: + p->on_bits &= ~mask; + p->auto_bits &= ~mask; + break; + + case ON_OFF_AUTO__MAX: + g_assert_not_reached(); + } +} + +const PropertyInfo qdev_prop_on_off_auto_bit64 = { + .type = "OnOffAuto", + .description = "on/off/auto", + .enum_table = &OnOffAuto_lookup, + .get = prop_get_on_off_auto_bit64, + .set = prop_set_on_off_auto_bit64, + .set_default_value = qdev_propinfo_set_default_value_enum, +}; + /* --- bool --- */ static void get_bool(Object *obj, Visitor *v, const char *name, void *opaque, diff --git a/hw/core/sysbus.c b/hw/core/sysbus.c index e71367a..ec69e87 100644 --- a/hw/core/sysbus.c +++ b/hw/core/sysbus.c @@ -151,6 +151,17 @@ void sysbus_mmio_map(SysBusDevice *dev, int n, hwaddr addr) sysbus_mmio_map_common(dev, n, addr, false, 0); } +int sysbus_mmio_map_name(SysBusDevice *dev, const char *name, hwaddr addr) +{ + for (int i = 0; i < dev->num_mmio; i++) { + if (!strcmp(dev->mmio[i].memory->name, name)) { + sysbus_mmio_map(dev, i, addr); + return i; + } + } + return -1; +} + void sysbus_mmio_map_overlap(SysBusDevice *dev, int n, hwaddr addr, int priority) { diff --git a/hw/cxl/cxl-events.c b/hw/cxl/cxl-events.c index 12dee2e..7583dd9 100644 --- a/hw/cxl/cxl-events.c +++ b/hw/cxl/cxl-events.c @@ -8,8 +8,6 @@ */ #include "qemu/osdep.h" - -#include "qemu/bswap.h" #include "qemu/error-report.h" #include "hw/pci/msi.h" #include "hw/pci/msix.h" @@ -260,3 +258,41 @@ void cxl_event_irq_assert(CXLType3Dev *ct3d) } } } + +void cxl_create_dc_event_records_for_extents(CXLType3Dev *ct3d, + CXLDCEventType type, + CXLDCExtentRaw extents[], + uint32_t ext_count) +{ + CXLEventDynamicCapacity event_rec = {}; + int i; + + cxl_assign_event_header(&event_rec.hdr, + &dynamic_capacity_uuid, + (1 << CXL_EVENT_TYPE_INFO), + sizeof(event_rec), + cxl_device_get_timestamp(&ct3d->cxl_dstate)); + event_rec.type = type; + event_rec.validity_flags = 1; + event_rec.host_id = 0; + event_rec.updated_region_id = 0; + event_rec.extents_avail = CXL_NUM_EXTENTS_SUPPORTED - + ct3d->dc.total_extent_count; + + for (i = 0; i < ext_count; i++) { + memcpy(&event_rec.dynamic_capacity_extent, + &extents[i], + sizeof(CXLDCExtentRaw)); + event_rec.flags = 0; + if (i < ext_count - 1) { + /* Set "More" flag */ + event_rec.flags |= BIT(0); + } + + if (cxl_event_insert(&ct3d->cxl_dstate, + CXL_EVENT_TYPE_DYNAMIC_CAP, + (CXLEventRecordRaw *)&event_rec)) { + cxl_event_irq_assert(ct3d); + } + } +} diff --git a/hw/cxl/cxl-mailbox-utils.c b/hw/cxl/cxl-mailbox-utils.c index 299f232..68c7cc9 100644 --- a/hw/cxl/cxl-mailbox-utils.c +++ b/hw/cxl/cxl-mailbox-utils.c @@ -18,15 +18,16 @@ #include "hw/pci/pci.h" #include "hw/pci-bridge/cxl_upstream_port.h" #include "qemu/cutils.h" +#include "qemu/host-utils.h" #include "qemu/log.h" #include "qemu/units.h" #include "qemu/uuid.h" #include "system/hostmem.h" #include "qemu/range.h" +#include "qapi/qapi-types-cxl.h" #define CXL_CAPACITY_MULTIPLIER (256 * MiB) #define CXL_DC_EVENT_LOG_SIZE 8 -#define CXL_NUM_EXTENTS_SUPPORTED 512 #define CXL_NUM_TAGS_SUPPORTED 0 #define CXL_ALERTS_LIFE_USED_WARN_THRESH (1 << 0) #define CXL_ALERTS_OVER_TEMP_WARN_THRESH (1 << 1) @@ -117,6 +118,13 @@ enum { #define GET_PHYSICAL_PORT_STATE 0x1 TUNNEL = 0x53, #define MANAGEMENT_COMMAND 0x0 + FMAPI_DCD_MGMT = 0x56, + #define GET_DCD_INFO 0x0 + #define GET_HOST_DC_REGION_CONFIG 0x1 + #define SET_DC_REGION_CONFIG 0x2 + #define GET_DC_REGION_EXTENT_LIST 0x3 + #define INITIATE_DC_ADD 0x4 + #define INITIATE_DC_RELEASE 0x5 }; /* CCI Message Format CXL r3.1 Figure 7-19 */ @@ -2750,7 +2758,7 @@ static CXLRetCode cmd_dcd_get_dyn_cap_ext_list(const struct cxl_cmd *cmd, uint16_t out_pl_len, size; CXLDCExtent *ent; - if (start_extent_id > ct3d->dc.total_extent_count) { + if (start_extent_id > ct3d->dc.nr_extents_accepted) { return CXL_MBOX_INVALID_INPUT; } @@ -2761,7 +2769,7 @@ static CXLRetCode cmd_dcd_get_dyn_cap_ext_list(const struct cxl_cmd *cmd, out_pl_len = sizeof(*out) + record_count * sizeof(out->records[0]); stl_le_p(&out->count, record_count); - stl_le_p(&out->total_extents, ct3d->dc.total_extent_count); + stl_le_p(&out->total_extents, ct3d->dc.nr_extents_accepted); stl_le_p(&out->generation_num, ct3d->dc.ext_list_gen_seq); if (record_count > 0) { @@ -2883,16 +2891,20 @@ void cxl_extent_group_list_insert_tail(CXLDCExtentGroupList *list, QTAILQ_INSERT_TAIL(list, group, node); } -void cxl_extent_group_list_delete_front(CXLDCExtentGroupList *list) +uint32_t cxl_extent_group_list_delete_front(CXLDCExtentGroupList *list) { CXLDCExtent *ent, *ent_next; CXLDCExtentGroup *group = QTAILQ_FIRST(list); + uint32_t extents_deleted = 0; QTAILQ_REMOVE(list, group, node); QTAILQ_FOREACH_SAFE(ent, &group->list, node, ent_next) { cxl_remove_extent_from_extent_list(&group->list, ent); + extents_deleted++; } g_free(group); + + return extents_deleted; } /* @@ -3011,7 +3023,7 @@ static CXLRetCode cmd_dcd_add_dyn_cap_rsp(const struct cxl_cmd *cmd, CXLUpdateDCExtentListInPl *in = (void *)payload_in; CXLType3Dev *ct3d = CXL_TYPE3(cci->d); CXLDCExtentList *extent_list = &ct3d->dc.extents; - uint32_t i; + uint32_t i, num; uint64_t dpa, len; CXLRetCode ret; @@ -3020,7 +3032,8 @@ static CXLRetCode cmd_dcd_add_dyn_cap_rsp(const struct cxl_cmd *cmd, } if (in->num_entries_updated == 0) { - cxl_extent_group_list_delete_front(&ct3d->dc.extents_pending); + num = cxl_extent_group_list_delete_front(&ct3d->dc.extents_pending); + ct3d->dc.total_extent_count -= num; return CXL_MBOX_SUCCESS; } @@ -3051,10 +3064,12 @@ static CXLRetCode cmd_dcd_add_dyn_cap_rsp(const struct cxl_cmd *cmd, cxl_insert_extent_to_extent_list(extent_list, dpa, len, NULL, 0); ct3d->dc.total_extent_count += 1; + ct3d->dc.nr_extents_accepted += 1; ct3_set_region_block_backed(ct3d, dpa, len); } /* Remove the first extent group in the pending list */ - cxl_extent_group_list_delete_front(&ct3d->dc.extents_pending); + num = cxl_extent_group_list_delete_front(&ct3d->dc.extents_pending); + ct3d->dc.total_extent_count -= num; return CXL_MBOX_SUCCESS; } @@ -3160,7 +3175,7 @@ free_and_exit: } *updated_list_size = 0; } else { - *updated_list_size = ct3d->dc.total_extent_count + cnt_delta; + *updated_list_size = ct3d->dc.nr_extents_accepted + cnt_delta; } return ret; @@ -3222,11 +3237,495 @@ static CXLRetCode cmd_dcd_release_dyn_cap(const struct cxl_cmd *cmd, ct3_set_region_block_backed(ct3d, ent->start_dpa, ent->len); cxl_remove_extent_from_extent_list(&updated_list, ent); } - ct3d->dc.total_extent_count = updated_list_size; + ct3d->dc.total_extent_count += (updated_list_size - + ct3d->dc.nr_extents_accepted); + + ct3d->dc.nr_extents_accepted = updated_list_size; + + return CXL_MBOX_SUCCESS; +} + +/* CXL r3.2 section 7.6.7.6.1: Get DCD Info (Opcode 5600h) */ +static CXLRetCode cmd_fm_get_dcd_info(const struct cxl_cmd *cmd, + uint8_t *payload_in, + size_t len_in, + uint8_t *payload_out, + size_t *len_out, + CXLCCI *cci) +{ + struct { + uint8_t num_hosts; + uint8_t num_regions_supported; + uint8_t rsvd1[2]; + uint16_t supported_add_sel_policy_bitmask; + uint8_t rsvd2[2]; + uint16_t supported_removal_policy_bitmask; + uint8_t sanitize_on_release_bitmask; + uint8_t rsvd3; + uint64_t total_dynamic_capacity; + uint64_t region_blk_size_bitmasks[8]; + } QEMU_PACKED *out = (void *)payload_out; + CXLType3Dev *ct3d = CXL_TYPE3(cci->d); + CXLDCRegion *region; + int i; + + out->num_hosts = 1; + out->num_regions_supported = ct3d->dc.num_regions; + stw_le_p(&out->supported_add_sel_policy_bitmask, + BIT(CXL_EXTENT_SELECTION_POLICY_PRESCRIPTIVE)); + stw_le_p(&out->supported_removal_policy_bitmask, + BIT(CXL_EXTENT_REMOVAL_POLICY_PRESCRIPTIVE)); + out->sanitize_on_release_bitmask = 0; + + stq_le_p(&out->total_dynamic_capacity, + ct3d->dc.total_capacity / CXL_CAPACITY_MULTIPLIER); + + for (i = 0; i < ct3d->dc.num_regions; i++) { + region = &ct3d->dc.regions[i]; + memcpy(&out->region_blk_size_bitmasks[i], + ®ion->supported_blk_size_bitmask, + sizeof(out->region_blk_size_bitmasks[i])); + } + + *len_out = sizeof(*out); + return CXL_MBOX_SUCCESS; +} + +static void build_dsmas_flags(uint8_t *flags, CXLDCRegion *region) +{ + *flags = 0; + + if (region->nonvolatile) { + *flags |= BIT(CXL_DSMAS_FLAGS_NONVOLATILE); + } + if (region->sharable) { + *flags |= BIT(CXL_DSMAS_FLAGS_SHARABLE); + } + if (region->hw_managed_coherency) { + *flags |= BIT(CXL_DSMAS_FLAGS_HW_MANAGED_COHERENCY); + } + if (region->ic_specific_dc_management) { + *flags |= BIT(CXL_DSMAS_FLAGS_IC_SPECIFIC_DC_MANAGEMENT); + } + if (region->rdonly) { + *flags |= BIT(CXL_DSMAS_FLAGS_RDONLY); + } +} + +/* + * CXL r3.2 section 7.6.7.6.2: + * Get Host DC Region Configuration (Opcode 5601h) + */ +static CXLRetCode cmd_fm_get_host_dc_region_config(const struct cxl_cmd *cmd, + uint8_t *payload_in, + size_t len_in, + uint8_t *payload_out, + size_t *len_out, + CXLCCI *cci) +{ + struct { + uint16_t host_id; + uint8_t region_cnt; + uint8_t start_rid; + } QEMU_PACKED *in = (void *)payload_in; + struct { + uint16_t host_id; + uint8_t num_regions; + uint8_t regions_returned; + struct { + uint64_t base; + uint64_t decode_len; + uint64_t region_len; + uint64_t block_size; + uint8_t flags; + uint8_t rsvd1[3]; + uint8_t sanitize; + uint8_t rsvd2[3]; + } QEMU_PACKED records[]; + } QEMU_PACKED *out = (void *)payload_out; + struct { + uint32_t num_extents_supported; + uint32_t num_extents_available; + uint32_t num_tags_supported; + uint32_t num_tags_available; + } QEMU_PACKED *extra_out; + CXLType3Dev *ct3d = CXL_TYPE3(cci->d); + uint16_t record_count, out_pl_len, i; + + if (in->start_rid >= ct3d->dc.num_regions) { + return CXL_MBOX_INVALID_INPUT; + } + record_count = MIN(ct3d->dc.num_regions - in->start_rid, in->region_cnt); + + out_pl_len = sizeof(*out) + record_count * sizeof(out->records[0]); + extra_out = (void *)out + out_pl_len; + out_pl_len += sizeof(*extra_out); + + assert(out_pl_len <= CXL_MAILBOX_MAX_PAYLOAD_SIZE); + + stw_le_p(&out->host_id, 0); + out->num_regions = ct3d->dc.num_regions; + out->regions_returned = record_count; + + for (i = 0; i < record_count; i++) { + stq_le_p(&out->records[i].base, + ct3d->dc.regions[in->start_rid + i].base); + stq_le_p(&out->records[i].decode_len, + ct3d->dc.regions[in->start_rid + i].decode_len / + CXL_CAPACITY_MULTIPLIER); + stq_le_p(&out->records[i].region_len, + ct3d->dc.regions[in->start_rid + i].len); + stq_le_p(&out->records[i].block_size, + ct3d->dc.regions[in->start_rid + i].block_size); + build_dsmas_flags(&out->records[i].flags, + &ct3d->dc.regions[in->start_rid + i]); + /* Sanitize is bit 0 of flags. */ + out->records[i].sanitize = + ct3d->dc.regions[in->start_rid + i].flags & BIT(0); + } + + stl_le_p(&extra_out->num_extents_supported, CXL_NUM_EXTENTS_SUPPORTED); + stl_le_p(&extra_out->num_extents_available, CXL_NUM_EXTENTS_SUPPORTED - + ct3d->dc.total_extent_count); + stl_le_p(&extra_out->num_tags_supported, CXL_NUM_TAGS_SUPPORTED); + stl_le_p(&extra_out->num_tags_available, CXL_NUM_TAGS_SUPPORTED); + + *len_out = out_pl_len; + return CXL_MBOX_SUCCESS; +} + +/* CXL r3.2 section 7.6.7.6.3: Set Host DC Region Configuration (Opcode 5602) */ +static CXLRetCode cmd_fm_set_dc_region_config(const struct cxl_cmd *cmd, + uint8_t *payload_in, + size_t len_in, + uint8_t *payload_out, + size_t *len_out, + CXLCCI *cci) +{ + struct { + uint8_t reg_id; + uint8_t rsvd[3]; + uint64_t block_sz; + uint8_t flags; + uint8_t rsvd2[3]; + } QEMU_PACKED *in = (void *)payload_in; + CXLType3Dev *ct3d = CXL_TYPE3(cci->d); + CXLEventDynamicCapacity dcEvent = {}; + CXLDCRegion *region = &ct3d->dc.regions[in->reg_id]; + + /* + * CXL r3.2 7.6.7.6.3: Set DC Region Configuration + * This command shall fail with Unsupported when the Sanitize on Release + * field does not match the region’s configuration... and the device + * does not support reconfiguration of the Sanitize on Release setting. + * + * Currently not reconfigurable, so always fail if sanitize bit (bit 0) + * doesn't match. + */ + if ((in->flags & 0x1) != (region->flags & 0x1)) { + return CXL_MBOX_UNSUPPORTED; + } + + if (in->reg_id >= DCD_MAX_NUM_REGION) { + return CXL_MBOX_UNSUPPORTED; + } + + /* Check that no extents are in the region being reconfigured */ + if (!bitmap_empty(region->blk_bitmap, region->len / region->block_size)) { + return CXL_MBOX_UNSUPPORTED; + } + + /* Check that new block size is supported */ + if (!is_power_of_2(in->block_sz) || + !(in->block_sz & region->supported_blk_size_bitmask)) { + return CXL_MBOX_INVALID_INPUT; + } + + /* Return success if new block size == current block size */ + if (in->block_sz == region->block_size) { + return CXL_MBOX_SUCCESS; + } + + /* Free bitmap and create new one for new block size. */ + qemu_mutex_lock(®ion->bitmap_lock); + g_free(region->blk_bitmap); + region->blk_bitmap = bitmap_new(region->len / in->block_sz); + qemu_mutex_unlock(®ion->bitmap_lock); + region->block_size = in->block_sz; + + /* Create event record and insert into event log */ + cxl_assign_event_header(&dcEvent.hdr, + &dynamic_capacity_uuid, + (1 << CXL_EVENT_TYPE_INFO), + sizeof(dcEvent), + cxl_device_get_timestamp(&ct3d->cxl_dstate)); + dcEvent.type = DC_EVENT_REGION_CONFIG_UPDATED; + dcEvent.validity_flags = 1; + dcEvent.host_id = 0; + dcEvent.updated_region_id = in->reg_id; + + if (cxl_event_insert(&ct3d->cxl_dstate, + CXL_EVENT_TYPE_DYNAMIC_CAP, + (CXLEventRecordRaw *)&dcEvent)) { + cxl_event_irq_assert(ct3d); + } + return CXL_MBOX_SUCCESS; +} + +/* CXL r3.2 section 7.6.7.6.4: Get DC Region Extent Lists (Opcode 5603h) */ +static CXLRetCode cmd_fm_get_dc_region_extent_list(const struct cxl_cmd *cmd, + uint8_t *payload_in, + size_t len_in, + uint8_t *payload_out, + size_t *len_out, + CXLCCI *cci) +{ + struct { + uint16_t host_id; + uint8_t rsvd[2]; + uint32_t extent_cnt; + uint32_t start_extent_id; + } QEMU_PACKED *in = (void *)payload_in; + struct { + uint16_t host_id; + uint8_t rsvd[2]; + uint32_t start_extent_id; + uint32_t extents_returned; + uint32_t total_extents; + uint32_t list_generation_num; + uint8_t rsvd2[4]; + CXLDCExtentRaw records[]; + } QEMU_PACKED *out = (void *)payload_out; + QEMU_BUILD_BUG_ON(sizeof(*in) != 0xc); + CXLType3Dev *ct3d = CXL_TYPE3(cci->d); + CXLDCExtent *ent; + CXLDCExtentRaw *out_rec; + uint16_t record_count = 0, record_done = 0, i = 0; + uint16_t out_pl_len, max_size; + + if (in->host_id != 0) { + return CXL_MBOX_INVALID_INPUT; + } + + if (in->start_extent_id > ct3d->dc.nr_extents_accepted) { + return CXL_MBOX_INVALID_INPUT; + } + record_count = MIN(in->extent_cnt, + ct3d->dc.nr_extents_accepted - in->start_extent_id); + max_size = CXL_MAILBOX_MAX_PAYLOAD_SIZE - sizeof(*out); + record_count = MIN(record_count, max_size / sizeof(out->records[0])); + out_pl_len = sizeof(*out) + record_count * sizeof(out->records[0]); + + stw_le_p(&out->host_id, in->host_id); + stl_le_p(&out->start_extent_id, in->start_extent_id); + stl_le_p(&out->extents_returned, record_count); + stl_le_p(&out->total_extents, ct3d->dc.nr_extents_accepted); + stl_le_p(&out->list_generation_num, ct3d->dc.ext_list_gen_seq); + + if (record_count > 0) { + QTAILQ_FOREACH(ent, &ct3d->dc.extents, node) { + if (i++ < in->start_extent_id) { + continue; + } + out_rec = &out->records[record_done]; + stq_le_p(&out_rec->start_dpa, ent->start_dpa); + stq_le_p(&out_rec->len, ent->len); + memcpy(&out_rec->tag, ent->tag, 0x10); + stw_le_p(&out_rec->shared_seq, ent->shared_seq); + + record_done++; + if (record_done == record_count) { + break; + } + } + } + + *len_out = out_pl_len; return CXL_MBOX_SUCCESS; } +/* + * Helper function to convert CXLDCExtentRaw to CXLUpdateDCExtentListInPl + * in order to reuse cxl_detect_malformed_extent_list() function which accepts + * CXLUpdateDCExtentListInPl as a parameter. + */ +static void convert_raw_extents(CXLDCExtentRaw raw_extents[], + CXLUpdateDCExtentListInPl *extent_list, + int count) +{ + int i; + + extent_list->num_entries_updated = count; + + for (i = 0; i < count; i++) { + extent_list->updated_entries[i].start_dpa = raw_extents[i].start_dpa; + extent_list->updated_entries[i].len = raw_extents[i].len; + } +} + +/* CXL r3.2 Section 7.6.7.6.5: Initiate Dynamic Capacity Add (Opcode 5604h) */ +static CXLRetCode cmd_fm_initiate_dc_add(const struct cxl_cmd *cmd, + uint8_t *payload_in, + size_t len_in, + uint8_t *payload_out, + size_t *len_out, + CXLCCI *cci) +{ + struct { + uint16_t host_id; + uint8_t selection_policy; + uint8_t reg_num; + uint64_t length; + uint8_t tag[0x10]; + uint32_t ext_count; + CXLDCExtentRaw extents[]; + } QEMU_PACKED *in = (void *)payload_in; + CXLType3Dev *ct3d = CXL_TYPE3(cci->d); + int i, rc; + + switch (in->selection_policy) { + case CXL_EXTENT_SELECTION_POLICY_PRESCRIPTIVE: { + /* Adding extents exceeds device's extent tracking ability. */ + if (in->ext_count + ct3d->dc.total_extent_count > + CXL_NUM_EXTENTS_SUPPORTED) { + return CXL_MBOX_RESOURCES_EXHAUSTED; + } + + g_autofree CXLUpdateDCExtentListInPl *list = + g_malloc0(sizeof(*list) + + in->ext_count * sizeof(*list->updated_entries)); + + convert_raw_extents(in->extents, list, in->ext_count); + rc = cxl_detect_malformed_extent_list(ct3d, list); + + for (i = 0; i < in->ext_count; i++) { + CXLDCExtentRaw *ext = &in->extents[i]; + + /* Check requested extents do not overlap with pending ones. */ + if (cxl_extent_groups_overlaps_dpa_range(&ct3d->dc.extents_pending, + ext->start_dpa, + ext->len)) { + return CXL_MBOX_INVALID_EXTENT_LIST; + } + /* Check requested extents do not overlap with existing ones. */ + if (cxl_extents_overlaps_dpa_range(&ct3d->dc.extents, + ext->start_dpa, + ext->len)) { + return CXL_MBOX_INVALID_EXTENT_LIST; + } + } + + if (rc) { + return rc; + } + + CXLDCExtentGroup *group = NULL; + for (i = 0; i < in->ext_count; i++) { + CXLDCExtentRaw *ext = &in->extents[i]; + + group = cxl_insert_extent_to_extent_group(group, ext->start_dpa, + ext->len, ext->tag, + ext->shared_seq); + } + + cxl_extent_group_list_insert_tail(&ct3d->dc.extents_pending, group); + ct3d->dc.total_extent_count += in->ext_count; + cxl_create_dc_event_records_for_extents(ct3d, + DC_EVENT_ADD_CAPACITY, + in->extents, + in->ext_count); + + return CXL_MBOX_SUCCESS; + } + default: { + qemu_log_mask(LOG_UNIMP, + "CXL extent selection policy not supported.\n"); + return CXL_MBOX_INVALID_INPUT; + } + } +} + +#define CXL_EXTENT_REMOVAL_POLICY_MASK 0x0F +#define CXL_FORCED_REMOVAL_MASK (1 << 4) +/* + * CXL r3.2 Section 7.6.7.6.6: + * Initiate Dynamic Capacity Release (Opcode 5605h) + */ +static CXLRetCode cmd_fm_initiate_dc_release(const struct cxl_cmd *cmd, + uint8_t *payload_in, + size_t len_in, + uint8_t *payload_out, + size_t *len_out, + CXLCCI *cci) +{ + struct { + uint16_t host_id; + uint8_t flags; + uint8_t reg_num; + uint64_t length; + uint8_t tag[0x10]; + uint32_t ext_count; + CXLDCExtentRaw extents[]; + } QEMU_PACKED *in = (void *)payload_in; + CXLType3Dev *ct3d = CXL_TYPE3(cci->d); + int i, rc; + + switch (in->flags & CXL_EXTENT_REMOVAL_POLICY_MASK) { + case CXL_EXTENT_REMOVAL_POLICY_PRESCRIPTIVE: { + CXLDCExtentList updated_list; + uint32_t updated_list_size; + g_autofree CXLUpdateDCExtentListInPl *list = + g_malloc0(sizeof(*list) + + in->ext_count * sizeof(*list->updated_entries)); + + convert_raw_extents(in->extents, list, in->ext_count); + rc = cxl_detect_malformed_extent_list(ct3d, list); + if (rc) { + return rc; + } + + /* + * Fail with Invalid PA if an extent is pending and Forced Removal + * flag not set. + */ + if (!(in->flags & CXL_FORCED_REMOVAL_MASK)) { + for (i = 0; i < in->ext_count; i++) { + CXLDCExtentRaw ext = in->extents[i]; + /* + * Check requested extents don't overlap with pending + * extents. + */ + if (cxl_extent_groups_overlaps_dpa_range( + &ct3d->dc.extents_pending, + ext.start_dpa, + ext.len)) { + return CXL_MBOX_INVALID_PA; + } + } + } + + rc = cxl_dc_extent_release_dry_run(ct3d, + list, + &updated_list, + &updated_list_size); + if (rc) { + return rc; + } + cxl_create_dc_event_records_for_extents(ct3d, + DC_EVENT_RELEASE_CAPACITY, + in->extents, + in->ext_count); + return CXL_MBOX_SUCCESS; + } + default: { + qemu_log_mask(LOG_UNIMP, + "CXL extent removal policy not supported.\n"); + return CXL_MBOX_INVALID_INPUT; + } + } +} + static const struct cxl_cmd cxl_cmd_set[256][256] = { [INFOSTAT][BACKGROUND_OPERATION_ABORT] = { "BACKGROUND_OPERATION_ABORT", cmd_infostat_bg_op_abort, 0, 0 }, @@ -3340,6 +3839,36 @@ static const struct cxl_cmd cxl_cmd_set_sw[256][256] = { cmd_tunnel_management_cmd, ~0, 0 }, }; +static const struct cxl_cmd cxl_cmd_set_fm_dcd[256][256] = { + [FMAPI_DCD_MGMT][GET_DCD_INFO] = { "GET_DCD_INFO", + cmd_fm_get_dcd_info, 0, 0 }, + [FMAPI_DCD_MGMT][GET_HOST_DC_REGION_CONFIG] = { "GET_HOST_DC_REGION_CONFIG", + cmd_fm_get_host_dc_region_config, 4, 0 }, + [FMAPI_DCD_MGMT][SET_DC_REGION_CONFIG] = { "SET_DC_REGION_CONFIG", + cmd_fm_set_dc_region_config, 16, + (CXL_MBOX_CONFIG_CHANGE_COLD_RESET | + CXL_MBOX_CONFIG_CHANGE_CONV_RESET | + CXL_MBOX_CONFIG_CHANGE_CXL_RESET | + CXL_MBOX_IMMEDIATE_CONFIG_CHANGE | + CXL_MBOX_IMMEDIATE_DATA_CHANGE) }, + [FMAPI_DCD_MGMT][GET_DC_REGION_EXTENT_LIST] = { "GET_DC_REGION_EXTENT_LIST", + cmd_fm_get_dc_region_extent_list, 12, 0 }, + [FMAPI_DCD_MGMT][INITIATE_DC_ADD] = { "INIT_DC_ADD", + cmd_fm_initiate_dc_add, ~0, + (CXL_MBOX_CONFIG_CHANGE_COLD_RESET | + CXL_MBOX_CONFIG_CHANGE_CONV_RESET | + CXL_MBOX_CONFIG_CHANGE_CXL_RESET | + CXL_MBOX_IMMEDIATE_CONFIG_CHANGE | + CXL_MBOX_IMMEDIATE_DATA_CHANGE) }, + [FMAPI_DCD_MGMT][INITIATE_DC_RELEASE] = { "INIT_DC_RELEASE", + cmd_fm_initiate_dc_release, ~0, + (CXL_MBOX_CONFIG_CHANGE_COLD_RESET | + CXL_MBOX_CONFIG_CHANGE_CONV_RESET | + CXL_MBOX_CONFIG_CHANGE_CXL_RESET | + CXL_MBOX_IMMEDIATE_CONFIG_CHANGE | + CXL_MBOX_IMMEDIATE_DATA_CHANGE) }, +}; + /* * While the command is executing in the background, the device should * update the percentage complete in the Background Command Status Register @@ -3614,7 +4143,12 @@ void cxl_initialize_t3_fm_owned_ld_mctpcci(CXLCCI *cci, DeviceState *d, DeviceState *intf, size_t payload_max) { + CXLType3Dev *ct3d = CXL_TYPE3(d); + cxl_copy_cci_commands(cci, cxl_cmd_set_t3_fm_owned_ld_mctp); + if (ct3d->dc.num_regions) { + cxl_copy_cci_commands(cci, cxl_cmd_set_fm_dcd); + } cci->d = d; cci->intf = intf; cxl_init_cci(cci, payload_max); diff --git a/hw/display/artist.c b/hw/display/artist.c index 3fafc8a..3c884c9 100644 --- a/hw/display/artist.c +++ b/hw/display/artist.c @@ -12,6 +12,7 @@ #include "qemu/log.h" #include "qemu/module.h" #include "qemu/units.h" +#include "qemu/bswap.h" #include "qapi/error.h" #include "hw/sysbus.h" #include "hw/loader.h" diff --git a/hw/display/ati.c b/hw/display/ati.c index 7de2773..f7c0006 100644 --- a/hw/display/ati.c +++ b/hw/display/ati.c @@ -22,6 +22,7 @@ #include "vga-access.h" #include "hw/qdev-properties.h" #include "vga_regs.h" +#include "qemu/bswap.h" #include "qemu/log.h" #include "qemu/module.h" #include "qemu/error-report.h" diff --git a/hw/display/vga.c b/hw/display/vga.c index 20475eb..90b89cf 100644 --- a/hw/display/vga.c +++ b/hw/display/vga.c @@ -26,7 +26,7 @@ #include "qemu/units.h" #include "system/reset.h" #include "qapi/error.h" -#include "exec/tswap.h" +#include "qemu/target-info.h" #include "hw/display/vga.h" #include "hw/i386/x86.h" #include "hw/pci/pci.h" diff --git a/hw/i386/acpi-build.c b/hw/i386/acpi-build.c index 61851cc..423c495 100644 --- a/hw/i386/acpi-build.c +++ b/hw/i386/acpi-build.c @@ -338,405 +338,6 @@ build_facs(GArray *table_data) g_array_append_vals(table_data, reserved, 40); /* Reserved */ } -Aml *aml_pci_device_dsm(void) -{ - Aml *method; - - method = aml_method("_DSM", 4, AML_SERIALIZED); - { - Aml *params = aml_local(0); - Aml *pkg = aml_package(2); - aml_append(pkg, aml_int(0)); - aml_append(pkg, aml_int(0)); - aml_append(method, aml_store(pkg, params)); - aml_append(method, - aml_store(aml_name("BSEL"), aml_index(params, aml_int(0)))); - aml_append(method, - aml_store(aml_name("ASUN"), aml_index(params, aml_int(1)))); - aml_append(method, - aml_return(aml_call5("PDSM", aml_arg(0), aml_arg(1), - aml_arg(2), aml_arg(3), params)) - ); - } - return method; -} - -static void build_append_pci_dsm_func0_common(Aml *ctx, Aml *retvar) -{ - Aml *UUID, *ifctx1; - uint8_t byte_list[1] = { 0 }; /* nothing supported yet */ - - aml_append(ctx, aml_store(aml_buffer(1, byte_list), retvar)); - /* - * PCI Firmware Specification 3.1 - * 4.6. _DSM Definitions for PCI - */ - UUID = aml_touuid("E5C937D0-3553-4D7A-9117-EA4D19C3434D"); - ifctx1 = aml_if(aml_lnot(aml_equal(aml_arg(0), UUID))); - { - /* call is for unsupported UUID, bail out */ - aml_append(ifctx1, aml_return(retvar)); - } - aml_append(ctx, ifctx1); - - ifctx1 = aml_if(aml_lless(aml_arg(1), aml_int(2))); - { - /* call is for unsupported REV, bail out */ - aml_append(ifctx1, aml_return(retvar)); - } - aml_append(ctx, ifctx1); -} - -static Aml *aml_pci_edsm(void) -{ - Aml *method, *ifctx; - Aml *zero = aml_int(0); - Aml *func = aml_arg(2); - Aml *ret = aml_local(0); - Aml *aidx = aml_local(1); - Aml *params = aml_arg(4); - - method = aml_method("EDSM", 5, AML_SERIALIZED); - - /* get supported functions */ - ifctx = aml_if(aml_equal(func, zero)); - { - /* 1: have supported functions */ - /* 7: support for function 7 */ - const uint8_t caps = 1 | BIT(7); - build_append_pci_dsm_func0_common(ifctx, ret); - aml_append(ifctx, aml_store(aml_int(caps), aml_index(ret, zero))); - aml_append(ifctx, aml_return(ret)); - } - aml_append(method, ifctx); - - /* handle specific functions requests */ - /* - * PCI Firmware Specification 3.1 - * 4.6.7. _DSM for Naming a PCI or PCI Express Device Under - * Operating Systems - */ - ifctx = aml_if(aml_equal(func, aml_int(7))); - { - Aml *pkg = aml_package(2); - aml_append(pkg, zero); - /* optional, if not impl. should return null string */ - aml_append(pkg, aml_string("%s", "")); - aml_append(ifctx, aml_store(pkg, ret)); - - /* - * IASL is fine when initializing Package with computational data, - * however it makes guest unhappy /it fails to process such AML/. - * So use runtime assignment to set acpi-index after initializer - * to make OSPM happy. - */ - aml_append(ifctx, - aml_store(aml_derefof(aml_index(params, aml_int(0))), aidx)); - aml_append(ifctx, aml_store(aidx, aml_index(ret, zero))); - aml_append(ifctx, aml_return(ret)); - } - aml_append(method, ifctx); - - return method; -} - -static Aml *aml_pci_static_endpoint_dsm(PCIDevice *pdev) -{ - Aml *method; - - g_assert(pdev->acpi_index != 0); - method = aml_method("_DSM", 4, AML_SERIALIZED); - { - Aml *params = aml_local(0); - Aml *pkg = aml_package(1); - aml_append(pkg, aml_int(pdev->acpi_index)); - aml_append(method, aml_store(pkg, params)); - aml_append(method, - aml_return(aml_call5("EDSM", aml_arg(0), aml_arg(1), - aml_arg(2), aml_arg(3), params)) - ); - } - return method; -} - -static void build_append_pcihp_notify_entry(Aml *method, int slot) -{ - Aml *if_ctx; - int32_t devfn = PCI_DEVFN(slot, 0); - - if_ctx = aml_if(aml_and(aml_arg(0), aml_int(0x1U << slot), NULL)); - aml_append(if_ctx, aml_notify(aml_name("S%.02X", devfn), aml_arg(1))); - aml_append(method, if_ctx); -} - -static bool is_devfn_ignored_generic(const int devfn, const PCIBus *bus) -{ - const PCIDevice *pdev = bus->devices[devfn]; - - if (PCI_FUNC(devfn)) { - if (IS_PCI_BRIDGE(pdev)) { - /* - * Ignore only hotplugged PCI bridges on !0 functions, but - * allow describing cold plugged bridges on all functions - */ - if (DEVICE(pdev)->hotplugged) { - return true; - } - } - } - return false; -} - -static bool is_devfn_ignored_hotplug(const int devfn, const PCIBus *bus) -{ - PCIDevice *pdev = bus->devices[devfn]; - if (pdev) { - return is_devfn_ignored_generic(devfn, bus) || - !DEVICE_GET_CLASS(pdev)->hotpluggable || - /* Cold plugged bridges aren't themselves hot-pluggable */ - (IS_PCI_BRIDGE(pdev) && !DEVICE(pdev)->hotplugged); - } else { /* non populated slots */ - /* - * hotplug is supported only for non-multifunction device - * so generate device description only for function 0 - */ - if (PCI_FUNC(devfn) || - (pci_bus_is_express(bus) && PCI_SLOT(devfn) > 0)) { - return true; - } - } - return false; -} - -void build_append_pcihp_slots(Aml *parent_scope, PCIBus *bus) -{ - int devfn; - Aml *dev, *notify_method = NULL, *method; - QObject *bsel = object_property_get_qobject(OBJECT(bus), - ACPI_PCIHP_PROP_BSEL, NULL); - uint64_t bsel_val = qnum_get_uint(qobject_to(QNum, bsel)); - qobject_unref(bsel); - - aml_append(parent_scope, aml_name_decl("BSEL", aml_int(bsel_val))); - notify_method = aml_method("DVNT", 2, AML_NOTSERIALIZED); - - for (devfn = 0; devfn < ARRAY_SIZE(bus->devices); devfn++) { - int slot = PCI_SLOT(devfn); - int adr = slot << 16 | PCI_FUNC(devfn); - - if (is_devfn_ignored_hotplug(devfn, bus)) { - continue; - } - - if (bus->devices[devfn]) { - dev = aml_scope("S%.02X", devfn); - } else { - dev = aml_device("S%.02X", devfn); - aml_append(dev, aml_name_decl("_ADR", aml_int(adr))); - } - - /* - * Can't declare _SUN here for every device as it changes 'slot' - * enumeration order in linux kernel, so use another variable for it - */ - aml_append(dev, aml_name_decl("ASUN", aml_int(slot))); - aml_append(dev, aml_pci_device_dsm()); - - aml_append(dev, aml_name_decl("_SUN", aml_int(slot))); - /* add _EJ0 to make slot hotpluggable */ - method = aml_method("_EJ0", 1, AML_NOTSERIALIZED); - aml_append(method, - aml_call2("PCEJ", aml_name("BSEL"), aml_name("_SUN")) - ); - aml_append(dev, method); - - build_append_pcihp_notify_entry(notify_method, slot); - - /* device descriptor has been composed, add it into parent context */ - aml_append(parent_scope, dev); - } - aml_append(parent_scope, notify_method); -} - -void build_append_pci_bus_devices(Aml *parent_scope, PCIBus *bus) -{ - int devfn; - Aml *dev; - - for (devfn = 0; devfn < ARRAY_SIZE(bus->devices); devfn++) { - /* ACPI spec: 1.0b: Table 6-2 _ADR Object Bus Types, PCI type */ - int adr = PCI_SLOT(devfn) << 16 | PCI_FUNC(devfn); - PCIDevice *pdev = bus->devices[devfn]; - - if (!pdev || is_devfn_ignored_generic(devfn, bus)) { - continue; - } - - /* start to compose PCI device descriptor */ - dev = aml_device("S%.02X", devfn); - aml_append(dev, aml_name_decl("_ADR", aml_int(adr))); - - call_dev_aml_func(DEVICE(bus->devices[devfn]), dev); - /* add _DSM if device has acpi-index set */ - if (pdev->acpi_index && - !object_property_get_bool(OBJECT(pdev), "hotpluggable", - &error_abort)) { - aml_append(dev, aml_pci_static_endpoint_dsm(pdev)); - } - - /* device descriptor has been composed, add it into parent context */ - aml_append(parent_scope, dev); - } -} - -static bool build_append_notification_callback(Aml *parent_scope, - const PCIBus *bus) -{ - Aml *method; - PCIBus *sec; - QObject *bsel; - int nr_notifiers = 0; - GQueue *pcnt_bus_list = g_queue_new(); - - QLIST_FOREACH(sec, &bus->child, sibling) { - Aml *br_scope = aml_scope("S%.02X", sec->parent_dev->devfn); - if (pci_bus_is_root(sec)) { - continue; - } - nr_notifiers = nr_notifiers + - build_append_notification_callback(br_scope, sec); - /* - * add new child scope to parent - * and keep track of bus that have PCNT, - * bus list is used later to call children PCNTs from this level PCNT - */ - if (nr_notifiers) { - g_queue_push_tail(pcnt_bus_list, sec); - aml_append(parent_scope, br_scope); - } - } - - /* - * Append PCNT method to notify about events on local and child buses. - * ps: hostbridge might not have hotplug (bsel) enabled but might have - * child bridges that do have bsel. - */ - method = aml_method("PCNT", 0, AML_NOTSERIALIZED); - - /* If bus supports hotplug select it and notify about local events */ - bsel = object_property_get_qobject(OBJECT(bus), ACPI_PCIHP_PROP_BSEL, NULL); - if (bsel) { - uint64_t bsel_val = qnum_get_uint(qobject_to(QNum, bsel)); - - aml_append(method, aml_store(aml_int(bsel_val), aml_name("BNUM"))); - aml_append(method, aml_call2("DVNT", aml_name("PCIU"), - aml_int(1))); /* Device Check */ - aml_append(method, aml_call2("DVNT", aml_name("PCID"), - aml_int(3))); /* Eject Request */ - nr_notifiers++; - } - - /* Notify about child bus events in any case */ - while ((sec = g_queue_pop_head(pcnt_bus_list))) { - aml_append(method, aml_name("^S%.02X.PCNT", sec->parent_dev->devfn)); - } - - aml_append(parent_scope, method); - qobject_unref(bsel); - g_queue_free(pcnt_bus_list); - return !!nr_notifiers; -} - -static Aml *aml_pci_pdsm(void) -{ - Aml *method, *ifctx, *ifctx1; - Aml *ret = aml_local(0); - Aml *caps = aml_local(1); - Aml *acpi_index = aml_local(2); - Aml *zero = aml_int(0); - Aml *one = aml_int(1); - Aml *not_supp = aml_int(0xFFFFFFFF); - Aml *func = aml_arg(2); - Aml *params = aml_arg(4); - Aml *bnum = aml_derefof(aml_index(params, aml_int(0))); - Aml *sunum = aml_derefof(aml_index(params, aml_int(1))); - - method = aml_method("PDSM", 5, AML_SERIALIZED); - - /* get supported functions */ - ifctx = aml_if(aml_equal(func, zero)); - { - build_append_pci_dsm_func0_common(ifctx, ret); - - aml_append(ifctx, aml_store(zero, caps)); - aml_append(ifctx, - aml_store(aml_call2("AIDX", bnum, sunum), acpi_index)); - /* - * advertise function 7 if device has acpi-index - * acpi_index values: - * 0: not present (default value) - * FFFFFFFF: not supported (old QEMU without PIDX reg) - * other: device's acpi-index - */ - ifctx1 = aml_if(aml_lnot( - aml_or(aml_equal(acpi_index, zero), - aml_equal(acpi_index, not_supp), NULL) - )); - { - /* have supported functions */ - aml_append(ifctx1, aml_or(caps, one, caps)); - /* support for function 7 */ - aml_append(ifctx1, - aml_or(caps, aml_shiftleft(one, aml_int(7)), caps)); - } - aml_append(ifctx, ifctx1); - - aml_append(ifctx, aml_store(caps, aml_index(ret, zero))); - aml_append(ifctx, aml_return(ret)); - } - aml_append(method, ifctx); - - /* handle specific functions requests */ - /* - * PCI Firmware Specification 3.1 - * 4.6.7. _DSM for Naming a PCI or PCI Express Device Under - * Operating Systems - */ - ifctx = aml_if(aml_equal(func, aml_int(7))); - { - Aml *pkg = aml_package(2); - - aml_append(ifctx, aml_store(aml_call2("AIDX", bnum, sunum), acpi_index)); - aml_append(ifctx, aml_store(pkg, ret)); - /* - * Windows calls func=7 without checking if it's available, - * as workaround Microsoft has suggested to return invalid for func7 - * Package, so return 2 elements package but only initialize elements - * when acpi_index is supported and leave them uninitialized, which - * leads elements to being Uninitialized ObjectType and should trip - * Windows into discarding result as an unexpected and prevent setting - * bogus 'PCI Label' on the device. - */ - ifctx1 = aml_if(aml_lnot(aml_lor( - aml_equal(acpi_index, zero), aml_equal(acpi_index, not_supp) - ))); - { - aml_append(ifctx1, aml_store(acpi_index, aml_index(ret, zero))); - /* - * optional, if not impl. should return null string - */ - aml_append(ifctx1, aml_store(aml_string("%s", ""), - aml_index(ret, one))); - } - aml_append(ifctx, ifctx1); - - aml_append(ifctx, aml_return(ret)); - } - - aml_append(method, ifctx); - return method; -} - /* * build_prt - Define interrupt routing rules * @@ -1227,112 +828,6 @@ static Aml *build_q35_dram_controller(const AcpiMcfgInfo *mcfg) return dev; } -static void build_x86_acpi_pci_hotplug(Aml *table, uint64_t pcihp_addr) -{ - Aml *scope; - Aml *field; - Aml *method; - - scope = aml_scope("_SB.PCI0"); - - aml_append(scope, - aml_operation_region("PCST", AML_SYSTEM_IO, aml_int(pcihp_addr), 0x08)); - field = aml_field("PCST", AML_DWORD_ACC, AML_NOLOCK, AML_WRITE_AS_ZEROS); - aml_append(field, aml_named_field("PCIU", 32)); - aml_append(field, aml_named_field("PCID", 32)); - aml_append(scope, field); - - aml_append(scope, - aml_operation_region("SEJ", AML_SYSTEM_IO, - aml_int(pcihp_addr + ACPI_PCIHP_SEJ_BASE), 0x04)); - field = aml_field("SEJ", AML_DWORD_ACC, AML_NOLOCK, AML_WRITE_AS_ZEROS); - aml_append(field, aml_named_field("B0EJ", 32)); - aml_append(scope, field); - - aml_append(scope, - aml_operation_region("BNMR", AML_SYSTEM_IO, - aml_int(pcihp_addr + ACPI_PCIHP_BNMR_BASE), 0x08)); - field = aml_field("BNMR", AML_DWORD_ACC, AML_NOLOCK, AML_WRITE_AS_ZEROS); - aml_append(field, aml_named_field("BNUM", 32)); - aml_append(field, aml_named_field("PIDX", 32)); - aml_append(scope, field); - - aml_append(scope, aml_mutex("BLCK", 0)); - - method = aml_method("PCEJ", 2, AML_NOTSERIALIZED); - aml_append(method, aml_acquire(aml_name("BLCK"), 0xFFFF)); - aml_append(method, aml_store(aml_arg(0), aml_name("BNUM"))); - aml_append(method, - aml_store(aml_shiftleft(aml_int(1), aml_arg(1)), aml_name("B0EJ"))); - aml_append(method, aml_release(aml_name("BLCK"))); - aml_append(method, aml_return(aml_int(0))); - aml_append(scope, method); - - method = aml_method("AIDX", 2, AML_NOTSERIALIZED); - aml_append(method, aml_acquire(aml_name("BLCK"), 0xFFFF)); - aml_append(method, aml_store(aml_arg(0), aml_name("BNUM"))); - aml_append(method, - aml_store(aml_shiftleft(aml_int(1), aml_arg(1)), aml_name("PIDX"))); - aml_append(method, aml_store(aml_name("PIDX"), aml_local(0))); - aml_append(method, aml_release(aml_name("BLCK"))); - aml_append(method, aml_return(aml_local(0))); - aml_append(scope, method); - - aml_append(scope, aml_pci_pdsm()); - - aml_append(table, scope); -} - -static Aml *build_q35_osc_method(bool enable_native_pcie_hotplug) -{ - Aml *if_ctx; - Aml *if_ctx2; - Aml *else_ctx; - Aml *method; - Aml *a_cwd1 = aml_name("CDW1"); - Aml *a_ctrl = aml_local(0); - - method = aml_method("_OSC", 4, AML_NOTSERIALIZED); - aml_append(method, aml_create_dword_field(aml_arg(3), aml_int(0), "CDW1")); - - if_ctx = aml_if(aml_equal( - aml_arg(0), aml_touuid("33DB4D5B-1FF7-401C-9657-7441C03DD766"))); - aml_append(if_ctx, aml_create_dword_field(aml_arg(3), aml_int(4), "CDW2")); - aml_append(if_ctx, aml_create_dword_field(aml_arg(3), aml_int(8), "CDW3")); - - aml_append(if_ctx, aml_store(aml_name("CDW3"), a_ctrl)); - - /* - * Always allow native PME, AER (no dependencies) - * Allow SHPC (PCI bridges can have SHPC controller) - * Disable PCIe Native Hot-plug if ACPI PCI Hot-plug is enabled. - */ - aml_append(if_ctx, aml_and(a_ctrl, - aml_int(0x1E | (enable_native_pcie_hotplug ? 0x1 : 0x0)), a_ctrl)); - - if_ctx2 = aml_if(aml_lnot(aml_equal(aml_arg(1), aml_int(1)))); - /* Unknown revision */ - aml_append(if_ctx2, aml_or(a_cwd1, aml_int(0x08), a_cwd1)); - aml_append(if_ctx, if_ctx2); - - if_ctx2 = aml_if(aml_lnot(aml_equal(aml_name("CDW3"), a_ctrl))); - /* Capabilities bits were masked */ - aml_append(if_ctx2, aml_or(a_cwd1, aml_int(0x10), a_cwd1)); - aml_append(if_ctx, if_ctx2); - - /* Update DWORD3 in the buffer */ - aml_append(if_ctx, aml_store(a_ctrl, aml_name("CDW3"))); - aml_append(method, if_ctx); - - else_ctx = aml_else(); - /* Unrecognized UUID */ - aml_append(else_ctx, aml_or(a_cwd1, aml_int(4), a_cwd1)); - aml_append(method, else_ctx); - - aml_append(method, aml_return(aml_arg(3))); - return method; -} - static void build_acpi0017(Aml *table) { Aml *dev, *scope, *method; @@ -1389,12 +884,12 @@ build_dsdt(GArray *table_data, BIOSLinker *linker, dev = aml_device("PCI0"); aml_append(dev, aml_name_decl("_HID", aml_eisaid("PNP0A03"))); aml_append(dev, aml_name_decl("_UID", aml_int(pcmc->pci_root_uid))); - aml_append(dev, aml_pci_edsm()); + aml_append(dev, build_pci_bridge_edsm()); aml_append(sb_scope, dev); aml_append(dsdt, sb_scope); if (pm->pcihp_bridge_en || pm->pcihp_root_en) { - build_x86_acpi_pci_hotplug(dsdt, pm->pcihp_io_base); + build_acpi_pci_hotplug(dsdt, AML_SYSTEM_IO, pm->pcihp_io_base); } build_piix4_pci0_int(dsdt); } else if (q35) { @@ -1403,8 +898,8 @@ build_dsdt(GArray *table_data, BIOSLinker *linker, aml_append(dev, aml_name_decl("_HID", aml_eisaid("PNP0A08"))); aml_append(dev, aml_name_decl("_CID", aml_eisaid("PNP0A03"))); aml_append(dev, aml_name_decl("_UID", aml_int(pcmc->pci_root_uid))); - aml_append(dev, build_q35_osc_method(!pm->pcihp_bridge_en)); - aml_append(dev, aml_pci_edsm()); + aml_append(dev, build_pci_host_bridge_osc_method(!pm->pcihp_bridge_en)); + aml_append(dev, build_pci_bridge_edsm()); aml_append(sb_scope, dev); if (mcfg_valid) { aml_append(sb_scope, build_q35_dram_controller(&mcfg)); @@ -1438,7 +933,7 @@ build_dsdt(GArray *table_data, BIOSLinker *linker, aml_append(dsdt, sb_scope); if (pm->pcihp_bridge_en) { - build_x86_acpi_pci_hotplug(dsdt, pm->pcihp_io_base); + build_acpi_pci_hotplug(dsdt, AML_SYSTEM_IO, pm->pcihp_io_base); } build_q35_pci0_int(dsdt); } @@ -1525,7 +1020,7 @@ build_dsdt(GArray *table_data, BIOSLinker *linker, aml_append(dev, aml_name_decl("_CID", aml_eisaid("PNP0A03"))); /* Expander bridges do not have ACPI PCI Hot-plug enabled */ - aml_append(dev, build_q35_osc_method(true)); + aml_append(dev, build_pci_host_bridge_osc_method(true)); } else { aml_append(dev, aml_name_decl("_HID", aml_eisaid("PNP0A03"))); } @@ -1654,19 +1149,8 @@ build_dsdt(GArray *table_data, BIOSLinker *linker, /* reserve PCIHP resources */ if (pm->pcihp_io_len && (pm->pcihp_bridge_en || pm->pcihp_root_en)) { - dev = aml_device("PHPR"); - aml_append(dev, aml_name_decl("_HID", aml_string("PNP0A06"))); - aml_append(dev, - aml_name_decl("_UID", aml_string("PCI Hotplug resources"))); - /* device present, functioning, decoding, not shown in UI */ - aml_append(dev, aml_name_decl("_STA", aml_int(0xB))); - crs = aml_resource_template(); - aml_append(crs, - aml_io(AML_DECODE16, pm->pcihp_io_base, pm->pcihp_io_base, 1, - pm->pcihp_io_len) - ); - aml_append(dev, aml_name_decl("_CRS", crs)); - aml_append(scope, dev); + build_append_pcihp_resources(scope, + pm->pcihp_io_base, pm->pcihp_io_len); } aml_append(dsdt, scope); diff --git a/hw/i386/acpi-build.h b/hw/i386/acpi-build.h index 275ec05..8ba3c33 100644 --- a/hw/i386/acpi-build.h +++ b/hw/i386/acpi-build.h @@ -5,10 +5,6 @@ extern const struct AcpiGenericAddress x86_nvdimm_acpi_dsmio; -/* PCI Hot-plug registers' base. See docs/specs/acpi_pci_hotplug.rst */ -#define ACPI_PCIHP_SEJ_BASE 0x8 -#define ACPI_PCIHP_BNMR_BASE 0x10 - void acpi_setup(void); Object *acpi_get_i386_pci_host(void); diff --git a/hw/i386/amd_iommu.c b/hw/i386/amd_iommu.c index 963aa24..5a24c17 100644 --- a/hw/i386/amd_iommu.c +++ b/hw/i386/amd_iommu.c @@ -140,7 +140,7 @@ static void amdvi_writeq(AMDVIState *s, hwaddr addr, uint64_t val) { uint64_t romask = ldq_le_p(&s->romask[addr]); uint64_t w1cmask = ldq_le_p(&s->w1cmask[addr]); - uint32_t oldval = ldq_le_p(&s->mmior[addr]); + uint64_t oldval = ldq_le_p(&s->mmior[addr]); stq_le_p(&s->mmior[addr], ((oldval & romask) | (val & ~romask)) & ~(val & w1cmask)); } @@ -508,7 +508,7 @@ static void amdvi_inval_inttable(AMDVIState *s, uint64_t *cmd) static void iommu_inval_iotlb(AMDVIState *s, uint64_t *cmd) { - uint16_t devid = extract64(cmd[0], 0, 16); + uint16_t devid = cpu_to_le16(extract64(cmd[0], 0, 16)); if (extract64(cmd[1], 1, 1) || extract64(cmd[1], 3, 1) || extract64(cmd[1], 6, 6)) { amdvi_log_illegalcom_error(s, extract64(cmd[0], 60, 4), @@ -521,7 +521,7 @@ static void iommu_inval_iotlb(AMDVIState *s, uint64_t *cmd) &devid); } else { amdvi_iotlb_remove_page(s, cpu_to_le64(extract64(cmd[1], 12, 52)) << 12, - cpu_to_le16(extract64(cmd[1], 0, 16))); + devid); } trace_amdvi_iotlb_inval(); } @@ -665,8 +665,8 @@ static inline void amdvi_handle_devtab_write(AMDVIState *s) uint64_t val = amdvi_readq(s, AMDVI_MMIO_DEVICE_TABLE); s->devtab = (val & AMDVI_MMIO_DEVTAB_BASE_MASK); - /* set device table length */ - s->devtab_len = ((val & AMDVI_MMIO_DEVTAB_SIZE_MASK) + 1 * + /* set device table length (i.e. number of entries table can hold) */ + s->devtab_len = (((val & AMDVI_MMIO_DEVTAB_SIZE_MASK) + 1) * (AMDVI_MMIO_DEVTAB_SIZE_UNIT / AMDVI_MMIO_DEVTAB_ENTRY_SIZE)); } @@ -848,9 +848,10 @@ static inline uint64_t amdvi_get_perms(uint64_t entry) static bool amdvi_validate_dte(AMDVIState *s, uint16_t devid, uint64_t *dte) { - if ((dte[0] & AMDVI_DTE_LOWER_QUAD_RESERVED) - || (dte[1] & AMDVI_DTE_MIDDLE_QUAD_RESERVED) - || (dte[2] & AMDVI_DTE_UPPER_QUAD_RESERVED) || dte[3]) { + if ((dte[0] & AMDVI_DTE_QUAD0_RESERVED) || + (dte[1] & AMDVI_DTE_QUAD1_RESERVED) || + (dte[2] & AMDVI_DTE_QUAD2_RESERVED) || + (dte[3] & AMDVI_DTE_QUAD3_RESERVED)) { amdvi_log_illegaldevtab_error(s, devid, s->devtab + devid * AMDVI_DEVTAB_ENTRY_SIZE, 0); diff --git a/hw/i386/amd_iommu.h b/hw/i386/amd_iommu.h index 5672bde..8b42913 100644 --- a/hw/i386/amd_iommu.h +++ b/hw/i386/amd_iommu.h @@ -25,6 +25,8 @@ #include "hw/i386/x86-iommu.h" #include "qom/object.h" +#define GENMASK64(h, l) (((~0ULL) >> (63 - (h) + (l))) << (l)) + /* Capability registers */ #define AMDVI_CAPAB_BAR_LOW 0x04 #define AMDVI_CAPAB_BAR_HIGH 0x08 @@ -66,34 +68,34 @@ #define AMDVI_MMIO_SIZE 0x4000 -#define AMDVI_MMIO_DEVTAB_SIZE_MASK ((1ULL << 12) - 1) -#define AMDVI_MMIO_DEVTAB_BASE_MASK (((1ULL << 52) - 1) & ~ \ - AMDVI_MMIO_DEVTAB_SIZE_MASK) +#define AMDVI_MMIO_DEVTAB_SIZE_MASK GENMASK64(8, 0) +#define AMDVI_MMIO_DEVTAB_BASE_MASK GENMASK64(51, 12) + #define AMDVI_MMIO_DEVTAB_ENTRY_SIZE 32 #define AMDVI_MMIO_DEVTAB_SIZE_UNIT 4096 /* some of this are similar but just for readability */ #define AMDVI_MMIO_CMDBUF_SIZE_BYTE (AMDVI_MMIO_COMMAND_BASE + 7) #define AMDVI_MMIO_CMDBUF_SIZE_MASK 0x0f -#define AMDVI_MMIO_CMDBUF_BASE_MASK AMDVI_MMIO_DEVTAB_BASE_MASK -#define AMDVI_MMIO_CMDBUF_HEAD_MASK (((1ULL << 19) - 1) & ~0x0f) -#define AMDVI_MMIO_CMDBUF_TAIL_MASK AMDVI_MMIO_EVTLOG_HEAD_MASK +#define AMDVI_MMIO_CMDBUF_BASE_MASK GENMASK64(51, 12) +#define AMDVI_MMIO_CMDBUF_HEAD_MASK GENMASK64(18, 4) +#define AMDVI_MMIO_CMDBUF_TAIL_MASK GENMASK64(18, 4) #define AMDVI_MMIO_EVTLOG_SIZE_BYTE (AMDVI_MMIO_EVENT_BASE + 7) -#define AMDVI_MMIO_EVTLOG_SIZE_MASK AMDVI_MMIO_CMDBUF_SIZE_MASK -#define AMDVI_MMIO_EVTLOG_BASE_MASK AMDVI_MMIO_CMDBUF_BASE_MASK -#define AMDVI_MMIO_EVTLOG_HEAD_MASK (((1ULL << 19) - 1) & ~0x0f) -#define AMDVI_MMIO_EVTLOG_TAIL_MASK AMDVI_MMIO_EVTLOG_HEAD_MASK +#define AMDVI_MMIO_EVTLOG_SIZE_MASK 0x0f +#define AMDVI_MMIO_EVTLOG_BASE_MASK GENMASK64(51, 12) +#define AMDVI_MMIO_EVTLOG_HEAD_MASK GENMASK64(18, 4) +#define AMDVI_MMIO_EVTLOG_TAIL_MASK GENMASK64(18, 4) -#define AMDVI_MMIO_PPRLOG_SIZE_BYTE (AMDVI_MMIO_EVENT_BASE + 7) -#define AMDVI_MMIO_PPRLOG_HEAD_MASK AMDVI_MMIO_EVTLOG_HEAD_MASK -#define AMDVI_MMIO_PPRLOG_TAIL_MASK AMDVI_MMIO_EVTLOG_HEAD_MASK -#define AMDVI_MMIO_PPRLOG_BASE_MASK AMDVI_MMIO_EVTLOG_BASE_MASK -#define AMDVI_MMIO_PPRLOG_SIZE_MASK AMDVI_MMIO_EVTLOG_SIZE_MASK +#define AMDVI_MMIO_PPRLOG_SIZE_BYTE (AMDVI_MMIO_PPR_BASE + 7) +#define AMDVI_MMIO_PPRLOG_SIZE_MASK 0x0f +#define AMDVI_MMIO_PPRLOG_BASE_MASK GENMASK64(51, 12) +#define AMDVI_MMIO_PPRLOG_HEAD_MASK GENMASK64(18, 4) +#define AMDVI_MMIO_PPRLOG_TAIL_MASK GENMASK64(18, 4) #define AMDVI_MMIO_EXCL_ENABLED_MASK (1ULL << 0) #define AMDVI_MMIO_EXCL_ALLOW_MASK (1ULL << 1) -#define AMDVI_MMIO_EXCL_LIMIT_MASK AMDVI_MMIO_DEVTAB_BASE_MASK +#define AMDVI_MMIO_EXCL_LIMIT_MASK GENMASK64(51, 12) #define AMDVI_MMIO_EXCL_LIMIT_LOW 0xfff /* mmio control register flags */ @@ -130,14 +132,14 @@ #define AMDVI_DEV_TRANSLATION_VALID (1ULL << 1) #define AMDVI_DEV_MODE_MASK 0x7 #define AMDVI_DEV_MODE_RSHIFT 9 -#define AMDVI_DEV_PT_ROOT_MASK 0xffffffffff000 +#define AMDVI_DEV_PT_ROOT_MASK GENMASK64(51, 12) #define AMDVI_DEV_PT_ROOT_RSHIFT 12 #define AMDVI_DEV_PERM_SHIFT 61 #define AMDVI_DEV_PERM_READ (1ULL << 61) #define AMDVI_DEV_PERM_WRITE (1ULL << 62) /* Device table entry bits 64:127 */ -#define AMDVI_DEV_DOMID_ID_MASK ((1ULL << 16) - 1) +#define AMDVI_DEV_DOMID_ID_MASK GENMASK64(15, 0) /* Event codes and flags, as stored in the info field */ #define AMDVI_EVENT_ILLEGAL_DEVTAB_ENTRY (0x1U << 12) @@ -162,9 +164,10 @@ #define AMDVI_FEATURE_PC (1ULL << 9) /* Perf counters */ /* reserved DTE bits */ -#define AMDVI_DTE_LOWER_QUAD_RESERVED 0x80300000000000fc -#define AMDVI_DTE_MIDDLE_QUAD_RESERVED 0x0000000000000100 -#define AMDVI_DTE_UPPER_QUAD_RESERVED 0x08f0000000000000 +#define AMDVI_DTE_QUAD0_RESERVED (GENMASK64(6, 2) | GENMASK64(63, 63)) +#define AMDVI_DTE_QUAD1_RESERVED 0 +#define AMDVI_DTE_QUAD2_RESERVED GENMASK64(53, 52) +#define AMDVI_DTE_QUAD3_RESERVED (GENMASK64(14, 0) | GENMASK64(53, 48)) /* AMDVI paging mode */ #define AMDVI_GATS_MODE (2ULL << 12) @@ -194,16 +197,12 @@ #define AMDVI_PAGE_SIZE (1ULL << AMDVI_PAGE_SHIFT) #define AMDVI_PAGE_SHIFT_4K 12 -#define AMDVI_PAGE_MASK_4K (~((1ULL << AMDVI_PAGE_SHIFT_4K) - 1)) - -#define AMDVI_MAX_VA_ADDR (48UL << 5) -#define AMDVI_MAX_PH_ADDR (40UL << 8) -#define AMDVI_MAX_GVA_ADDR (48UL << 15) +#define AMDVI_PAGE_MASK_4K GENMASK64(63, 12) -/* Completion Wait data size */ -#define AMDVI_COMPLETION_DATA_SIZE 8 +#define AMDVI_MAX_GVA_ADDR (2UL << 5) +#define AMDVI_MAX_PH_ADDR (40UL << 8) +#define AMDVI_MAX_VA_ADDR (48UL << 15) -#define AMDVI_COMMAND_SIZE 16 /* Completion Wait data size */ #define AMDVI_COMPLETION_DATA_SIZE 8 @@ -228,7 +227,7 @@ #define AMDVI_IR_INTCTL_PASS 1 #define AMDVI_IR_INTCTL_REMAP 2 -#define AMDVI_IR_PHYS_ADDR_MASK (((1ULL << 45) - 1) << 6) +#define AMDVI_IR_PHYS_ADDR_MASK GENMASK64(51, 6) /* MSI data 10:0 bits (section 2.2.5.1 Fig 14) */ #define AMDVI_IRTE_OFFSET 0x7ff diff --git a/hw/i386/intel_iommu.c b/hw/i386/intel_iommu.c index 69d72ad..fe9a5f2 100644 --- a/hw/i386/intel_iommu.c +++ b/hw/i386/intel_iommu.c @@ -1987,9 +1987,9 @@ static int vtd_iova_to_flpte(IntelIOMMUState *s, VTDContextEntry *ce, uint32_t pasid) { dma_addr_t addr = vtd_get_iova_pgtbl_base(s, ce, pasid); - uint32_t level = vtd_get_iova_level(s, ce, pasid); uint32_t offset; uint64_t flpte, flag_ad = VTD_FL_A; + *flpte_level = vtd_get_iova_level(s, ce, pasid); if (!vtd_iova_fl_check_canonical(s, iova, ce, pasid)) { error_report_once("%s: detected non canonical IOVA (iova=0x%" PRIx64 "," @@ -1998,11 +1998,11 @@ static int vtd_iova_to_flpte(IntelIOMMUState *s, VTDContextEntry *ce, } while (true) { - offset = vtd_iova_level_offset(iova, level); + offset = vtd_iova_level_offset(iova, *flpte_level); flpte = vtd_get_pte(addr, offset); if (flpte == (uint64_t)-1) { - if (level == vtd_get_iova_level(s, ce, pasid)) { + if (*flpte_level == vtd_get_iova_level(s, ce, pasid)) { /* Invalid programming of pasid-entry */ return -VTD_FR_PASID_ENTRY_FSPTPTR_INV; } else { @@ -2028,15 +2028,15 @@ static int vtd_iova_to_flpte(IntelIOMMUState *s, VTDContextEntry *ce, if (is_write && !(flpte & VTD_FL_RW)) { return -VTD_FR_SM_WRITE; } - if (vtd_flpte_nonzero_rsvd(flpte, level)) { + if (vtd_flpte_nonzero_rsvd(flpte, *flpte_level)) { error_report_once("%s: detected flpte reserved non-zero " "iova=0x%" PRIx64 ", level=0x%" PRIx32 "flpte=0x%" PRIx64 ", pasid=0x%" PRIX32 ")", - __func__, iova, level, flpte, pasid); + __func__, iova, *flpte_level, flpte, pasid); return -VTD_FR_FS_PAGING_ENTRY_RSVD; } - if (vtd_is_last_pte(flpte, level) && is_write) { + if (vtd_is_last_pte(flpte, *flpte_level) && is_write) { flag_ad |= VTD_FL_D; } @@ -2044,14 +2044,13 @@ static int vtd_iova_to_flpte(IntelIOMMUState *s, VTDContextEntry *ce, return -VTD_FR_FS_BIT_UPDATE_FAILED; } - if (vtd_is_last_pte(flpte, level)) { + if (vtd_is_last_pte(flpte, *flpte_level)) { *flptep = flpte; - *flpte_level = level; return 0; } addr = vtd_get_pte_addr(flpte, aw_bits); - level--; + (*flpte_level)--; } } @@ -2092,7 +2091,8 @@ static bool vtd_do_iommu_translate(VTDAddressSpace *vtd_as, PCIBus *bus, uint8_t bus_num = pci_bus_num(bus); VTDContextCacheEntry *cc_entry; uint64_t pte, page_mask; - uint32_t level, pasid = vtd_as->pasid; + uint32_t level = UINT32_MAX; + uint32_t pasid = vtd_as->pasid; uint16_t source_id = PCI_BUILD_BDF(bus_num, devfn); int ret_fr; bool is_fpd_set = false; @@ -2251,14 +2251,19 @@ out: entry->iova = addr & page_mask; entry->translated_addr = vtd_get_pte_addr(pte, s->aw_bits) & page_mask; entry->addr_mask = ~page_mask; - entry->perm = access_flags; + entry->perm = (is_write ? access_flags : (access_flags & (~IOMMU_WO))); return true; error: vtd_iommu_unlock(s); entry->iova = 0; entry->translated_addr = 0; - entry->addr_mask = 0; + /* + * Set the mask for ATS (the range must be present even when the + * translation fails : PCIe rev 5 10.2.3.5) + */ + entry->addr_mask = (level != UINT32_MAX) ? + (~vtd_pt_level_page_mask(level)) : (~VTD_PAGE_MASK_4K); entry->perm = IOMMU_NONE; return false; } @@ -2503,6 +2508,7 @@ static void vtd_iotlb_page_invalidate_notify(IntelIOMMUState *s, .translated_addr = 0, .addr_mask = size - 1, .perm = IOMMU_NONE, + .pasid = vtd_as->pasid, }, }; memory_region_notify_iommu(&vtd_as->iommu, 0, event); @@ -3090,6 +3096,7 @@ static void do_invalidate_device_tlb(VTDAddressSpace *vtd_dev_as, event.entry.iova = addr; event.entry.perm = IOMMU_NONE; event.entry.translated_addr = 0; + event.entry.pasid = vtd_dev_as->pasid; memory_region_notify_iommu(&vtd_dev_as->iommu, 0, event); } @@ -3672,6 +3679,7 @@ static IOMMUTLBEntry vtd_iommu_translate(IOMMUMemoryRegion *iommu, hwaddr addr, IOMMUTLBEntry iotlb = { /* We'll fill in the rest later. */ .target_as = &address_space_memory, + .pasid = vtd_as->pasid, }; bool success; @@ -4587,7 +4595,7 @@ static void vtd_cap_init(IntelIOMMUState *s) } if (s->pasid) { - s->ecap |= VTD_ECAP_PASID; + s->ecap |= VTD_ECAP_PASID | VTD_ECAP_PSS; } } @@ -4730,10 +4738,118 @@ static AddressSpace *vtd_host_dma_iommu(PCIBus *bus, void *opaque, int devfn) return &vtd_as->as; } +static IOMMUTLBEntry vtd_iommu_ats_do_translate(IOMMUMemoryRegion *iommu, + hwaddr addr, + IOMMUAccessFlags flags) +{ + IOMMUTLBEntry entry; + VTDAddressSpace *vtd_as = container_of(iommu, VTDAddressSpace, iommu); + + if (vtd_is_interrupt_addr(addr)) { + vtd_report_ir_illegal_access(vtd_as, addr, flags & IOMMU_WO); + entry.target_as = &address_space_memory; + entry.iova = 0; + entry.translated_addr = 0; + entry.addr_mask = ~VTD_PAGE_MASK_4K; + entry.perm = IOMMU_NONE; + entry.pasid = PCI_NO_PASID; + } else { + entry = vtd_iommu_translate(iommu, addr, flags, 0); + } + + return entry; +} + +static ssize_t vtd_ats_request_translation(PCIBus *bus, void *opaque, + int devfn, uint32_t pasid, + bool priv_req, bool exec_req, + hwaddr addr, size_t length, + bool no_write, IOMMUTLBEntry *result, + size_t result_length, + uint32_t *err_count) +{ + IntelIOMMUState *s = opaque; + VTDAddressSpace *vtd_as; + IOMMUAccessFlags flags = IOMMU_ACCESS_FLAG_FULL(true, !no_write, exec_req, + priv_req, false, false); + ssize_t res_index = 0; + hwaddr target_address = addr + length; + IOMMUTLBEntry entry; + + vtd_as = vtd_find_add_as(s, bus, devfn, pasid); + *err_count = 0; + + while ((addr < target_address) && (res_index < result_length)) { + entry = vtd_iommu_ats_do_translate(&vtd_as->iommu, addr, flags); + entry.perm &= ~IOMMU_GLOBAL; /* Spec 4.1.2: Global Mapping never set */ + + if ((entry.perm & flags) != flags) { + *err_count += 1; /* Less than expected */ + } + + result[res_index] = entry; + res_index += 1; + addr = (addr & (~entry.addr_mask)) + (entry.addr_mask + 1); + } + + /* Buffer too small */ + if (addr < target_address) { + return -ENOMEM; + } + + return res_index; +} + +static void vtd_init_iotlb_notifier(PCIBus *bus, void *opaque, int devfn, + IOMMUNotifier *n, IOMMUNotify fn, + void *user_opaque) +{ + n->opaque = user_opaque; + iommu_notifier_init(n, fn, IOMMU_NOTIFIER_DEVIOTLB_EVENTS, 0, + HWADDR_MAX, 0); +} + +static void vtd_get_iotlb_info(void *opaque, uint8_t *addr_width, + uint32_t *min_page_size) +{ + IntelIOMMUState *s = opaque; + + *addr_width = s->aw_bits; + *min_page_size = VTD_PAGE_SIZE; +} + +static void vtd_register_iotlb_notifier(PCIBus *bus, void *opaque, + int devfn, uint32_t pasid, + IOMMUNotifier *n) +{ + IntelIOMMUState *s = opaque; + VTDAddressSpace *vtd_as; + + vtd_as = vtd_find_add_as(s, bus, devfn, pasid); + memory_region_register_iommu_notifier(MEMORY_REGION(&vtd_as->iommu), n, + &error_fatal); +} + +static void vtd_unregister_iotlb_notifier(PCIBus *bus, void *opaque, + int devfn, uint32_t pasid, + IOMMUNotifier *n) +{ + IntelIOMMUState *s = opaque; + VTDAddressSpace *vtd_as; + + vtd_as = vtd_find_add_as(s, bus, devfn, pasid); + memory_region_unregister_iommu_notifier(MEMORY_REGION(&vtd_as->iommu), n); +} + static PCIIOMMUOps vtd_iommu_ops = { .get_address_space = vtd_host_dma_iommu, .set_iommu_device = vtd_dev_set_iommu_device, .unset_iommu_device = vtd_dev_unset_iommu_device, + .get_iotlb_info = vtd_get_iotlb_info, + .init_iotlb_notifier = vtd_init_iotlb_notifier, + .register_iotlb_notifier = vtd_register_iotlb_notifier, + .unregister_iotlb_notifier = vtd_unregister_iotlb_notifier, + .ats_request_translation = vtd_ats_request_translation, }; static bool vtd_decide_config(IntelIOMMUState *s, Error **errp) diff --git a/hw/i386/intel_iommu_internal.h b/hw/i386/intel_iommu_internal.h index e8b211e..360e937 100644 --- a/hw/i386/intel_iommu_internal.h +++ b/hw/i386/intel_iommu_internal.h @@ -192,6 +192,7 @@ #define VTD_ECAP_SC (1ULL << 7) #define VTD_ECAP_MHMV (15ULL << 20) #define VTD_ECAP_SRS (1ULL << 31) +#define VTD_ECAP_PSS (7ULL << 35) /* limit: MemTxAttrs::pid */ #define VTD_ECAP_PASID (1ULL << 40) #define VTD_ECAP_SMTS (1ULL << 43) #define VTD_ECAP_SLTS (1ULL << 46) diff --git a/hw/i386/pc.c b/hw/i386/pc.c index 860346d..2f58e73 100644 --- a/hw/i386/pc.c +++ b/hw/i386/pc.c @@ -81,7 +81,10 @@ { "qemu64-" TYPE_X86_CPU, "model-id", "QEMU Virtual CPU version " v, },\ { "athlon-" TYPE_X86_CPU, "model-id", "QEMU Virtual CPU version " v, }, -GlobalProperty pc_compat_10_0[] = {}; +GlobalProperty pc_compat_10_0[] = { + { TYPE_X86_CPU, "x-consistent-cache", "false" }, + { TYPE_X86_CPU, "x-vendor-cpuid-only-v2", "false" }, +}; const size_t pc_compat_10_0_len = G_N_ELEMENTS(pc_compat_10_0); GlobalProperty pc_compat_9_2[] = {}; @@ -1827,6 +1830,18 @@ static void pc_machine_class_init(ObjectClass *oc, const void *data) object_class_property_add_bool(oc, "fd-bootchk", pc_machine_get_fd_bootchk, pc_machine_set_fd_bootchk); + +#if defined(CONFIG_IGVM) + object_class_property_add_link(oc, "igvm-cfg", + TYPE_IGVM_CFG, + offsetof(X86MachineState, igvm), + object_property_allow_set_link, + OBJ_PROP_LINK_STRONG); + object_class_property_set_description(oc, "igvm-cfg", + "Set IGVM configuration"); +#endif + + } static const TypeInfo pc_machine_info = { diff --git a/hw/i386/pc_piix.c b/hw/i386/pc_piix.c index ea7572e..a3285fb 100644 --- a/hw/i386/pc_piix.c +++ b/hw/i386/pc_piix.c @@ -366,6 +366,16 @@ static void pc_init1(MachineState *machine, const char *pci_type) x86_nvdimm_acpi_dsmio, x86ms->fw_cfg, OBJECT(pcms)); } + +#if defined(CONFIG_IGVM) + /* Apply guest state from IGVM if supplied */ + if (x86ms->igvm) { + if (IGVM_CFG_GET_CLASS(x86ms->igvm) + ->process(x86ms->igvm, machine->cgs, false, &error_fatal) < 0) { + g_assert_not_reached(); + } + } +#endif } typedef enum PCSouthBridgeOption { diff --git a/hw/i386/pc_q35.c b/hw/i386/pc_q35.c index 33211b1..cf871cf 100644 --- a/hw/i386/pc_q35.c +++ b/hw/i386/pc_q35.c @@ -325,6 +325,16 @@ static void pc_q35_init(MachineState *machine) x86_nvdimm_acpi_dsmio, x86ms->fw_cfg, OBJECT(pcms)); } + +#if defined(CONFIG_IGVM) + /* Apply guest state from IGVM if supplied */ + if (x86ms->igvm) { + if (IGVM_CFG_GET_CLASS(x86ms->igvm) + ->process(x86ms->igvm, machine->cgs, false, &error_fatal) < 0) { + g_assert_not_reached(); + } + } +#endif } #define DEFINE_Q35_MACHINE(major, minor) \ diff --git a/hw/i386/pc_sysfw.c b/hw/i386/pc_sysfw.c index 821396c..1a12b63 100644 --- a/hw/i386/pc_sysfw.c +++ b/hw/i386/pc_sysfw.c @@ -220,7 +220,13 @@ void pc_system_firmware_init(PCMachineState *pcms, BlockBackend *pflash_blk[ARRAY_SIZE(pcms->flash)]; if (!pcmc->pci_enabled) { - x86_bios_rom_init(X86_MACHINE(pcms), "bios.bin", rom_memory, true); + /* + * If an IGVM file is specified then the firmware must be provided + * in the IGVM file. + */ + if (!X86_MACHINE(pcms)->igvm) { + x86_bios_rom_init(X86_MACHINE(pcms), "bios.bin", rom_memory, true); + } return; } @@ -240,8 +246,13 @@ void pc_system_firmware_init(PCMachineState *pcms, } if (!pflash_blk[0]) { - /* Machine property pflash0 not set, use ROM mode */ - x86_bios_rom_init(X86_MACHINE(pcms), "bios.bin", rom_memory, false); + /* + * Machine property pflash0 not set, use ROM mode unless using IGVM, + * in which case the firmware must be provided by the IGVM file. + */ + if (!X86_MACHINE(pcms)->igvm) { + x86_bios_rom_init(X86_MACHINE(pcms), "bios.bin", rom_memory, false); + } } else { if (kvm_enabled() && !kvm_readonly_mem_enabled()) { /* @@ -257,6 +268,20 @@ void pc_system_firmware_init(PCMachineState *pcms, } pc_system_flash_cleanup_unused(pcms); + + /* + * The user should not have specified any pflash devices when using IGVM + * to configure the guest. + */ + if (X86_MACHINE(pcms)->igvm) { + for (i = 0; i < ARRAY_SIZE(pcms->flash); i++) { + if (pcms->flash[i]) { + error_report("pflash devices cannot be configured when " + "using IGVM"); + exit(1); + } + } + } } void x86_firmware_configure(hwaddr gpa, void *ptr, int size) diff --git a/hw/intc/loongarch_extioi_kvm.c b/hw/intc/loongarch_extioi_kvm.c index 0133540..aa2e8c7 100644 --- a/hw/intc/loongarch_extioi_kvm.c +++ b/hw/intc/loongarch_extioi_kvm.c @@ -6,7 +6,6 @@ */ #include "qemu/osdep.h" -#include "qemu/typedefs.h" #include "hw/intc/loongarch_extioi.h" #include "linux/kvm.h" #include "qapi/error.h" diff --git a/hw/loongarch/virt-acpi-build.c b/hw/loongarch/virt-acpi-build.c index 2cd2d9d..8c2228a 100644 --- a/hw/loongarch/virt-acpi-build.c +++ b/hw/loongarch/virt-acpi-build.c @@ -557,7 +557,9 @@ static void acpi_build(AcpiBuildTables *tables, MachineState *machine) acpi_add_table(table_offsets, tables_blob); build_srat(tables_blob, tables->linker, machine); acpi_add_table(table_offsets, tables_blob); - spcr_setup(tables_blob, tables->linker, machine); + + if (machine->acpi_spcr_enabled) + spcr_setup(tables_blob, tables->linker, machine); if (machine->numa_state->num_nodes) { if (machine->numa_state->have_numa_distance) { diff --git a/hw/mem/cxl_type3.c b/hw/mem/cxl_type3.c index 94e7274..be609ff 100644 --- a/hw/mem/cxl_type3.c +++ b/hw/mem/cxl_type3.c @@ -8,6 +8,7 @@ * * SPDX-License-Identifier: GPL-v2-only */ +#include <math.h> #include "qemu/osdep.h" #include "qemu/units.h" @@ -225,10 +226,16 @@ static int ct3_build_cdat_table(CDATSubHeader ***cdat_table, void *priv) * future. */ for (i = 0; i < ct3d->dc.num_regions; i++) { + ct3d->dc.regions[i].nonvolatile = false; + ct3d->dc.regions[i].sharable = false; + ct3d->dc.regions[i].hw_managed_coherency = false; + ct3d->dc.regions[i].ic_specific_dc_management = false; + ct3d->dc.regions[i].rdonly = false; ct3_build_cdat_entries_for_mr(&(table[cur_ent]), dsmad_handle++, ct3d->dc.regions[i].len, - false, true, region_base); + ct3d->dc.regions[i].nonvolatile, + true, region_base); ct3d->dc.regions[i].dsmadhandle = dsmad_handle - 1; cur_ent += CT3_CDAT_NUM_ENTRIES; @@ -634,6 +641,8 @@ static bool cxl_create_dc_regions(CXLType3Dev *ct3d, Error **errp) uint64_t region_len; uint64_t decode_len; uint64_t blk_size = 2 * MiB; + /* Only 1 block size is supported for now. */ + uint64_t supported_blk_size_bitmask = blk_size; CXLDCRegion *region; MemoryRegion *mr; uint64_t dc_size; @@ -679,9 +688,11 @@ static bool cxl_create_dc_regions(CXLType3Dev *ct3d, Error **errp) .block_size = blk_size, /* dsmad_handle set when creating CDAT table entries */ .flags = 0, + .supported_blk_size_bitmask = supported_blk_size_bitmask, }; ct3d->dc.total_capacity += region->len; region->blk_bitmap = bitmap_new(region->len / region->block_size); + qemu_mutex_init(®ion->bitmap_lock); } QTAILQ_INIT(&ct3d->dc.extents); QTAILQ_INIT(&ct3d->dc.extents_pending); @@ -1010,6 +1021,7 @@ void ct3_set_region_block_backed(CXLType3Dev *ct3d, uint64_t dpa, return; } + QEMU_LOCK_GUARD(®ion->bitmap_lock); bitmap_set(region->blk_bitmap, (dpa - region->base) / region->block_size, len / region->block_size); } @@ -1036,6 +1048,7 @@ bool ct3_test_region_block_backed(CXLType3Dev *ct3d, uint64_t dpa, * if bits between [dpa, dpa + len) are all 1s, meaning the DPA range is * backed with DC extents, return true; else return false. */ + QEMU_LOCK_GUARD(®ion->bitmap_lock); return find_next_zero_bit(region->blk_bitmap, nr + nbits, nr) == nr + nbits; } @@ -1057,6 +1070,7 @@ void ct3_clear_region_block_backed(CXLType3Dev *ct3d, uint64_t dpa, nr = (dpa - region->base) / region->block_size; nbits = len / region->block_size; + QEMU_LOCK_GUARD(®ion->bitmap_lock); bitmap_clear(region->blk_bitmap, nr, nbits); } @@ -1576,9 +1590,9 @@ void qmp_cxl_inject_correctable_error(const char *path, CxlCorErrorType type, pcie_aer_inject_error(PCI_DEVICE(obj), &err); } -static void cxl_assign_event_header(CXLEventRecordHdr *hdr, - const QemuUUID *uuid, uint32_t flags, - uint8_t length, uint64_t timestamp) +void cxl_assign_event_header(CXLEventRecordHdr *hdr, + const QemuUUID *uuid, uint32_t flags, + uint8_t length, uint64_t timestamp) { st24_le_p(&hdr->flags, flags); hdr->length = length; @@ -1866,28 +1880,13 @@ void qmp_cxl_inject_memory_module_event(const char *path, CxlEventLog log, } } -/* CXL r3.1 Table 8-50: Dynamic Capacity Event Record */ -static const QemuUUID dynamic_capacity_uuid = { - .data = UUID(0xca95afa7, 0xf183, 0x4018, 0x8c, 0x2f, - 0x95, 0x26, 0x8e, 0x10, 0x1a, 0x2a), -}; - -typedef enum CXLDCEventType { - DC_EVENT_ADD_CAPACITY = 0x0, - DC_EVENT_RELEASE_CAPACITY = 0x1, - DC_EVENT_FORCED_RELEASE_CAPACITY = 0x2, - DC_EVENT_REGION_CONFIG_UPDATED = 0x3, - DC_EVENT_ADD_CAPACITY_RSP = 0x4, - DC_EVENT_CAPACITY_RELEASED = 0x5, -} CXLDCEventType; - /* * Check whether the range [dpa, dpa + len - 1] has overlaps with extents in * the list. * Return value: return true if has overlaps; otherwise, return false */ -static bool cxl_extents_overlaps_dpa_range(CXLDCExtentList *list, - uint64_t dpa, uint64_t len) +bool cxl_extents_overlaps_dpa_range(CXLDCExtentList *list, + uint64_t dpa, uint64_t len) { CXLDCExtent *ent; Range range1, range2; @@ -1932,8 +1931,8 @@ bool cxl_extents_contains_dpa_range(CXLDCExtentList *list, return false; } -static bool cxl_extent_groups_overlaps_dpa_range(CXLDCExtentGroupList *list, - uint64_t dpa, uint64_t len) +bool cxl_extent_groups_overlaps_dpa_range(CXLDCExtentGroupList *list, + uint64_t dpa, uint64_t len) { CXLDCExtentGroup *group; @@ -1958,15 +1957,11 @@ static void qmp_cxl_process_dynamic_capacity_prescriptive(const char *path, CxlDynamicCapacityExtentList *records, Error **errp) { Object *obj; - CXLEventDynamicCapacity dCap = {}; - CXLEventRecordHdr *hdr = &dCap.hdr; CXLType3Dev *dcd; - uint8_t flags = 1 << CXL_EVENT_TYPE_INFO; uint32_t num_extents = 0; CxlDynamicCapacityExtentList *list; CXLDCExtentGroup *group = NULL; g_autofree CXLDCExtentRaw *extents = NULL; - uint8_t enc_log = CXL_EVENT_TYPE_DYNAMIC_CAP; uint64_t dpa, offset, len, block_size; g_autofree unsigned long *blk_bitmap = NULL; int i; @@ -2076,40 +2071,10 @@ static void qmp_cxl_process_dynamic_capacity_prescriptive(const char *path, } if (group) { cxl_extent_group_list_insert_tail(&dcd->dc.extents_pending, group); + dcd->dc.total_extent_count += num_extents; } - /* - * CXL r3.1 section 8.2.9.2.1.6: Dynamic Capacity Event Record - * - * All Dynamic Capacity event records shall set the Event Record Severity - * field in the Common Event Record Format to Informational Event. All - * Dynamic Capacity related events shall be logged in the Dynamic Capacity - * Event Log. - */ - cxl_assign_event_header(hdr, &dynamic_capacity_uuid, flags, sizeof(dCap), - cxl_device_get_timestamp(&dcd->cxl_dstate)); - - dCap.type = type; - /* FIXME: for now, validity flag is cleared */ - dCap.validity_flags = 0; - stw_le_p(&dCap.host_id, hid); - /* only valid for DC_REGION_CONFIG_UPDATED event */ - dCap.updated_region_id = 0; - for (i = 0; i < num_extents; i++) { - memcpy(&dCap.dynamic_capacity_extent, &extents[i], - sizeof(CXLDCExtentRaw)); - - dCap.flags = 0; - if (i < num_extents - 1) { - /* Set "More" flag */ - dCap.flags |= BIT(0); - } - - if (cxl_event_insert(&dcd->cxl_dstate, enc_log, - (CXLEventRecordRaw *)&dCap)) { - cxl_event_irq_assert(dcd); - } - } + cxl_create_dc_event_records_for_extents(dcd, type, extents, num_extents); } void qmp_cxl_add_dynamic_capacity(const char *path, uint16_t host_id, diff --git a/hw/microblaze/Kconfig b/hw/microblaze/Kconfig index b0214b2..72d8072 100644 --- a/hw/microblaze/Kconfig +++ b/hw/microblaze/Kconfig @@ -1,7 +1,7 @@ config PETALOGIX_S3ADSP1800 bool default y - depends on MICROBLAZE + depends on MICROBLAZE && FDT select PFLASH_CFI01 select XILINX select XILINX_AXI @@ -11,7 +11,7 @@ config PETALOGIX_S3ADSP1800 config PETALOGIX_ML605 bool default y - depends on MICROBLAZE + depends on MICROBLAZE && FDT select PFLASH_CFI01 select SERIAL_MM select SSI_M25P80 diff --git a/hw/mips/Kconfig b/hw/mips/Kconfig index b09c89a..f84fffc 100644 --- a/hw/mips/Kconfig +++ b/hw/mips/Kconfig @@ -76,7 +76,7 @@ config LOONGSON3V config MIPS_CPS bool - select MIPS_ITU + select MIPS_ITU if TCG config MIPS_BOSTON bool diff --git a/hw/mips/cps.c b/hw/mips/cps.c index 2a3ba3f..e47695e 100644 --- a/hw/mips/cps.c +++ b/hw/mips/cps.c @@ -24,7 +24,7 @@ #include "hw/mips/mips.h" #include "hw/qdev-clock.h" #include "hw/qdev-properties.h" -#include "system/kvm.h" +#include "system/tcg.h" #include "system/reset.h" qemu_irq get_cps_irq(MIPSCPSState *s, int pin_number) @@ -59,7 +59,7 @@ static bool cpu_mips_itu_supported(CPUMIPSState *env) { bool is_mt = (env->CP0_Config5 & (1 << CP0C5_VP)) || ase_mt_available(env); - return is_mt && !kvm_enabled(); + return is_mt && tcg_enabled(); } static void mips_cps_realize(DeviceState *dev, Error **errp) diff --git a/hw/misc/Kconfig b/hw/misc/Kconfig index c27285b..4e35657 100644 --- a/hw/misc/Kconfig +++ b/hw/misc/Kconfig @@ -119,6 +119,7 @@ config STM32L4X5_RCC config MIPS_ITU bool + depends on TCG config MPS2_FPGAIO bool diff --git a/hw/net/can/ctucan_core.c b/hw/net/can/ctucan_core.c index 17131a4..6bd99c4 100644 --- a/hw/net/can/ctucan_core.c +++ b/hw/net/can/ctucan_core.c @@ -28,7 +28,6 @@ #include "qemu/osdep.h" #include "qemu/log.h" -#include "qemu/bswap.h" #include "qemu/bitops.h" #include "hw/irq.h" #include "migration/vmstate.h" diff --git a/hw/net/lan9118.c b/hw/net/lan9118.c index 6dda1e5..3017e12 100644 --- a/hw/net/lan9118.c +++ b/hw/net/lan9118.c @@ -21,6 +21,7 @@ #include "hw/ptimer.h" #include "hw/qdev-properties.h" #include "qapi/error.h" +#include "qemu/bswap.h" #include "qemu/log.h" #include "qemu/module.h" #include <zlib.h> /* for crc32 */ diff --git a/hw/net/rtl8139.c b/hw/net/rtl8139.c index 654a087..324fb93 100644 --- a/hw/net/rtl8139.c +++ b/hw/net/rtl8139.c @@ -57,6 +57,7 @@ #include "system/dma.h" #include "qemu/module.h" #include "qemu/timer.h" +#include "qemu/bswap.h" #include "net/net.h" #include "net/eth.h" #include "system/system.h" diff --git a/hw/net/vhost_net-stub.c b/hw/net/vhost_net-stub.c index 72df6d7..7d49f82 100644 --- a/hw/net/vhost_net-stub.c +++ b/hw/net/vhost_net-stub.c @@ -13,7 +13,6 @@ #include "qemu/osdep.h" #include "net/net.h" #include "net/tap.h" -#include "net/vhost-user.h" #include "hw/virtio/virtio-net.h" #include "net/vhost_net.h" @@ -101,7 +100,7 @@ VHostNetState *get_vhost_net(NetClientState *nc) return 0; } -int vhost_set_vring_enable(NetClientState *nc, int enable) +int vhost_net_set_vring_enable(NetClientState *nc, int enable) { return 0; } diff --git a/hw/net/vhost_net.c b/hw/net/vhost_net.c index 891f235..540492b 100644 --- a/hw/net/vhost_net.c +++ b/hw/net/vhost_net.c @@ -16,7 +16,6 @@ #include "qemu/osdep.h" #include "net/net.h" #include "net/tap.h" -#include "net/vhost-user.h" #include "net/vhost-vdpa.h" #include "standard-headers/linux/vhost_types.h" @@ -36,94 +35,9 @@ #include "hw/virtio/virtio-bus.h" #include "linux-headers/linux/vhost.h" - -/* Features supported by host kernel. */ -static const int kernel_feature_bits[] = { - VIRTIO_F_NOTIFY_ON_EMPTY, - VIRTIO_RING_F_INDIRECT_DESC, - VIRTIO_RING_F_EVENT_IDX, - VIRTIO_NET_F_MRG_RXBUF, - VIRTIO_F_VERSION_1, - VIRTIO_NET_F_MTU, - VIRTIO_F_IOMMU_PLATFORM, - VIRTIO_F_RING_PACKED, - VIRTIO_F_RING_RESET, - VIRTIO_F_IN_ORDER, - VIRTIO_F_NOTIFICATION_DATA, - VIRTIO_NET_F_RSC_EXT, - VIRTIO_NET_F_HASH_REPORT, - VHOST_INVALID_FEATURE_BIT -}; - -/* Features supported by others. */ -static const int user_feature_bits[] = { - VIRTIO_F_NOTIFY_ON_EMPTY, - VIRTIO_F_NOTIFICATION_DATA, - VIRTIO_RING_F_INDIRECT_DESC, - VIRTIO_RING_F_EVENT_IDX, - - VIRTIO_F_ANY_LAYOUT, - VIRTIO_F_VERSION_1, - VIRTIO_NET_F_CSUM, - VIRTIO_NET_F_GUEST_CSUM, - VIRTIO_NET_F_GSO, - VIRTIO_NET_F_GUEST_TSO4, - VIRTIO_NET_F_GUEST_TSO6, - VIRTIO_NET_F_GUEST_ECN, - VIRTIO_NET_F_GUEST_UFO, - VIRTIO_NET_F_HOST_TSO4, - VIRTIO_NET_F_HOST_TSO6, - VIRTIO_NET_F_HOST_ECN, - VIRTIO_NET_F_HOST_UFO, - VIRTIO_NET_F_MRG_RXBUF, - VIRTIO_NET_F_MTU, - VIRTIO_F_IOMMU_PLATFORM, - VIRTIO_F_RING_PACKED, - VIRTIO_F_RING_RESET, - VIRTIO_F_IN_ORDER, - VIRTIO_NET_F_RSS, - VIRTIO_NET_F_RSC_EXT, - VIRTIO_NET_F_HASH_REPORT, - VIRTIO_NET_F_GUEST_USO4, - VIRTIO_NET_F_GUEST_USO6, - VIRTIO_NET_F_HOST_USO, - - /* This bit implies RARP isn't sent by QEMU out of band */ - VIRTIO_NET_F_GUEST_ANNOUNCE, - - VIRTIO_NET_F_MQ, - - VHOST_INVALID_FEATURE_BIT -}; - -static const int *vhost_net_get_feature_bits(struct vhost_net *net) -{ - const int *feature_bits = 0; - - switch (net->nc->info->type) { - case NET_CLIENT_DRIVER_TAP: - feature_bits = kernel_feature_bits; - break; - case NET_CLIENT_DRIVER_VHOST_USER: - feature_bits = user_feature_bits; - break; -#ifdef CONFIG_VHOST_NET_VDPA - case NET_CLIENT_DRIVER_VHOST_VDPA: - feature_bits = vdpa_feature_bits; - break; -#endif - default: - error_report("Feature bits not defined for this type: %d", - net->nc->info->type); - break; - } - - return feature_bits; -} - uint64_t vhost_net_get_features(struct vhost_net *net, uint64_t features) { - return vhost_get_features(&net->dev, vhost_net_get_feature_bits(net), + return vhost_get_features(&net->dev, net->feature_bits, features); } int vhost_net_get_config(struct vhost_net *net, uint8_t *config, @@ -140,7 +54,7 @@ int vhost_net_set_config(struct vhost_net *net, const uint8_t *data, void vhost_net_ack_features(struct vhost_net *net, uint64_t features) { net->dev.acked_features = net->dev.backend_features; - vhost_ack_features(&net->dev, vhost_net_get_feature_bits(net), features); + vhost_ack_features(&net->dev, net->feature_bits, features); } uint64_t vhost_net_get_max_queues(VHostNetState *net) @@ -155,11 +69,11 @@ uint64_t vhost_net_get_acked_features(VHostNetState *net) void vhost_net_save_acked_features(NetClientState *nc) { -#ifdef CONFIG_VHOST_NET_USER - if (nc->info->type == NET_CLIENT_DRIVER_VHOST_USER) { - vhost_user_save_acked_features(nc); + struct vhost_net *net = get_vhost_net(nc); + + if (net && net->save_acked_features) { + net->save_acked_features(nc); } -#endif } static void vhost_net_disable_notifiers_nvhosts(VirtIODevice *dev, @@ -329,6 +243,10 @@ struct vhost_net *vhost_net_init(VhostNetOptions *options) } net->nc = options->net_backend; net->dev.nvqs = options->nvqs; + net->feature_bits = options->feature_bits; + net->save_acked_features = options->save_acked_features; + net->max_tx_queue_size = options->max_tx_queue_size; + net->is_vhost_user = options->is_vhost_user; net->dev.max_queues = 1; net->dev.vqs = net->vqs; @@ -372,9 +290,8 @@ struct vhost_net *vhost_net_init(VhostNetOptions *options) } /* Set sane init value. Override when guest acks. */ -#ifdef CONFIG_VHOST_NET_USER - if (net->nc->info->type == NET_CLIENT_DRIVER_VHOST_USER) { - features = vhost_user_get_acked_features(net->nc); + if (options->get_acked_features) { + features = options->get_acked_features(net->nc); if (~net->dev.features & features) { fprintf(stderr, "vhost lacks feature mask 0x%" PRIx64 " for backend\n", @@ -382,7 +299,6 @@ struct vhost_net *vhost_net_init(VhostNetOptions *options) goto fail; } } -#endif vhost_net_ack_features(net, features); @@ -525,7 +441,7 @@ int vhost_net_start(VirtIODevice *dev, NetClientState *ncs, * because vhost user doesn't interrupt masking/unmasking * properly. */ - if (net->nc->info->type == NET_CLIENT_DRIVER_VHOST_USER) { + if (net->is_vhost_user) { dev->use_guest_notifier_mask = false; } } @@ -551,7 +467,7 @@ int vhost_net_start(VirtIODevice *dev, NetClientState *ncs, if (peer->vring_enable) { /* restore vring enable state */ - r = vhost_set_vring_enable(peer, peer->vring_enable); + r = vhost_net_set_vring_enable(peer, peer->vring_enable); if (r < 0) { goto err_guest_notifiers; @@ -649,44 +565,21 @@ void vhost_net_config_mask(VHostNetState *net, VirtIODevice *dev, bool mask) { vhost_config_mask(&net->dev, dev, mask); } + VHostNetState *get_vhost_net(NetClientState *nc) { - VHostNetState *vhost_net = 0; - if (!nc) { return 0; } - switch (nc->info->type) { - case NET_CLIENT_DRIVER_TAP: - vhost_net = tap_get_vhost_net(nc); - /* - * tap_get_vhost_net() can return NULL if a tap net-device backend is - * created with 'vhost=off' option, 'vhostforce=off' or no vhost or - * vhostforce or vhostfd options at all. Please see net_init_tap_one(). - * Hence, we omit the assertion here. - */ - break; -#ifdef CONFIG_VHOST_NET_USER - case NET_CLIENT_DRIVER_VHOST_USER: - vhost_net = vhost_user_get_vhost_net(nc); - assert(vhost_net); - break; -#endif -#ifdef CONFIG_VHOST_NET_VDPA - case NET_CLIENT_DRIVER_VHOST_VDPA: - vhost_net = vhost_vdpa_get_vhost_net(nc); - assert(vhost_net); - break; -#endif - default: - break; + if (nc->info->get_vhost_net) { + return nc->info->get_vhost_net(nc); } - return vhost_net; + return NULL; } -int vhost_set_vring_enable(NetClientState *nc, int enable) +int vhost_net_set_vring_enable(NetClientState *nc, int enable) { VHostNetState *net = get_vhost_net(nc); const VhostOps *vhost_ops = net->dev.vhost_ops; diff --git a/hw/net/virtio-net.c b/hw/net/virtio-net.c index eb93607..c4c49b0 100644 --- a/hw/net/virtio-net.c +++ b/hw/net/virtio-net.c @@ -158,7 +158,7 @@ static void virtio_net_get_config(VirtIODevice *vdev, uint8_t *config) virtio_host_has_feature(vdev, VIRTIO_NET_F_RSS) ? VIRTIO_NET_RSS_MAX_TABLE_LEN : 1); virtio_stl_p(vdev, &netcfg.supported_hash_types, - VIRTIO_NET_RSS_SUPPORTED_HASHES); + n->rss_data.supported_hash_types); memcpy(config, &netcfg, n->config_size); /* @@ -670,34 +670,36 @@ static void virtio_net_set_mrg_rx_bufs(VirtIONet *n, int mergeable_rx_bufs, static int virtio_net_max_tx_queue_size(VirtIONet *n) { NetClientState *peer = n->nic_conf.peers.ncs[0]; + struct vhost_net *net; - /* - * Backends other than vhost-user or vhost-vdpa don't support max queue - * size. - */ if (!peer) { - return VIRTIO_NET_TX_QUEUE_DEFAULT_SIZE; + goto default_value; } - switch(peer->info->type) { - case NET_CLIENT_DRIVER_VHOST_USER: - case NET_CLIENT_DRIVER_VHOST_VDPA: - return VIRTQUEUE_MAX_SIZE; - default: - return VIRTIO_NET_TX_QUEUE_DEFAULT_SIZE; - }; + net = get_vhost_net(peer); + + if (!net || !net->max_tx_queue_size) { + goto default_value; + } + + return net->max_tx_queue_size; + +default_value: + return VIRTIO_NET_TX_QUEUE_DEFAULT_SIZE; } static int peer_attach(VirtIONet *n, int index) { NetClientState *nc = qemu_get_subqueue(n->nic, index); + struct vhost_net *net; if (!nc->peer) { return 0; } - if (nc->peer->info->type == NET_CLIENT_DRIVER_VHOST_USER) { - vhost_set_vring_enable(nc->peer, 1); + net = get_vhost_net(nc->peer); + if (net && net->is_vhost_user) { + vhost_net_set_vring_enable(nc->peer, 1); } if (nc->peer->info->type != NET_CLIENT_DRIVER_TAP) { @@ -714,13 +716,15 @@ static int peer_attach(VirtIONet *n, int index) static int peer_detach(VirtIONet *n, int index) { NetClientState *nc = qemu_get_subqueue(n->nic, index); + struct vhost_net *net; if (!nc->peer) { return 0; } - if (nc->peer->info->type == NET_CLIENT_DRIVER_VHOST_USER) { - vhost_set_vring_enable(nc->peer, 0); + net = get_vhost_net(nc->peer); + if (net && net->is_vhost_user) { + vhost_net_set_vring_enable(nc->peer, 0); } if (nc->peer->info->type != NET_CLIENT_DRIVER_TAP) { @@ -752,79 +756,6 @@ static void virtio_net_set_queue_pairs(VirtIONet *n) static void virtio_net_set_multiqueue(VirtIONet *n, int multiqueue); -static uint64_t virtio_net_get_features(VirtIODevice *vdev, uint64_t features, - Error **errp) -{ - VirtIONet *n = VIRTIO_NET(vdev); - NetClientState *nc = qemu_get_queue(n->nic); - - /* Firstly sync all virtio-net possible supported features */ - features |= n->host_features; - - virtio_add_feature(&features, VIRTIO_NET_F_MAC); - - if (!peer_has_vnet_hdr(n)) { - virtio_clear_feature(&features, VIRTIO_NET_F_CSUM); - virtio_clear_feature(&features, VIRTIO_NET_F_HOST_TSO4); - virtio_clear_feature(&features, VIRTIO_NET_F_HOST_TSO6); - virtio_clear_feature(&features, VIRTIO_NET_F_HOST_ECN); - - virtio_clear_feature(&features, VIRTIO_NET_F_GUEST_CSUM); - virtio_clear_feature(&features, VIRTIO_NET_F_GUEST_TSO4); - virtio_clear_feature(&features, VIRTIO_NET_F_GUEST_TSO6); - virtio_clear_feature(&features, VIRTIO_NET_F_GUEST_ECN); - - virtio_clear_feature(&features, VIRTIO_NET_F_HOST_USO); - virtio_clear_feature(&features, VIRTIO_NET_F_GUEST_USO4); - virtio_clear_feature(&features, VIRTIO_NET_F_GUEST_USO6); - - virtio_clear_feature(&features, VIRTIO_NET_F_HASH_REPORT); - } - - if (!peer_has_vnet_hdr(n) || !peer_has_ufo(n)) { - virtio_clear_feature(&features, VIRTIO_NET_F_GUEST_UFO); - virtio_clear_feature(&features, VIRTIO_NET_F_HOST_UFO); - } - - if (!peer_has_uso(n)) { - virtio_clear_feature(&features, VIRTIO_NET_F_HOST_USO); - virtio_clear_feature(&features, VIRTIO_NET_F_GUEST_USO4); - virtio_clear_feature(&features, VIRTIO_NET_F_GUEST_USO6); - } - - if (!get_vhost_net(nc->peer)) { - return features; - } - - if (!ebpf_rss_is_loaded(&n->ebpf_rss)) { - virtio_clear_feature(&features, VIRTIO_NET_F_RSS); - } - features = vhost_net_get_features(get_vhost_net(nc->peer), features); - vdev->backend_features = features; - - if (n->mtu_bypass_backend && - (n->host_features & 1ULL << VIRTIO_NET_F_MTU)) { - features |= (1ULL << VIRTIO_NET_F_MTU); - } - - /* - * Since GUEST_ANNOUNCE is emulated the feature bit could be set without - * enabled. This happens in the vDPA case. - * - * Make sure the feature set is not incoherent, as the driver could refuse - * to start. - * - * TODO: QEMU is able to emulate a CVQ just for guest_announce purposes, - * helping guest to notify the new location with vDPA devices that does not - * support it. - */ - if (!virtio_has_feature(vdev->backend_features, VIRTIO_NET_F_CTRL_VQ)) { - virtio_clear_feature(&features, VIRTIO_NET_F_GUEST_ANNOUNCE); - } - - return features; -} - static uint64_t virtio_net_bad_features(VirtIODevice *vdev) { uint64_t features = 0; @@ -1251,7 +1182,7 @@ static void rss_data_to_rss_config(struct VirtioNetRssData *data, { config->redirect = data->redirect; config->populate_hash = data->populate_hash; - config->hash_types = data->hash_types; + config->hash_types = data->runtime_hash_types; config->indirections_len = data->indirections_len; config->default_queue = data->default_queue; } @@ -1286,6 +1217,10 @@ static void virtio_net_detach_ebpf_rss(VirtIONet *n) static void virtio_net_commit_rss_config(VirtIONet *n) { + if (n->rss_data.peer_hash_available) { + return; + } + if (n->rss_data.enabled) { n->rss_data.enabled_software_rss = n->rss_data.populate_hash; if (n->rss_data.populate_hash) { @@ -1300,7 +1235,7 @@ static void virtio_net_commit_rss_config(VirtIONet *n) } trace_virtio_net_rss_enable(n, - n->rss_data.hash_types, + n->rss_data.runtime_hash_types, n->rss_data.indirections_len, sizeof(n->rss_data.key)); } else { @@ -1411,7 +1346,7 @@ static uint16_t virtio_net_handle_rss(VirtIONet *n, err_value = (uint32_t)s; goto error; } - n->rss_data.hash_types = virtio_ldl_p(vdev, &cfg.hash_types); + n->rss_data.runtime_hash_types = virtio_ldl_p(vdev, &cfg.hash_types); n->rss_data.indirections_len = virtio_lduw_p(vdev, &cfg.indirection_table_mask); if (!do_rss) { @@ -1474,12 +1409,12 @@ static uint16_t virtio_net_handle_rss(VirtIONet *n, err_value = temp.b; goto error; } - if (!temp.b && n->rss_data.hash_types) { + if (!temp.b && n->rss_data.runtime_hash_types) { err_msg = "No key provided"; err_value = 0; goto error; } - if (!temp.b && !n->rss_data.hash_types) { + if (!temp.b && !n->rss_data.runtime_hash_types) { virtio_net_disable_rss(n); return queue_pairs; } @@ -1881,7 +1816,7 @@ static int virtio_net_process_rss(NetClientState *nc, const uint8_t *buf, net_rx_pkt_set_protocols(pkt, &iov, 1, n->host_hdr_len); net_rx_pkt_get_protocols(pkt, &hasip4, &hasip6, &l4hdr_proto); net_hash_type = virtio_net_get_hash_type(hasip4, hasip6, l4hdr_proto, - n->rss_data.hash_types); + n->rss_data.runtime_hash_types); if (net_hash_type > NetPktRssIpV6UdpEx) { if (n->rss_data.populate_hash) { hdr->hash_value = VIRTIO_NET_HASH_REPORT_NONE; @@ -3022,11 +2957,10 @@ static void virtio_net_del_queue(VirtIONet *n, int index) virtio_del_queue(vdev, index * 2 + 1); } -static void virtio_net_change_num_queue_pairs(VirtIONet *n, int new_max_queue_pairs) +static void virtio_net_change_num_queues(VirtIONet *n, int new_num_queues) { VirtIODevice *vdev = VIRTIO_DEVICE(n); int old_num_queues = virtio_get_num_queues(vdev); - int new_num_queues = new_max_queue_pairs * 2 + 1; int i; assert(old_num_queues >= 3); @@ -3062,20 +2996,115 @@ static void virtio_net_set_multiqueue(VirtIONet *n, int multiqueue) int max = multiqueue ? n->max_queue_pairs : 1; n->multiqueue = multiqueue; - virtio_net_change_num_queue_pairs(n, max); + virtio_net_change_num_queues(n, max * 2 + 1); virtio_net_set_queue_pairs(n); } -static int virtio_net_pre_load_queues(VirtIODevice *vdev) +static int virtio_net_pre_load_queues(VirtIODevice *vdev, uint32_t n) { - virtio_net_set_multiqueue(VIRTIO_NET(vdev), - virtio_has_feature(vdev->guest_features, VIRTIO_NET_F_RSS) || - virtio_has_feature(vdev->guest_features, VIRTIO_NET_F_MQ)); + virtio_net_change_num_queues(VIRTIO_NET(vdev), n); return 0; } +static uint64_t virtio_net_get_features(VirtIODevice *vdev, uint64_t features, + Error **errp) +{ + VirtIONet *n = VIRTIO_NET(vdev); + NetClientState *nc = qemu_get_queue(n->nic); + uint32_t supported_hash_types = n->rss_data.supported_hash_types; + uint32_t peer_hash_types = n->rss_data.peer_hash_types; + bool use_own_hash = + (supported_hash_types & VIRTIO_NET_RSS_SUPPORTED_HASHES) == + supported_hash_types; + bool use_peer_hash = + n->rss_data.peer_hash_available && + (supported_hash_types & peer_hash_types) == supported_hash_types; + + /* Firstly sync all virtio-net possible supported features */ + features |= n->host_features; + + virtio_add_feature(&features, VIRTIO_NET_F_MAC); + + if (!peer_has_vnet_hdr(n)) { + virtio_clear_feature(&features, VIRTIO_NET_F_CSUM); + virtio_clear_feature(&features, VIRTIO_NET_F_HOST_TSO4); + virtio_clear_feature(&features, VIRTIO_NET_F_HOST_TSO6); + virtio_clear_feature(&features, VIRTIO_NET_F_HOST_ECN); + + virtio_clear_feature(&features, VIRTIO_NET_F_GUEST_CSUM); + virtio_clear_feature(&features, VIRTIO_NET_F_GUEST_TSO4); + virtio_clear_feature(&features, VIRTIO_NET_F_GUEST_TSO6); + virtio_clear_feature(&features, VIRTIO_NET_F_GUEST_ECN); + + virtio_clear_feature(&features, VIRTIO_NET_F_HOST_USO); + virtio_clear_feature(&features, VIRTIO_NET_F_GUEST_USO4); + virtio_clear_feature(&features, VIRTIO_NET_F_GUEST_USO6); + + virtio_clear_feature(&features, VIRTIO_NET_F_HASH_REPORT); + } + + if (!peer_has_vnet_hdr(n) || !peer_has_ufo(n)) { + virtio_clear_feature(&features, VIRTIO_NET_F_GUEST_UFO); + virtio_clear_feature(&features, VIRTIO_NET_F_HOST_UFO); + } + + if (!peer_has_uso(n)) { + virtio_clear_feature(&features, VIRTIO_NET_F_HOST_USO); + virtio_clear_feature(&features, VIRTIO_NET_F_GUEST_USO4); + virtio_clear_feature(&features, VIRTIO_NET_F_GUEST_USO6); + } + + if (!get_vhost_net(nc->peer)) { + if (!use_own_hash) { + virtio_clear_feature(&features, VIRTIO_NET_F_HASH_REPORT); + virtio_clear_feature(&features, VIRTIO_NET_F_RSS); + } else if (virtio_has_feature(features, VIRTIO_NET_F_RSS)) { + virtio_net_load_ebpf(n, errp); + } + + return features; + } + + if (!use_peer_hash) { + virtio_clear_feature(&features, VIRTIO_NET_F_HASH_REPORT); + + if (!use_own_hash || !virtio_net_attach_ebpf_to_backend(n->nic, -1)) { + if (!virtio_net_load_ebpf(n, errp)) { + return features; + } + + virtio_clear_feature(&features, VIRTIO_NET_F_RSS); + } + } + + features = vhost_net_get_features(get_vhost_net(nc->peer), features); + vdev->backend_features = features; + + if (n->mtu_bypass_backend && + (n->host_features & 1ULL << VIRTIO_NET_F_MTU)) { + features |= (1ULL << VIRTIO_NET_F_MTU); + } + + /* + * Since GUEST_ANNOUNCE is emulated the feature bit could be set without + * enabled. This happens in the vDPA case. + * + * Make sure the feature set is not incoherent, as the driver could refuse + * to start. + * + * TODO: QEMU is able to emulate a CVQ just for guest_announce purposes, + * helping guest to notify the new location with vDPA devices that does not + * support it. + */ + if (!virtio_has_feature(vdev->backend_features, VIRTIO_NET_F_CTRL_VQ)) { + virtio_clear_feature(&features, VIRTIO_NET_F_GUEST_ANNOUNCE); + } + + return features; +} + static int virtio_net_post_load_device(void *opaque, int version_id) { VirtIONet *n = opaque; @@ -3314,6 +3343,17 @@ static const VMStateDescription vmstate_virtio_net_has_vnet = { }, }; +static int virtio_net_rss_post_load(void *opaque, int version_id) +{ + VirtIONet *n = VIRTIO_NET(opaque); + + if (version_id == 1) { + n->rss_data.supported_hash_types = VIRTIO_NET_RSS_SUPPORTED_HASHES; + } + + return 0; +} + static bool virtio_net_rss_needed(void *opaque) { return VIRTIO_NET(opaque)->rss_data.enabled; @@ -3321,14 +3361,16 @@ static bool virtio_net_rss_needed(void *opaque) static const VMStateDescription vmstate_virtio_net_rss = { .name = "virtio-net-device/rss", - .version_id = 1, + .version_id = 2, .minimum_version_id = 1, + .post_load = virtio_net_rss_post_load, .needed = virtio_net_rss_needed, .fields = (const VMStateField[]) { VMSTATE_BOOL(rss_data.enabled, VirtIONet), VMSTATE_BOOL(rss_data.redirect, VirtIONet), VMSTATE_BOOL(rss_data.populate_hash, VirtIONet), - VMSTATE_UINT32(rss_data.hash_types, VirtIONet), + VMSTATE_UINT32(rss_data.runtime_hash_types, VirtIONet), + VMSTATE_UINT32_V(rss_data.supported_hash_types, VirtIONet, 2), VMSTATE_UINT16(rss_data.indirections_len, VirtIONet), VMSTATE_UINT16(rss_data.default_queue, VirtIONet), VMSTATE_UINT8_ARRAY(rss_data.key, VirtIONet, @@ -3915,8 +3957,17 @@ static void virtio_net_device_realize(DeviceState *dev, Error **errp) net_rx_pkt_init(&n->rx_pkt); - if (virtio_has_feature(n->host_features, VIRTIO_NET_F_RSS)) { - virtio_net_load_ebpf(n, errp); + if (qemu_get_vnet_hash_supported_types(qemu_get_queue(n->nic)->peer, + &n->rss_data.peer_hash_types)) { + n->rss_data.peer_hash_available = true; + n->rss_data.supported_hash_types = + n->rss_data.specified_hash_types.on_bits | + (n->rss_data.specified_hash_types.auto_bits & + n->rss_data.peer_hash_types); + } else { + n->rss_data.supported_hash_types = + n->rss_data.specified_hash_types.on_bits | + n->rss_data.specified_hash_types.auto_bits; } } @@ -4133,6 +4184,42 @@ static const Property virtio_net_properties[] = { VIRTIO_NET_F_GUEST_USO6, true), DEFINE_PROP_BIT64("host_uso", VirtIONet, host_features, VIRTIO_NET_F_HOST_USO, true), + DEFINE_PROP_ON_OFF_AUTO_BIT64("hash-ipv4", VirtIONet, + rss_data.specified_hash_types, + VIRTIO_NET_HASH_REPORT_IPv4 - 1, + ON_OFF_AUTO_AUTO), + DEFINE_PROP_ON_OFF_AUTO_BIT64("hash-tcp4", VirtIONet, + rss_data.specified_hash_types, + VIRTIO_NET_HASH_REPORT_TCPv4 - 1, + ON_OFF_AUTO_AUTO), + DEFINE_PROP_ON_OFF_AUTO_BIT64("hash-udp4", VirtIONet, + rss_data.specified_hash_types, + VIRTIO_NET_HASH_REPORT_UDPv4 - 1, + ON_OFF_AUTO_AUTO), + DEFINE_PROP_ON_OFF_AUTO_BIT64("hash-ipv6", VirtIONet, + rss_data.specified_hash_types, + VIRTIO_NET_HASH_REPORT_IPv6 - 1, + ON_OFF_AUTO_AUTO), + DEFINE_PROP_ON_OFF_AUTO_BIT64("hash-tcp6", VirtIONet, + rss_data.specified_hash_types, + VIRTIO_NET_HASH_REPORT_TCPv6 - 1, + ON_OFF_AUTO_AUTO), + DEFINE_PROP_ON_OFF_AUTO_BIT64("hash-udp6", VirtIONet, + rss_data.specified_hash_types, + VIRTIO_NET_HASH_REPORT_UDPv6 - 1, + ON_OFF_AUTO_AUTO), + DEFINE_PROP_ON_OFF_AUTO_BIT64("hash-ipv6ex", VirtIONet, + rss_data.specified_hash_types, + VIRTIO_NET_HASH_REPORT_IPv6_EX - 1, + ON_OFF_AUTO_AUTO), + DEFINE_PROP_ON_OFF_AUTO_BIT64("hash-tcp6ex", VirtIONet, + rss_data.specified_hash_types, + VIRTIO_NET_HASH_REPORT_TCPv6_EX - 1, + ON_OFF_AUTO_AUTO), + DEFINE_PROP_ON_OFF_AUTO_BIT64("hash-udp6ex", VirtIONet, + rss_data.specified_hash_types, + VIRTIO_NET_HASH_REPORT_UDPv6_EX - 1, + ON_OFF_AUTO_AUTO), }; static void virtio_net_class_init(ObjectClass *klass, const void *data) diff --git a/hw/net/vmxnet3.c b/hw/net/vmxnet3.c index 7c0ca56..af73aa8 100644 --- a/hw/net/vmxnet3.c +++ b/hw/net/vmxnet3.c @@ -22,7 +22,6 @@ #include "net/tap.h" #include "net/checksum.h" #include "system/system.h" -#include "qemu/bswap.h" #include "qemu/log.h" #include "qemu/module.h" #include "hw/pci/msix.h" diff --git a/hw/pci-host/Kconfig b/hw/pci-host/Kconfig index 35c0415..9824fa1 100644 --- a/hw/pci-host/Kconfig +++ b/hw/pci-host/Kconfig @@ -54,6 +54,7 @@ config PCI_EXPRESS_Q35 config PCI_EXPRESS_GENERIC_BRIDGE bool select PCI_EXPRESS + imply ACPI_PCI config PCI_EXPRESS_XILINX bool diff --git a/hw/pci-host/gpex-acpi.c b/hw/pci-host/gpex-acpi.c index 0aba47c..952a0ac 100644 --- a/hw/pci-host/gpex-acpi.c +++ b/hw/pci-host/gpex-acpi.c @@ -1,5 +1,6 @@ #include "qemu/osdep.h" #include "hw/acpi/aml-build.h" +#include "hw/acpi/pci.h" #include "hw/pci-host/gpex.h" #include "hw/arm/virt.h" #include "hw/pci/pci_bus.h" @@ -50,61 +51,10 @@ static void acpi_dsdt_add_pci_route_table(Aml *dev, uint32_t irq, } } -static void acpi_dsdt_add_pci_osc(Aml *dev) +static Aml *build_pci_host_bridge_dsm_method(void) { - Aml *method, *UUID, *ifctx, *ifctx1, *elsectx, *buf; - - /* Declare an _OSC (OS Control Handoff) method */ - aml_append(dev, aml_name_decl("SUPP", aml_int(0))); - aml_append(dev, aml_name_decl("CTRL", aml_int(0))); - method = aml_method("_OSC", 4, AML_NOTSERIALIZED); - aml_append(method, - aml_create_dword_field(aml_arg(3), aml_int(0), "CDW1")); - - /* PCI Firmware Specification 3.0 - * 4.5.1. _OSC Interface for PCI Host Bridge Devices - * The _OSC interface for a PCI/PCI-X/PCI Express hierarchy is - * identified by the Universal Unique IDentifier (UUID) - * 33DB4D5B-1FF7-401C-9657-7441C03DD766 - */ - UUID = aml_touuid("33DB4D5B-1FF7-401C-9657-7441C03DD766"); - ifctx = aml_if(aml_equal(aml_arg(0), UUID)); - aml_append(ifctx, - aml_create_dword_field(aml_arg(3), aml_int(4), "CDW2")); - aml_append(ifctx, - aml_create_dword_field(aml_arg(3), aml_int(8), "CDW3")); - aml_append(ifctx, aml_store(aml_name("CDW2"), aml_name("SUPP"))); - aml_append(ifctx, aml_store(aml_name("CDW3"), aml_name("CTRL"))); - - /* - * Allow OS control for all 5 features: - * PCIeHotplug SHPCHotplug PME AER PCIeCapability. - */ - aml_append(ifctx, aml_and(aml_name("CTRL"), aml_int(0x1F), - aml_name("CTRL"))); - - ifctx1 = aml_if(aml_lnot(aml_equal(aml_arg(1), aml_int(0x1)))); - aml_append(ifctx1, aml_or(aml_name("CDW1"), aml_int(0x08), - aml_name("CDW1"))); - aml_append(ifctx, ifctx1); - - ifctx1 = aml_if(aml_lnot(aml_equal(aml_name("CDW3"), aml_name("CTRL")))); - aml_append(ifctx1, aml_or(aml_name("CDW1"), aml_int(0x10), - aml_name("CDW1"))); - aml_append(ifctx, ifctx1); - - aml_append(ifctx, aml_store(aml_name("CTRL"), aml_name("CDW3"))); - aml_append(ifctx, aml_return(aml_arg(3))); - aml_append(method, ifctx); - - elsectx = aml_else(); - aml_append(elsectx, aml_or(aml_name("CDW1"), aml_int(4), - aml_name("CDW1"))); - aml_append(elsectx, aml_return(aml_arg(3))); - aml_append(method, elsectx); - aml_append(dev, method); - - method = aml_method("_DSM", 4, AML_NOTSERIALIZED); + Aml *method = aml_method("_DSM", 4, AML_NOTSERIALIZED); + Aml *UUID, *ifctx, *ifctx1, *buf; /* PCI Firmware Specification 3.0 * 4.6.1. _DSM for PCI Express Slot Information @@ -123,7 +73,16 @@ static void acpi_dsdt_add_pci_osc(Aml *dev) byte_list[0] = 0; buf = aml_buffer(1, byte_list); aml_append(method, aml_return(buf)); - aml_append(dev, method); + return method; +} + +static void acpi_dsdt_add_host_bridge_methods(Aml *dev, + bool enable_native_pcie_hotplug) +{ + /* Declare an _OSC (OS Control Handoff) method */ + aml_append(dev, + build_pci_host_bridge_osc_method(enable_native_pcie_hotplug)); + aml_append(dev, build_pci_host_bridge_dsm_method()); } void acpi_dsdt_add_gpex(Aml *scope, struct GPEXConfig *cfg) @@ -192,7 +151,8 @@ void acpi_dsdt_add_gpex(Aml *scope, struct GPEXConfig *cfg) if (is_cxl) { build_cxl_osc_method(dev); } else { - acpi_dsdt_add_pci_osc(dev); + /* pxb bridges do not have ACPI PCI Hot-plug enabled */ + acpi_dsdt_add_host_bridge_methods(dev, true); } aml_append(scope, dev); @@ -267,7 +227,7 @@ void acpi_dsdt_add_gpex(Aml *scope, struct GPEXConfig *cfg) } aml_append(dev, aml_name_decl("_CRS", rbuf)); - acpi_dsdt_add_pci_osc(dev); + acpi_dsdt_add_host_bridge_methods(dev, cfg->pci_native_hotplug); Aml *dev_res0 = aml_device("%s", "RES0"); aml_append(dev_res0, aml_name_decl("_HID", aml_string("PNP0C02"))); diff --git a/hw/pci-host/gt64120.c b/hw/pci-host/gt64120.c index b12a256..b1d96f6 100644 --- a/hw/pci-host/gt64120.c +++ b/hw/pci-host/gt64120.c @@ -28,6 +28,7 @@ #include "qapi/error.h" #include "qemu/units.h" #include "qemu/log.h" +#include "qemu/bswap.h" #include "hw/qdev-properties.h" #include "hw/registerfields.h" #include "hw/pci/pci_device.h" diff --git a/hw/pci-host/pnv_phb3.c b/hw/pci-host/pnv_phb3.c index a4335f4..5d8383f 100644 --- a/hw/pci-host/pnv_phb3.c +++ b/hw/pci-host/pnv_phb3.c @@ -8,6 +8,7 @@ */ #include "qemu/osdep.h" #include "qemu/log.h" +#include "qemu/bswap.h" #include "qapi/visitor.h" #include "qapi/error.h" #include "hw/pci-host/pnv_phb3_regs.h" diff --git a/hw/pci-host/pnv_phb4.c b/hw/pci-host/pnv_phb4.c index 77ea352..1899205 100644 --- a/hw/pci-host/pnv_phb4.c +++ b/hw/pci-host/pnv_phb4.c @@ -8,6 +8,7 @@ */ #include "qemu/osdep.h" #include "qemu/log.h" +#include "qemu/bswap.h" #include "qapi/visitor.h" #include "qapi/error.h" #include "target/ppc/cpu.h" diff --git a/hw/pci-host/ppce500.c b/hw/pci-host/ppce500.c index 52269b0..975d191 100644 --- a/hw/pci-host/ppce500.c +++ b/hw/pci-host/ppce500.c @@ -20,7 +20,6 @@ #include "migration/vmstate.h" #include "hw/pci/pci_device.h" #include "hw/pci/pci_host.h" -#include "qemu/bswap.h" #include "hw/pci-host/ppce500.h" #include "qom/object.h" diff --git a/hw/pci-host/sh_pci.c b/hw/pci-host/sh_pci.c index de8f6a8..62fb945 100644 --- a/hw/pci-host/sh_pci.c +++ b/hw/pci-host/sh_pci.c @@ -28,7 +28,6 @@ #include "hw/irq.h" #include "hw/pci/pci_device.h" #include "hw/pci/pci_host.h" -#include "qemu/bswap.h" #include "qemu/module.h" #include "qom/object.h" diff --git a/hw/riscv/virt-acpi-build.c b/hw/riscv/virt-acpi-build.c index 8b5683d..ee1416d 100644 --- a/hw/riscv/virt-acpi-build.c +++ b/hw/riscv/virt-acpi-build.c @@ -894,7 +894,10 @@ static void virt_acpi_build(RISCVVirtState *s, AcpiBuildTables *tables) } acpi_add_table(table_offsets, tables_blob); - spcr_setup(tables_blob, tables->linker, s); + + if (ms->acpi_spcr_enabled) { + spcr_setup(tables_blob, tables->linker, s); + } acpi_add_table(table_offsets, tables_blob); { diff --git a/hw/s390x/s390-pci-inst.c b/hw/s390x/s390-pci-inst.c index b5dddb2..a3bb5aa 100644 --- a/hw/s390x/s390-pci-inst.c +++ b/hw/s390x/s390-pci-inst.c @@ -16,6 +16,7 @@ #include "exec/target_page.h" #include "system/memory.h" #include "qemu/error-report.h" +#include "qemu/bswap.h" #include "system/hw_accel.h" #include "hw/boards.h" #include "hw/pci/pci_device.h" diff --git a/hw/scsi/esp.c b/hw/scsi/esp.c index f24991f..1d264c4 100644 --- a/hw/scsi/esp.c +++ b/hw/scsi/esp.c @@ -275,6 +275,7 @@ static int esp_select(ESPState *s) if (!s->current_dev) { /* No such drive */ s->rregs[ESP_RSTAT] = 0; + s->asc_mode = ESP_ASC_MODE_DIS; s->rregs[ESP_RINTR] = INTR_DC; esp_raise_irq(s); return -1; @@ -284,6 +285,7 @@ static int esp_select(ESPState *s) * Note that we deliberately don't raise the IRQ here: this will be done * either in esp_transfer_data() or esp_command_complete() */ + s->asc_mode = ESP_ASC_MODE_INI; return 0; } @@ -308,6 +310,7 @@ static void do_command_phase(ESPState *s) if (!current_lun) { /* No such drive */ s->rregs[ESP_RSTAT] = 0; + s->asc_mode = ESP_ASC_MODE_DIS; s->rregs[ESP_RINTR] = INTR_DC; s->rregs[ESP_RSEQ] = SEQ_0; esp_raise_irq(s); @@ -487,8 +490,10 @@ static void esp_do_dma(ESPState *s) case STAT_MO: if (s->dma_memory_read) { len = MIN(len, fifo8_num_free(&s->cmdfifo)); - s->dma_memory_read(s->dma_opaque, buf, len); - esp_set_tc(s, esp_get_tc(s) - len); + if (len) { + s->dma_memory_read(s->dma_opaque, buf, len); + esp_set_tc(s, esp_get_tc(s) - len); + } } else { len = esp_fifo_pop_buf(s, buf, fifo8_num_used(&s->fifo)); len = MIN(fifo8_num_free(&s->cmdfifo), len); @@ -541,9 +546,11 @@ static void esp_do_dma(ESPState *s) trace_esp_do_dma(cmdlen, len); if (s->dma_memory_read) { len = MIN(len, fifo8_num_free(&s->cmdfifo)); - s->dma_memory_read(s->dma_opaque, buf, len); - fifo8_push_all(&s->cmdfifo, buf, len); - esp_set_tc(s, esp_get_tc(s) - len); + if (len) { + s->dma_memory_read(s->dma_opaque, buf, len); + fifo8_push_all(&s->cmdfifo, buf, len); + esp_set_tc(s, esp_get_tc(s) - len); + } } else { len = esp_fifo_pop_buf(s, buf, fifo8_num_used(&s->fifo)); len = MIN(fifo8_num_free(&s->cmdfifo), len); @@ -572,8 +579,10 @@ static void esp_do_dma(ESPState *s) switch (s->rregs[ESP_CMD]) { case CMD_TI | CMD_DMA: if (s->dma_memory_read) { - s->dma_memory_read(s->dma_opaque, s->async_buf, len); - esp_set_tc(s, esp_get_tc(s) - len); + if (len) { + s->dma_memory_read(s->dma_opaque, s->async_buf, len); + esp_set_tc(s, esp_get_tc(s) - len); + } } else { /* Copy FIFO data to device */ len = MIN(s->async_len, ESP_FIFO_SZ); @@ -625,7 +634,9 @@ static void esp_do_dma(ESPState *s) switch (s->rregs[ESP_CMD]) { case CMD_TI | CMD_DMA: if (s->dma_memory_write) { - s->dma_memory_write(s->dma_opaque, s->async_buf, len); + if (len) { + s->dma_memory_write(s->dma_opaque, s->async_buf, len); + } } else { /* Copy device data to FIFO */ len = MIN(len, fifo8_num_free(&s->fifo)); @@ -675,6 +686,7 @@ static void esp_do_dma(ESPState *s) buf[0] = s->status; if (s->dma_memory_write) { + /* Length already non-zero */ s->dma_memory_write(s->dma_opaque, buf, len); } else { esp_fifo_push_buf(s, buf, len); @@ -709,6 +721,7 @@ static void esp_do_dma(ESPState *s) buf[0] = 0; if (s->dma_memory_write) { + /* Length already non-zero */ s->dma_memory_write(s->dma_opaque, buf, len); } else { esp_fifo_push_buf(s, buf, len); @@ -1012,6 +1025,7 @@ void esp_transfer_data(SCSIRequest *req, uint32_t len) */ s->rregs[ESP_RINTR] |= INTR_BS | INTR_FC; s->rregs[ESP_RSEQ] = SEQ_CD; + esp_raise_irq(s); break; case CMD_SELATNS | CMD_DMA: @@ -1022,20 +1036,21 @@ void esp_transfer_data(SCSIRequest *req, uint32_t len) */ s->rregs[ESP_RINTR] |= INTR_BS; s->rregs[ESP_RSEQ] = SEQ_MO; + esp_raise_irq(s); break; case CMD_TI | CMD_DMA: case CMD_TI: /* - * Bus service interrupt raised because of initial change to - * DATA phase + * If the final COMMAND phase data was transferred using a TI + * command, clear ESP_CMD to terminate the TI command and raise + * the completion interrupt */ s->rregs[ESP_CMD] = 0; s->rregs[ESP_RINTR] |= INTR_BS; + esp_raise_irq(s); break; } - - esp_raise_irq(s); } /* @@ -1090,6 +1105,7 @@ void esp_hard_reset(ESPState *s) fifo8_reset(&s->cmdfifo); s->dma = 0; s->dma_cb = NULL; + s->asc_mode = ESP_ASC_MODE_DIS; s->rregs[ESP_CFG1] = 7; } @@ -1113,6 +1129,38 @@ static void parent_esp_reset(ESPState *s, int irq, int level) } } +static bool esp_cmd_is_valid(ESPState *s, uint8_t cmd) +{ + uint8_t cmd_group = (cmd & CMD_GRP_MASK) >> 4; + + /* Always allow misc commands */ + if (cmd_group == CMD_GRP_MISC) { + return true; + } + + switch (s->asc_mode) { + case ESP_ASC_MODE_DIS: + /* Disconnected mode: only allow disconnected commands */ + if (cmd_group == CMD_GRP_DISC) { + return true; + } + break; + + case ESP_ASC_MODE_INI: + /* Initiator mode: allow initiator commands */ + if (cmd_group == CMD_GRP_INIT) { + return true; + } + break; + + default: + g_assert_not_reached(); + } + + trace_esp_invalid_cmd(cmd, s->asc_mode); + return false; +} + static void esp_run_cmd(ESPState *s) { uint8_t cmd = s->rregs[ESP_CMD]; @@ -1158,6 +1206,7 @@ static void esp_run_cmd(ESPState *s) break; case CMD_MSGACC: trace_esp_mem_writeb_cmd_msgacc(cmd); + s->asc_mode = ESP_ASC_MODE_DIS; s->rregs[ESP_RINTR] |= INTR_DC; s->rregs[ESP_RSEQ] = 0; s->rregs[ESP_RFLAGS] = 0; @@ -1268,6 +1317,11 @@ void esp_reg_write(ESPState *s, uint32_t saddr, uint64_t val) break; case ESP_CMD: s->rregs[saddr] = val; + if (!esp_cmd_is_valid(s, s->rregs[saddr])) { + s->rregs[ESP_RSTAT] |= INTR_IL; + esp_raise_irq(s); + break; + } esp_run_cmd(s); break; case ESP_WBUSID ... ESP_WSYNO: @@ -1325,6 +1379,14 @@ static bool esp_is_between_version_5_and_6(void *opaque, int version_id) return version_id >= 5 && version_id <= 6; } +static bool esp_is_version_8(void *opaque, int version_id) +{ + ESPState *s = ESP(opaque); + + version_id = MIN(version_id, s->mig_version_id); + return version_id >= 8; +} + int esp_pre_save(void *opaque) { ESPState *s = ESP(object_resolve_path_component( @@ -1356,13 +1418,18 @@ static int esp_post_load(void *opaque, int version_id) } } + if (version_id < 8) { + /* Assume initiator mode to allow all commands to continue */ + s->asc_mode = ESP_ASC_MODE_INI; + } + s->mig_version_id = vmstate_esp.version_id; return 0; } const VMStateDescription vmstate_esp = { .name = "esp", - .version_id = 7, + .version_id = 8, .minimum_version_id = 3, .post_load = esp_post_load, .fields = (const VMStateField[]) { @@ -1394,6 +1461,7 @@ const VMStateDescription vmstate_esp = { esp_is_between_version_5_and_6), VMSTATE_UINT8_TEST(lun, ESPState, esp_is_version_6), VMSTATE_BOOL(drq_state, ESPState), + VMSTATE_UINT8_TEST(asc_mode, ESPState, esp_is_version_8), VMSTATE_END_OF_LIST() }, }; diff --git a/hw/scsi/trace-events b/hw/scsi/trace-events index f0f2a98..6c2788e 100644 --- a/hw/scsi/trace-events +++ b/hw/scsi/trace-events @@ -198,6 +198,7 @@ esp_mem_writeb_cmd_ensel(uint32_t val) "Enable selection (0x%2.2x)" esp_mem_writeb_cmd_dissel(uint32_t val) "Disable selection (0x%2.2x)" esp_mem_writeb_cmd_ti(uint32_t val) "Transfer Information (0x%2.2x)" esp_set_phase(const char *phase) "setting bus phase to %s" +esp_invalid_cmd(uint8_t cmd, uint8_t asc_mode) "command 0x%x asc_mode 0x%x" # esp-pci.c esp_pci_error_invalid_dma_direction(void) "invalid DMA transfer direction" diff --git a/hw/sensor/lsm303dlhc_mag.c b/hw/sensor/lsm303dlhc_mag.c index f9e501d..cd5773a 100644 --- a/hw/sensor/lsm303dlhc_mag.c +++ b/hw/sensor/lsm303dlhc_mag.c @@ -28,7 +28,6 @@ #include "qapi/visitor.h" #include "qemu/module.h" #include "qemu/log.h" -#include "qemu/bswap.h" enum LSM303DLHCMagReg { LSM303DLHC_MAG_REG_CRA = 0x00, diff --git a/hw/smbios/smbios.c b/hw/smbios/smbios.c index ad4cd67..1ac063c 100644 --- a/hw/smbios/smbios.c +++ b/hw/smbios/smbios.c @@ -17,6 +17,7 @@ #include "qemu/osdep.h" #include "qemu/units.h" +#include "qemu/bswap.h" #include "qapi/error.h" #include "qemu/config-file.h" #include "qemu/module.h" diff --git a/hw/uefi/trace.h b/hw/uefi/trace.h new file mode 100644 index 0000000..6aa1c93 --- /dev/null +++ b/hw/uefi/trace.h @@ -0,0 +1,2 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ +#include "trace/trace-hw_uefi.h" diff --git a/hw/uefi/var-service-core.c b/hw/uefi/var-service-core.c index 4836a0c..feec5a5 100644 --- a/hw/uefi/var-service-core.c +++ b/hw/uefi/var-service-core.c @@ -12,7 +12,7 @@ #include "hw/uefi/var-service-api.h" #include "hw/uefi/var-service-edk2.h" -#include "trace/trace-hw_uefi.h" +#include "trace.h" static int uefi_vars_pre_load(void *opaque) { diff --git a/hw/uefi/var-service-policy.c b/hw/uefi/var-service-policy.c index 3b1155f..58da4ad 100644 --- a/hw/uefi/var-service-policy.c +++ b/hw/uefi/var-service-policy.c @@ -14,7 +14,7 @@ #include "hw/uefi/var-service-api.h" #include "hw/uefi/var-service-edk2.h" -#include "trace/trace-hw_uefi.h" +#include "trace.h" static void calc_policy(uefi_var_policy *pol); diff --git a/hw/uefi/var-service-utils.c b/hw/uefi/var-service-utils.c index c9ef465..258013f 100644 --- a/hw/uefi/var-service-utils.c +++ b/hw/uefi/var-service-utils.c @@ -8,7 +8,7 @@ #include "hw/uefi/var-service.h" -#include "trace/trace-hw_uefi.h" +#include "trace.h" /* ------------------------------------------------------------------ */ diff --git a/hw/uefi/var-service-vars.c b/hw/uefi/var-service-vars.c index 7f98d77..37d05b7 100644 --- a/hw/uefi/var-service-vars.c +++ b/hw/uefi/var-service-vars.c @@ -12,7 +12,7 @@ #include "hw/uefi/var-service-api.h" #include "hw/uefi/var-service-edk2.h" -#include "trace/trace-hw_uefi.h" +#include "trace.h" #define EFI_VARIABLE_ATTRIBUTE_SUPPORTED \ (EFI_VARIABLE_NON_VOLATILE | \ diff --git a/hw/usb/dev-hid.c b/hw/usb/dev-hid.c index 54d064e..96623aa 100644 --- a/hw/usb/dev-hid.c +++ b/hw/usb/dev-hid.c @@ -491,14 +491,14 @@ static const uint8_t qemu_tablet_hid_report_descriptor[] = { 0xa1, 0x00, /* Collection (Physical) */ 0x05, 0x09, /* Usage Page (Button) */ 0x19, 0x01, /* Usage Minimum (1) */ - 0x29, 0x03, /* Usage Maximum (3) */ + 0x29, 0x05, /* Usage Maximum (5) */ 0x15, 0x00, /* Logical Minimum (0) */ 0x25, 0x01, /* Logical Maximum (1) */ - 0x95, 0x03, /* Report Count (3) */ + 0x95, 0x05, /* Report Count (5) */ 0x75, 0x01, /* Report Size (1) */ 0x81, 0x02, /* Input (Data, Variable, Absolute) */ 0x95, 0x01, /* Report Count (1) */ - 0x75, 0x05, /* Report Size (5) */ + 0x75, 0x03, /* Report Size (3) */ 0x81, 0x01, /* Input (Constant) */ 0x05, 0x01, /* Usage Page (Generic Desktop) */ 0x09, 0x30, /* Usage (X) */ diff --git a/hw/vfio/migration-multifd.c b/hw/vfio/migration-multifd.c index 5563548..9dc70fd 100644 --- a/hw/vfio/migration-multifd.c +++ b/hw/vfio/migration-multifd.c @@ -13,7 +13,6 @@ #include "hw/vfio/vfio-device.h" #include "migration/misc.h" #include "qapi/error.h" -#include "qemu/bswap.h" #include "qemu/error-report.h" #include "qemu/lockable.h" #include "qemu/main-loop.h" diff --git a/hw/virtio/meson.build b/hw/virtio/meson.build index 164f6fd..3ea7b3c 100644 --- a/hw/virtio/meson.build +++ b/hw/virtio/meson.build @@ -1,6 +1,7 @@ system_virtio_ss = ss.source_set() system_virtio_ss.add(files('virtio-bus.c')) system_virtio_ss.add(files('iothread-vq-mapping.c')) +system_virtio_ss.add(files('virtio-config-io.c')) system_virtio_ss.add(when: 'CONFIG_VIRTIO_PCI', if_true: files('virtio-pci.c')) system_virtio_ss.add(when: 'CONFIG_VIRTIO_MMIO', if_true: files('virtio-mmio.c')) system_virtio_ss.add(when: 'CONFIG_VIRTIO_CRYPTO', if_true: files('virtio-crypto.c')) @@ -10,11 +11,11 @@ system_virtio_ss.add(when: 'CONFIG_VHOST_VDPA_DEV', if_true: files('vdpa-dev.c') specific_virtio_ss = ss.source_set() specific_virtio_ss.add(files('virtio.c')) -specific_virtio_ss.add(files('virtio-config-io.c', 'virtio-qmp.c')) +specific_virtio_ss.add(files('virtio-qmp.c')) if have_vhost system_virtio_ss.add(files('vhost.c')) - specific_virtio_ss.add(files('vhost-backend.c', 'vhost-iova-tree.c')) + system_virtio_ss.add(files('vhost-backend.c', 'vhost-iova-tree.c')) if have_vhost_user # fixme - this really should be generic specific_virtio_ss.add(files('vhost-user.c')) @@ -43,22 +44,22 @@ if have_vhost endif if have_vhost_vdpa system_virtio_ss.add(files('vhost-vdpa.c')) - specific_virtio_ss.add(files('vhost-shadow-virtqueue.c')) + system_virtio_ss.add(files('vhost-shadow-virtqueue.c')) endif else system_virtio_ss.add(files('vhost-stub.c')) endif +system_virtio_ss.add(when: 'CONFIG_VHOST_USER_VSOCK', if_true: files('vhost-user-vsock.c')) +system_virtio_ss.add(when: 'CONFIG_VIRTIO_RNG', if_true: files('virtio-rng.c')) specific_virtio_ss.add(when: 'CONFIG_VIRTIO_BALLOON', if_true: files('virtio-balloon.c')) specific_virtio_ss.add(when: 'CONFIG_VHOST_USER_FS', if_true: files('vhost-user-fs.c')) specific_virtio_ss.add(when: 'CONFIG_VIRTIO_PMEM', if_true: files('virtio-pmem.c')) specific_virtio_ss.add(when: 'CONFIG_VHOST_VSOCK', if_true: files('vhost-vsock.c')) -specific_virtio_ss.add(when: 'CONFIG_VHOST_USER_VSOCK', if_true: files('vhost-user-vsock.c')) -specific_virtio_ss.add(when: 'CONFIG_VIRTIO_RNG', if_true: files('virtio-rng.c')) -specific_virtio_ss.add(when: 'CONFIG_VIRTIO_NSM', if_true: [files('virtio-nsm.c', 'cbor-helpers.c'), libcbor]) specific_virtio_ss.add(when: 'CONFIG_VIRTIO_MEM', if_true: files('virtio-mem.c')) -specific_virtio_ss.add(when: 'CONFIG_VHOST_USER_SCMI', if_true: files('vhost-user-scmi.c')) -specific_virtio_ss.add(when: ['CONFIG_VIRTIO_PCI', 'CONFIG_VHOST_USER_SCMI'], if_true: files('vhost-user-scmi-pci.c')) +system_virtio_ss.add(when: 'CONFIG_VIRTIO_NSM', if_true: files('virtio-nsm.c')) +system_virtio_ss.add(when: 'CONFIG_VIRTIO_NSM', if_true: [files('cbor-helpers.c'), libcbor]) +system_virtio_ss.add(when: 'CONFIG_VHOST_USER_SCMI', if_true: files('vhost-user-scmi.c')) virtio_pci_ss = ss.source_set() virtio_pci_ss.add(when: 'CONFIG_VHOST_VSOCK', if_true: files('vhost-vsock-pci.c')) @@ -67,6 +68,7 @@ virtio_pci_ss.add(when: 'CONFIG_VHOST_USER_BLK', if_true: files('vhost-user-blk- virtio_pci_ss.add(when: 'CONFIG_VHOST_USER_SCSI', if_true: files('vhost-user-scsi-pci.c')) virtio_pci_ss.add(when: 'CONFIG_VHOST_SCSI', if_true: files('vhost-scsi-pci.c')) virtio_pci_ss.add(when: 'CONFIG_VHOST_USER_FS', if_true: files('vhost-user-fs-pci.c')) +virtio_pci_ss.add(when: 'CONFIG_VHOST_USER_SCMI', if_true: files('vhost-user-scmi-pci.c')) virtio_pci_ss.add(when: 'CONFIG_VIRTIO_CRYPTO', if_true: files('virtio-crypto-pci.c')) virtio_pci_ss.add(when: 'CONFIG_VIRTIO_INPUT_HOST', if_true: files('virtio-input-host-pci.c')) @@ -85,7 +87,7 @@ virtio_pci_ss.add(when: 'CONFIG_VIRTIO_MEM', if_true: files('virtio-mem-pci.c')) virtio_pci_ss.add(when: 'CONFIG_VHOST_VDPA_DEV', if_true: files('vdpa-dev-pci.c')) virtio_pci_ss.add(when: 'CONFIG_VIRTIO_MD', if_true: files('virtio-md-pci.c')) -specific_virtio_ss.add_all(when: 'CONFIG_VIRTIO_PCI', if_true: virtio_pci_ss) +system_virtio_ss.add_all(when: 'CONFIG_VIRTIO_PCI', if_true: virtio_pci_ss) system_ss.add_all(when: 'CONFIG_VIRTIO', if_true: system_virtio_ss) system_ss.add(when: 'CONFIG_VIRTIO', if_false: files('vhost-stub.c')) diff --git a/hw/virtio/vhost.c b/hw/virtio/vhost.c index fc43853..c30ea11 100644 --- a/hw/virtio/vhost.c +++ b/hw/virtio/vhost.c @@ -47,12 +47,6 @@ static struct vhost_log *vhost_log[VHOST_BACKEND_TYPE_MAX]; static struct vhost_log *vhost_log_shm[VHOST_BACKEND_TYPE_MAX]; static QLIST_HEAD(, vhost_dev) vhost_log_devs[VHOST_BACKEND_TYPE_MAX]; -/* Memslots used by backends that support private memslots (without an fd). */ -static unsigned int used_memslots; - -/* Memslots used by backends that only support shared memslots (with an fd). */ -static unsigned int used_shared_memslots; - static QLIST_HEAD(, vhost_dev) vhost_devices = QLIST_HEAD_INITIALIZER(vhost_devices); @@ -74,15 +68,15 @@ unsigned int vhost_get_free_memslots(void) QLIST_FOREACH(hdev, &vhost_devices, entry) { unsigned int r = hdev->vhost_ops->vhost_backend_memslots_limit(hdev); - unsigned int cur_free; + unsigned int cur_free = r - hdev->mem->nregions; - if (hdev->vhost_ops->vhost_backend_no_private_memslots && - hdev->vhost_ops->vhost_backend_no_private_memslots(hdev)) { - cur_free = r - used_shared_memslots; + if (unlikely(r < hdev->mem->nregions)) { + warn_report_once("used (%u) vhost backend memory slots exceed" + " the device limit (%u).", hdev->mem->nregions, r); + free = 0; } else { - cur_free = r - used_memslots; + free = MIN(free, cur_free); } - free = MIN(free, cur_free); } return free; } @@ -666,13 +660,6 @@ static void vhost_commit(MemoryListener *listener) dev->mem = g_realloc(dev->mem, regions_size); dev->mem->nregions = dev->n_mem_sections; - if (dev->vhost_ops->vhost_backend_no_private_memslots && - dev->vhost_ops->vhost_backend_no_private_memslots(dev)) { - used_shared_memslots = dev->mem->nregions; - } else { - used_memslots = dev->mem->nregions; - } - for (i = 0; i < dev->n_mem_sections; i++) { struct vhost_memory_region *cur_vmr = dev->mem->regions + i; struct MemoryRegionSection *mrs = dev->mem_sections + i; @@ -1367,25 +1354,30 @@ fail_alloc_desc: return r; } -int vhost_virtqueue_stop(struct vhost_dev *dev, - struct VirtIODevice *vdev, - struct vhost_virtqueue *vq, - unsigned idx) +static int do_vhost_virtqueue_stop(struct vhost_dev *dev, + struct VirtIODevice *vdev, + struct vhost_virtqueue *vq, + unsigned idx, bool force) { int vhost_vq_index = dev->vhost_ops->vhost_get_vq_index(dev, idx); struct vhost_vring_state state = { .index = vhost_vq_index, }; - int r; + int r = 0; if (virtio_queue_get_desc_addr(vdev, idx) == 0) { /* Don't stop the virtqueue which might have not been started */ return 0; } - r = dev->vhost_ops->vhost_get_vring_base(dev, &state); - if (r < 0) { - VHOST_OPS_DEBUG(r, "vhost VQ %u ring restore failed: %d", idx, r); + if (!force) { + r = dev->vhost_ops->vhost_get_vring_base(dev, &state); + if (r < 0) { + VHOST_OPS_DEBUG(r, "vhost VQ %u ring restore failed: %d", idx, r); + } + } + + if (r < 0 || force) { /* Connection to the backend is broken, so let's sync internal * last avail idx to the device used idx. */ @@ -1414,6 +1406,14 @@ int vhost_virtqueue_stop(struct vhost_dev *dev, return r; } +int vhost_virtqueue_stop(struct vhost_dev *dev, + struct VirtIODevice *vdev, + struct vhost_virtqueue *vq, + unsigned idx) +{ + return do_vhost_virtqueue_stop(dev, vdev, vq, idx, false); +} + static int vhost_virtqueue_set_busyloop_timeout(struct vhost_dev *dev, int n, uint32_t timeout) { @@ -1619,15 +1619,11 @@ int vhost_dev_init(struct vhost_dev *hdev, void *opaque, QLIST_INSERT_HEAD(&vhost_devices, hdev, entry); /* - * The listener we registered properly updated the corresponding counter. - * So we can trust that these values are accurate. + * The listener we registered properly setup the number of required + * memslots in vhost_commit(). */ - if (hdev->vhost_ops->vhost_backend_no_private_memslots && - hdev->vhost_ops->vhost_backend_no_private_memslots(hdev)) { - used = used_shared_memslots; - } else { - used = used_memslots; - } + used = hdev->mem->nregions; + /* * We assume that all reserved memslots actually require a real memslot * in our vhost backend. This might not be true, for example, if the @@ -2136,7 +2132,8 @@ fail_features: } /* Host notifiers must be enabled at this point. */ -int vhost_dev_stop(struct vhost_dev *hdev, VirtIODevice *vdev, bool vrings) +static int do_vhost_dev_stop(struct vhost_dev *hdev, VirtIODevice *vdev, + bool vrings, bool force) { int i; int rc = 0; @@ -2158,10 +2155,11 @@ int vhost_dev_stop(struct vhost_dev *hdev, VirtIODevice *vdev, bool vrings) vhost_dev_set_vring_enable(hdev, false); } for (i = 0; i < hdev->nvqs; ++i) { - rc |= vhost_virtqueue_stop(hdev, - vdev, - hdev->vqs + i, - hdev->vq_index + i); + rc |= do_vhost_virtqueue_stop(hdev, + vdev, + hdev->vqs + i, + hdev->vq_index + i, + force); } if (hdev->vhost_ops->vhost_reset_status) { hdev->vhost_ops->vhost_reset_status(hdev); @@ -2181,6 +2179,17 @@ int vhost_dev_stop(struct vhost_dev *hdev, VirtIODevice *vdev, bool vrings) return rc; } +int vhost_dev_stop(struct vhost_dev *hdev, VirtIODevice *vdev, bool vrings) +{ + return do_vhost_dev_stop(hdev, vdev, vrings, false); +} + +int vhost_dev_force_stop(struct vhost_dev *hdev, VirtIODevice *vdev, + bool vrings) +{ + return do_vhost_dev_stop(hdev, vdev, vrings, true); +} + int vhost_net_set_backend(struct vhost_dev *hdev, struct vhost_vring_file *file) { diff --git a/hw/virtio/virtio-config-io.c b/hw/virtio/virtio-config-io.c index ad78e0b..f58d90b 100644 --- a/hw/virtio/virtio-config-io.c +++ b/hw/virtio/virtio-config-io.c @@ -11,7 +11,6 @@ #include "qemu/osdep.h" #include "hw/virtio/virtio.h" -#include "cpu.h" uint32_t virtio_config_readb(VirtIODevice *vdev, uint32_t addr) { diff --git a/hw/virtio/virtio-pci.c b/hw/virtio/virtio-pci.c index fba2372..767216d 100644 --- a/hw/virtio/virtio-pci.c +++ b/hw/virtio/virtio-pci.c @@ -30,6 +30,7 @@ #include "qemu/error-report.h" #include "qemu/log.h" #include "qemu/module.h" +#include "qemu/bswap.h" #include "hw/pci/msi.h" #include "hw/pci/msix.h" #include "hw/loader.h" diff --git a/hw/virtio/virtio.c b/hw/virtio/virtio.c index 82a285a..2ab1d20 100644 --- a/hw/virtio/virtio.c +++ b/hw/virtio/virtio.c @@ -20,7 +20,7 @@ #include "qemu/log.h" #include "qemu/main-loop.h" #include "qemu/module.h" -#include "exec/tswap.h" +#include "qemu/target-info.h" #include "qom/object_interfaces.h" #include "hw/core/cpu.h" #include "hw/virtio/virtio.h" @@ -3270,13 +3270,6 @@ virtio_load(VirtIODevice *vdev, QEMUFile *f, int version_id) config_len--; } - if (vdc->pre_load_queues) { - ret = vdc->pre_load_queues(vdev); - if (ret) { - return ret; - } - } - num = qemu_get_be32(f); if (num > VIRTIO_QUEUE_MAX) { @@ -3284,6 +3277,13 @@ virtio_load(VirtIODevice *vdev, QEMUFile *f, int version_id) return -1; } + if (vdc->pre_load_queues) { + ret = vdc->pre_load_queues(vdev, num); + if (ret) { + return ret; + } + } + for (i = 0; i < num; i++) { vdev->vq[i].vring.num = qemu_get_be32(f); if (k->has_variable_vring_alignment) { diff --git a/hw/vmapple/virtio-blk.c b/hw/vmapple/virtio-blk.c index 532b564..9de9aaa 100644 --- a/hw/vmapple/virtio-blk.c +++ b/hw/vmapple/virtio-blk.c @@ -19,7 +19,6 @@ #include "hw/vmapple/vmapple.h" #include "hw/virtio/virtio-blk.h" #include "hw/virtio/virtio-pci.h" -#include "qemu/bswap.h" #include "qemu/log.h" #include "qemu/module.h" #include "qapi/error.h" diff --git a/include/exec/memattrs.h b/include/exec/memattrs.h index 8db1d30..52ee955 100644 --- a/include/exec/memattrs.h +++ b/include/exec/memattrs.h @@ -54,6 +54,9 @@ typedef struct MemTxAttrs { */ unsigned int pid:8; + /* PCI - IOMMU operations, see PCIAddressType */ + unsigned int address_type:1; + /* * Bus masters which don't specify any attributes will get this * (via the MEMTXATTRS_UNSPECIFIED constant), so that we can diff --git a/include/exec/tswap.h b/include/exec/tswap.h index 49511f2..72219e2 100644 --- a/include/exec/tswap.h +++ b/include/exec/tswap.h @@ -9,18 +9,7 @@ #define TSWAP_H #include "qemu/bswap.h" - -/** - * target_big_endian: - * Returns true if the (default) endianness of the target is big endian, - * false otherwise. Common code should normally never need to know about the - * endianness of the target, so please do *not* use this function unless you - * know very well what you are doing! - */ -bool target_big_endian(void); -#ifdef COMPILING_PER_TARGET -#define target_big_endian() TARGET_BIG_ENDIAN -#endif +#include "qemu/target-info.h" /* * If we're in target-specific code, we can hard-code the swapping @@ -80,74 +69,4 @@ static inline void tswap64s(uint64_t *s) } } -/* Return ld{word}_{le,be}_p following target endianness. */ -#define LOAD_IMPL(word, args...) \ -do { \ - if (target_big_endian()) { \ - return glue(glue(ld, word), _be_p)(args); \ - } else { \ - return glue(glue(ld, word), _le_p)(args); \ - } \ -} while (0) - -static inline int lduw_p(const void *ptr) -{ - LOAD_IMPL(uw, ptr); -} - -static inline int ldsw_p(const void *ptr) -{ - LOAD_IMPL(sw, ptr); -} - -static inline int ldl_p(const void *ptr) -{ - LOAD_IMPL(l, ptr); -} - -static inline uint64_t ldq_p(const void *ptr) -{ - LOAD_IMPL(q, ptr); -} - -static inline uint64_t ldn_p(const void *ptr, int sz) -{ - LOAD_IMPL(n, ptr, sz); -} - -#undef LOAD_IMPL - -/* Call st{word}_{le,be}_p following target endianness. */ -#define STORE_IMPL(word, args...) \ -do { \ - if (target_big_endian()) { \ - glue(glue(st, word), _be_p)(args); \ - } else { \ - glue(glue(st, word), _le_p)(args); \ - } \ -} while (0) - - -static inline void stw_p(void *ptr, uint16_t v) -{ - STORE_IMPL(w, ptr, v); -} - -static inline void stl_p(void *ptr, uint32_t v) -{ - STORE_IMPL(l, ptr, v); -} - -static inline void stq_p(void *ptr, uint64_t v) -{ - STORE_IMPL(q, ptr, v); -} - -static inline void stn_p(void *ptr, int sz, uint64_t v) -{ - STORE_IMPL(n, ptr, sz, v); -} - -#undef STORE_IMPL - #endif /* TSWAP_H */ diff --git a/include/gdbstub/helpers.h b/include/gdbstub/helpers.h index 6f7cc48..b685afa 100644 --- a/include/gdbstub/helpers.h +++ b/include/gdbstub/helpers.h @@ -16,7 +16,8 @@ #error "gdbstub helpers should only be included by target specific code" #endif -#include "exec/tswap.h" +#include "qemu/bswap.h" +#include "qemu/target-info.h" #include "cpu-param.h" /* @@ -33,40 +34,49 @@ static inline int gdb_get_reg8(GByteArray *buf, uint8_t val) static inline int gdb_get_reg16(GByteArray *buf, uint16_t val) { - uint16_t to_word = tswap16(val); - g_byte_array_append(buf, (uint8_t *) &to_word, 2); + if (target_big_endian()) { + cpu_to_be16s(&val); + } else { + cpu_to_le16s(&val); + } + g_byte_array_append(buf, (uint8_t *) &val, 2); return 2; } static inline int gdb_get_reg32(GByteArray *buf, uint32_t val) { - uint32_t to_long = tswap32(val); - g_byte_array_append(buf, (uint8_t *) &to_long, 4); + if (target_big_endian()) { + cpu_to_be32s(&val); + } else { + cpu_to_le32s(&val); + } + g_byte_array_append(buf, (uint8_t *) &val, 4); return 4; } static inline int gdb_get_reg64(GByteArray *buf, uint64_t val) { - uint64_t to_quad = tswap64(val); - g_byte_array_append(buf, (uint8_t *) &to_quad, 8); + if (target_big_endian()) { + cpu_to_be64s(&val); + } else { + cpu_to_le64s(&val); + } + g_byte_array_append(buf, (uint8_t *) &val, 8); return 8; } static inline int gdb_get_reg128(GByteArray *buf, uint64_t val_hi, uint64_t val_lo) { - uint64_t to_quad; -#if TARGET_BIG_ENDIAN - to_quad = tswap64(val_hi); - g_byte_array_append(buf, (uint8_t *) &to_quad, 8); - to_quad = tswap64(val_lo); - g_byte_array_append(buf, (uint8_t *) &to_quad, 8); -#else - to_quad = tswap64(val_lo); - g_byte_array_append(buf, (uint8_t *) &to_quad, 8); - to_quad = tswap64(val_hi); - g_byte_array_append(buf, (uint8_t *) &to_quad, 8); -#endif + uint64_t tmp[2]; + if (target_big_endian()) { + tmp[0] = cpu_to_be64(val_hi); + tmp[1] = cpu_to_be64(val_lo); + } else { + tmp[0] = cpu_to_le64(val_lo); + tmp[1] = cpu_to_le64(val_hi); + } + g_byte_array_append(buf, (uint8_t *)&tmp, 16); return 16; } diff --git a/include/hw/acpi/generic_event_device.h b/include/hw/acpi/generic_event_device.h index d2dac87..2c5b055 100644 --- a/include/hw/acpi/generic_event_device.h +++ b/include/hw/acpi/generic_event_device.h @@ -63,12 +63,13 @@ #include "hw/acpi/memory_hotplug.h" #include "hw/acpi/ghes.h" #include "hw/acpi/cpu.h" +#include "hw/acpi/pcihp.h" #include "qom/object.h" #define ACPI_POWER_BUTTON_DEVICE "PWRB" #define TYPE_ACPI_GED "acpi-ged" -OBJECT_DECLARE_SIMPLE_TYPE(AcpiGedState, ACPI_GED) +OBJECT_DECLARE_TYPE(AcpiGedState, AcpiGedClass, ACPI_GED) #define ACPI_GED_EVT_SEL_OFFSET 0x0 #define ACPI_GED_EVT_SEL_LEN 0x4 @@ -101,6 +102,7 @@ OBJECT_DECLARE_SIMPLE_TYPE(AcpiGedState, ACPI_GED) #define ACPI_GED_PWR_DOWN_EVT 0x2 #define ACPI_GED_NVDIMM_HOTPLUG_EVT 0x4 #define ACPI_GED_CPU_HOTPLUG_EVT 0x8 +#define ACPI_GED_PCI_HOTPLUG_EVT 0x10 typedef struct GEDState { MemoryRegion evt; @@ -108,18 +110,31 @@ typedef struct GEDState { uint32_t sel; } GEDState; +#define ACPI_PCIHP_REGION_NAME "pcihp container" +#define ACPI_MEMHP_REGION_NAME "memhp container" + struct AcpiGedState { SysBusDevice parent_obj; MemHotplugState memhp_state; MemoryRegion container_memhp; CPUHotplugState cpuhp_state; MemoryRegion container_cpuhp; + AcpiPciHpState pcihp_state; + MemoryRegion container_pcihp; GEDState ged_state; uint32_t ged_event_bitmap; qemu_irq irq; AcpiGhesState ghes_state; }; +typedef struct AcpiGedClass { + /* <private> */ + SysBusDeviceClass parent_class; + + /*< public >*/ + ResettablePhases parent_phases; +} AcpiGedClass; + void build_ged_aml(Aml *table, const char* name, HotplugHandler *hotplug_dev, uint32_t ged_irq, AmlRegionSpace rs, hwaddr ged_base); void acpi_dsdt_add_power_button(Aml *scope); diff --git a/include/hw/acpi/pci.h b/include/hw/acpi/pci.h index 6359d57..20b6725 100644 --- a/include/hw/acpi/pci.h +++ b/include/hw/acpi/pci.h @@ -36,11 +36,12 @@ typedef struct AcpiMcfgInfo { void build_mcfg(GArray *table_data, BIOSLinker *linker, AcpiMcfgInfo *info, const char *oem_id, const char *oem_table_id); -Aml *aml_pci_device_dsm(void); -void build_append_pci_bus_devices(Aml *parent_scope, PCIBus *bus); void build_pci_bridge_aml(AcpiDevAmlIf *adev, Aml *scope); void build_srat_generic_affinity_structures(GArray *table_data); +Aml *build_pci_host_bridge_osc_method(bool enable_native_pcie_hotplug); +Aml *build_pci_bridge_edsm(void); + #endif diff --git a/include/hw/acpi/pcihp.h b/include/hw/acpi/pcihp.h index cdc0cb8..ca6a258 100644 --- a/include/hw/acpi/pcihp.h +++ b/include/hw/acpi/pcihp.h @@ -28,11 +28,18 @@ #define HW_ACPI_PCIHP_H #include "hw/acpi/acpi.h" +#include "hw/acpi/aml-build.h" #include "hw/hotplug.h" #define ACPI_PCIHP_IO_BASE_PROP "acpi-pcihp-io-base" #define ACPI_PCIHP_IO_LEN_PROP "acpi-pcihp-io-len" +/* PCI Hot-plug registers bases. See docs/specs/acpi_pci_hotplug.rst */ +#define ACPI_PCIHP_SEJ_BASE 0x8 +#define ACPI_PCIHP_BNMR_BASE 0x10 + +#define ACPI_PCIHP_SIZE 0x0018 + typedef struct AcpiPciHpPciStatus { uint32_t up; uint32_t down; @@ -55,7 +62,7 @@ typedef struct AcpiPciHpState { bool use_acpi_root_pci_hotplug; } AcpiPciHpState; -void acpi_pcihp_init(Object *owner, AcpiPciHpState *, PCIBus *root, +void acpi_pcihp_init(Object *owner, AcpiPciHpState *, MemoryRegion *io, uint16_t io_base); bool acpi_pcihp_is_hotpluggable_bus(AcpiPciHpState *s, BusState *bus); @@ -69,6 +76,14 @@ void acpi_pcihp_device_unplug_request_cb(HotplugHandler *hotplug_dev, AcpiPciHpState *s, DeviceState *dev, Error **errp); +void build_acpi_pci_hotplug(Aml *table, AmlRegionSpace rs, uint64_t pcihp_addr); +void build_append_pci_dsm_func0_common(Aml *ctx, Aml *retvar); +void build_append_pcihp_resources(Aml *table, + uint64_t io_addr, uint64_t io_len); +bool build_append_notification_callback(Aml *parent_scope, const PCIBus *bus); + +void build_append_pci_bus_devices(Aml *parent_scope, PCIBus *bus); + /* Called on reset */ void acpi_pcihp_reset(AcpiPciHpState *s); diff --git a/include/hw/arm/virt.h b/include/hw/arm/virt.h index 4375819..365a28b 100644 --- a/include/hw/arm/virt.h +++ b/include/hw/arm/virt.h @@ -80,6 +80,7 @@ enum { VIRT_ACPI_GED, VIRT_NVDIMM_ACPI, VIRT_PVTIME, + VIRT_ACPI_PCIHP, VIRT_LOWMEMMAP_LAST, }; diff --git a/include/hw/boards.h b/include/hw/boards.h index f424b2b..f94713e 100644 --- a/include/hw/boards.h +++ b/include/hw/boards.h @@ -443,6 +443,7 @@ struct MachineState { SmpCache smp_cache; struct NVDIMMState *nvdimms_state; struct NumaState *numa_state; + bool acpi_spcr_enabled; }; /* diff --git a/include/hw/cxl/cxl.h b/include/hw/cxl/cxl.h index de66ab8..998f495 100644 --- a/include/hw/cxl/cxl.h +++ b/include/hw/cxl/cxl.h @@ -23,6 +23,7 @@ #define CXL_DEVICE_REG_BAR_IDX 2 #define CXL_WINDOW_MAX 10 +#define CXL_NUM_EXTENTS_SUPPORTED 512 typedef struct PXBCXLDev PXBCXLDev; diff --git a/include/hw/cxl/cxl_device.h b/include/hw/cxl/cxl_device.h index ed6cd50..89411c8 100644 --- a/include/hw/cxl/cxl_device.h +++ b/include/hw/cxl/cxl_device.h @@ -133,6 +133,15 @@ typedef enum { CXL_MBOX_MAX = 0x20 } CXLRetCode; +/* r3.2 Section 7.6.7.6.2: Table 7-66: DSMAS Flags Bits */ +typedef enum { + CXL_DSMAS_FLAGS_NONVOLATILE = 2, + CXL_DSMAS_FLAGS_SHARABLE = 3, + CXL_DSMAS_FLAGS_HW_MANAGED_COHERENCY = 4, + CXL_DSMAS_FLAGS_IC_SPECIFIC_DC_MANAGEMENT = 5, + CXL_DSMAS_FLAGS_RDONLY = 6, +} CXLDSMASFlags; + typedef struct CXLCCI CXLCCI; typedef struct cxl_device_state CXLDeviceState; struct cxl_cmd; @@ -530,6 +539,14 @@ typedef struct CXLDCRegion { uint32_t dsmadhandle; uint8_t flags; unsigned long *blk_bitmap; + uint64_t supported_blk_size_bitmask; + QemuMutex bitmap_lock; + /* Following bools make up dsmas flags, as defined in the CDAT */ + bool nonvolatile; + bool sharable; + bool hw_managed_coherency; + bool ic_specific_dc_management; + bool rdonly; } CXLDCRegion; typedef struct CXLSetFeatureInfo { @@ -618,6 +635,7 @@ struct CXLType3Dev { CXLDCExtentList extents; CXLDCExtentGroupList extents_pending; uint32_t total_extent_count; + uint32_t nr_extents_accepted; uint32_t ext_list_gen_seq; uint8_t num_regions; /* 0-8 regions */ @@ -696,11 +714,22 @@ CXLDCExtentGroup *cxl_insert_extent_to_extent_group(CXLDCExtentGroup *group, uint16_t shared_seq); void cxl_extent_group_list_insert_tail(CXLDCExtentGroupList *list, CXLDCExtentGroup *group); -void cxl_extent_group_list_delete_front(CXLDCExtentGroupList *list); +uint32_t cxl_extent_group_list_delete_front(CXLDCExtentGroupList *list); void ct3_set_region_block_backed(CXLType3Dev *ct3d, uint64_t dpa, uint64_t len); void ct3_clear_region_block_backed(CXLType3Dev *ct3d, uint64_t dpa, uint64_t len); bool ct3_test_region_block_backed(CXLType3Dev *ct3d, uint64_t dpa, uint64_t len); +void cxl_assign_event_header(CXLEventRecordHdr *hdr, + const QemuUUID *uuid, uint32_t flags, + uint8_t length, uint64_t timestamp); +void cxl_create_dc_event_records_for_extents(CXLType3Dev *ct3d, + CXLDCEventType type, + CXLDCExtentRaw extents[], + uint32_t ext_count); +bool cxl_extents_overlaps_dpa_range(CXLDCExtentList *list, + uint64_t dpa, uint64_t len); +bool cxl_extent_groups_overlaps_dpa_range(CXLDCExtentGroupList *list, + uint64_t dpa, uint64_t len); #endif diff --git a/include/hw/cxl/cxl_events.h b/include/hw/cxl/cxl_events.h index 38cadaa..758b075 100644 --- a/include/hw/cxl/cxl_events.h +++ b/include/hw/cxl/cxl_events.h @@ -184,4 +184,19 @@ typedef struct CXLEventDynamicCapacity { uint32_t tags_avail; } QEMU_PACKED CXLEventDynamicCapacity; +/* CXL r3.1 Table 8-50: Dynamic Capacity Event Record */ +static const QemuUUID dynamic_capacity_uuid = { + .data = UUID(0xca95afa7, 0xf183, 0x4018, 0x8c, 0x2f, + 0x95, 0x26, 0x8e, 0x10, 0x1a, 0x2a), +}; + +typedef enum CXLDCEventType { + DC_EVENT_ADD_CAPACITY = 0x0, + DC_EVENT_RELEASE_CAPACITY = 0x1, + DC_EVENT_FORCED_RELEASE_CAPACITY = 0x2, + DC_EVENT_REGION_CONFIG_UPDATED = 0x3, + DC_EVENT_ADD_CAPACITY_RSP = 0x4, + DC_EVENT_CAPACITY_RELEASED = 0x5, +} CXLDCEventType; + #endif /* CXL_EVENTS_H */ diff --git a/include/hw/cxl/cxl_mailbox.h b/include/hw/cxl/cxl_mailbox.h index 9008402..a05d7cb 100644 --- a/include/hw/cxl/cxl_mailbox.h +++ b/include/hw/cxl/cxl_mailbox.h @@ -8,6 +8,7 @@ #ifndef CXL_MAILBOX_H #define CXL_MAILBOX_H +#define CXL_MBOX_CONFIG_CHANGE_COLD_RESET (1) #define CXL_MBOX_IMMEDIATE_CONFIG_CHANGE (1 << 1) #define CXL_MBOX_IMMEDIATE_DATA_CHANGE (1 << 2) #define CXL_MBOX_IMMEDIATE_POLICY_CHANGE (1 << 3) @@ -15,5 +16,10 @@ #define CXL_MBOX_SECURITY_STATE_CHANGE (1 << 5) #define CXL_MBOX_BACKGROUND_OPERATION (1 << 6) #define CXL_MBOX_BACKGROUND_OPERATION_ABORT (1 << 7) +#define CXL_MBOX_SECONDARY_MBOX_SUPPORTED (1 << 8) +#define CXL_MBOX_REQUEST_ABORT_BACKGROUND_OP_SUPPORTED (1 << 9) +#define CXL_MBOX_CEL_10_TO_11_VALID (1 << 10) +#define CXL_MBOX_CONFIG_CHANGE_CONV_RESET (1 << 11) +#define CXL_MBOX_CONFIG_CHANGE_CXL_RESET (1 << 12) #endif diff --git a/include/hw/i386/x86.h b/include/hw/i386/x86.h index fc460b8..8755cad 100644 --- a/include/hw/i386/x86.h +++ b/include/hw/i386/x86.h @@ -25,6 +25,7 @@ #include "hw/intc/ioapic.h" #include "hw/isa/isa.h" #include "qom/object.h" +#include "system/igvm-cfg.h" struct X86MachineClass { MachineClass parent; @@ -92,6 +93,8 @@ struct X86MachineState { * which means no limitation on the guest's bus locks. */ uint64_t bus_lock_ratelimit; + + IgvmCfg *igvm; }; #define X86_MACHINE_SMM "smm" diff --git a/include/hw/pci-host/gpex.h b/include/hw/pci-host/gpex.h index 8447153..feaf827 100644 --- a/include/hw/pci-host/gpex.h +++ b/include/hw/pci-host/gpex.h @@ -45,6 +45,7 @@ struct GPEXConfig { MemMapEntry pio; int irq; PCIBus *bus; + bool pci_native_hotplug; }; typedef struct GPEXIrq GPEXIrq; diff --git a/include/hw/pci/pci.h b/include/hw/pci/pci.h index df3cc7b..6b7d3ac 100644 --- a/include/hw/pci/pci.h +++ b/include/hw/pci/pci.h @@ -134,6 +134,15 @@ struct PCIHostDeviceAddress { unsigned int function; }; +/* + * Represents the Address Type (AT) field in a PCI request, + * see MemTxAttrs.address_type + */ +typedef enum PCIAddressType { + PCI_AT_UNTRANSLATED = 0, /* Default when no attribute is set */ + PCI_AT_TRANSLATED = 1, +} PCIAddressType; + typedef void PCIConfigWriteFunc(PCIDevice *pci_dev, uint32_t address, uint32_t data, int len); typedef uint32_t PCIConfigReadFunc(PCIDevice *pci_dev, diff --git a/include/hw/qdev-properties.h b/include/hw/qdev-properties.h index 2c99856..0197aa4 100644 --- a/include/hw/qdev-properties.h +++ b/include/hw/qdev-properties.h @@ -43,11 +43,22 @@ struct PropertyInfo { ObjectPropertyRelease *release; }; +/** + * struct OnOffAutoBit64 - OnOffAuto storage with 64 elements. + * @on_bits: Bitmap of elements with "on". + * @auto_bits: Bitmap of elements with "auto". + */ +typedef struct OnOffAutoBit64 { + uint64_t on_bits; + uint64_t auto_bits; +} OnOffAutoBit64; + /*** qdev-properties.c ***/ extern const PropertyInfo qdev_prop_bit; extern const PropertyInfo qdev_prop_bit64; +extern const PropertyInfo qdev_prop_on_off_auto_bit64; extern const PropertyInfo qdev_prop_bool; extern const PropertyInfo qdev_prop_uint8; extern const PropertyInfo qdev_prop_uint16; @@ -100,6 +111,13 @@ extern const PropertyInfo qdev_prop_link; .set_default = true, \ .defval.u = (bool)_defval) +#define DEFINE_PROP_ON_OFF_AUTO_BIT64(_name, _state, _field, _bit, _defval) \ + DEFINE_PROP(_name, _state, _field, qdev_prop_on_off_auto_bit64, \ + OnOffAutoBit64, \ + .bitnr = (_bit), \ + .set_default = true, \ + .defval.i = (OnOffAuto)_defval) + #define DEFINE_PROP_BOOL(_name, _state, _field, _defval) \ DEFINE_PROP(_name, _state, _field, qdev_prop_bool, bool, \ .set_default = true, \ diff --git a/include/hw/scsi/esp.h b/include/hw/scsi/esp.h index 533d856..3526bad 100644 --- a/include/hw/scsi/esp.h +++ b/include/hw/scsi/esp.h @@ -14,7 +14,11 @@ typedef void (*ESPDMAMemoryReadWriteFunc)(void *opaque, uint8_t *buf, int len); #define ESP_FIFO_SZ 16 #define ESP_CMDFIFO_SZ 32 -typedef struct ESPState ESPState; +enum ESPASCMode { + ESP_ASC_MODE_DIS = 0, /* Disconnected */ + ESP_ASC_MODE_INI = 1, /* Initiator */ + ESP_ASC_MODE_TGT = 2 /* Target */ +}; #define TYPE_ESP "esp" OBJECT_DECLARE_SIMPLE_TYPE(ESPState, ESP) @@ -40,6 +44,7 @@ struct ESPState { uint8_t cmdfifo_cdb_offset; uint8_t lun; uint32_t do_cmd; + uint8_t asc_mode; bool data_ready; int dma_enabled; @@ -106,6 +111,13 @@ struct SysBusESPState { #define CMD_DMA 0x80 #define CMD_CMD 0x7f +#define CMD_GRP_MASK 0x70 + +#define CMD_GRP_MISC 0x00 +#define CMD_GRP_INIT 0x01 +#define CMD_GRP_TRGT 0x02 +#define CMD_GRP_DISC 0x04 + #define CMD_NOP 0x00 #define CMD_FLUSH 0x01 #define CMD_RESET 0x02 @@ -140,6 +152,7 @@ struct SysBusESPState { #define INTR_FC 0x08 #define INTR_BS 0x10 #define INTR_DC 0x20 +#define INTR_IL 0x40 #define INTR_RST 0x80 #define SEQ_0 0x0 diff --git a/include/hw/sysbus.h b/include/hw/sysbus.h index 7dc88aa..18fde8a 100644 --- a/include/hw/sysbus.h +++ b/include/hw/sysbus.h @@ -82,6 +82,7 @@ void sysbus_connect_irq(SysBusDevice *dev, int n, qemu_irq irq); bool sysbus_is_irq_connected(SysBusDevice *dev, int n); qemu_irq sysbus_get_connected_irq(SysBusDevice *dev, int n); void sysbus_mmio_map(SysBusDevice *dev, int n, hwaddr addr); +int sysbus_mmio_map_name(SysBusDevice *dev, const char*name, hwaddr addr); void sysbus_mmio_map_overlap(SysBusDevice *dev, int n, hwaddr addr, int priority); diff --git a/include/hw/virtio/vhost-user-blk.h b/include/hw/virtio/vhost-user-blk.h index ea085ee..a10f785 100644 --- a/include/hw/virtio/vhost-user-blk.h +++ b/include/hw/virtio/vhost-user-blk.h @@ -50,6 +50,8 @@ struct VHostUserBlk { bool connected; /* vhost_user_blk_start/vhost_user_blk_stop */ bool started_vu; + + bool skip_get_vring_base_on_force_shutdown; }; #endif diff --git a/include/hw/virtio/vhost.h b/include/hw/virtio/vhost.h index 38800a7..66be6af 100644 --- a/include/hw/virtio/vhost.h +++ b/include/hw/virtio/vhost.h @@ -1,6 +1,7 @@ #ifndef VHOST_H #define VHOST_H +#include "net/vhost_net.h" #include "hw/virtio/vhost-backend.h" #include "hw/virtio/virtio.h" #include "system/memory.h" @@ -143,6 +144,10 @@ struct vhost_net { struct vhost_dev dev; struct vhost_virtqueue vqs[2]; int backend; + const int *feature_bits; + int max_tx_queue_size; + SaveAcketFeatures *save_acked_features; + bool is_vhost_user; NetClientState *nc; }; @@ -238,6 +243,21 @@ int vhost_dev_start(struct vhost_dev *hdev, VirtIODevice *vdev, bool vrings); int vhost_dev_stop(struct vhost_dev *hdev, VirtIODevice *vdev, bool vrings); /** + * vhost_dev_force_stop() - force stop the vhost device + * @hdev: common vhost_dev structure + * @vdev: the VirtIODevice structure + * @vrings: true to have vrings disabled in this call + * + * Force stop the vhost device. After the device is stopped the notifiers + * can be disabled (@vhost_dev_disable_notifiers) and the device can + * be torn down (@vhost_dev_cleanup). Unlike @vhost_dev_stop, this doesn't + * attempt to flush in-flight backend requests by skipping GET_VRING_BASE + * entirely. + */ +int vhost_dev_force_stop(struct vhost_dev *hdev, VirtIODevice *vdev, + bool vrings); + +/** * DOC: vhost device configuration handling * * The VirtIO device configuration space is used for rarely changing diff --git a/include/hw/virtio/virtio-net.h b/include/hw/virtio/virtio-net.h index b9ea9e8..73fdefc 100644 --- a/include/hw/virtio/virtio-net.h +++ b/include/hw/virtio/virtio-net.h @@ -144,7 +144,11 @@ typedef struct VirtioNetRssData { bool enabled_software_rss; bool redirect; bool populate_hash; - uint32_t hash_types; + bool peer_hash_available; + uint32_t runtime_hash_types; + uint32_t supported_hash_types; + uint32_t peer_hash_types; + OnOffAutoBit64 specified_hash_types; uint8_t key[VIRTIO_NET_RSS_MAX_KEY_SIZE]; uint16_t indirections_len; uint16_t *indirections_table; diff --git a/include/hw/virtio/virtio.h b/include/hw/virtio/virtio.h index 214d4a7..c594764 100644 --- a/include/hw/virtio/virtio.h +++ b/include/hw/virtio/virtio.h @@ -210,8 +210,14 @@ struct VirtioDeviceClass { void (*guest_notifier_mask)(VirtIODevice *vdev, int n, bool mask); int (*start_ioeventfd)(VirtIODevice *vdev); void (*stop_ioeventfd)(VirtIODevice *vdev); - /* Called before loading queues. Useful to add queues before loading. */ - int (*pre_load_queues)(VirtIODevice *vdev); + /* + * Called before loading queues. + * If the number of queues change at runtime, use @n to know the + * number and add or remove queues accordingly. + * Note that this function is called in the middle of loading vmsd; + * no assumption should be made on states being loaded from vmsd. + */ + int (*pre_load_queues)(VirtIODevice *vdev, uint32_t n); /* Saving and loading of a device; trying to deprecate save/load * use vmsd for new devices. */ diff --git a/include/net/net.h b/include/net/net.h index cdd5b10..84ee18e 100644 --- a/include/net/net.h +++ b/include/net/net.h @@ -60,6 +60,7 @@ typedef bool (HasVnetHdrLen)(NetClientState *, int); typedef void (SetOffload)(NetClientState *, int, int, int, int, int, int, int); typedef int (GetVnetHdrLen)(NetClientState *); typedef void (SetVnetHdrLen)(NetClientState *, int); +typedef bool (GetVnetHashSupportedTypes)(NetClientState *, uint32_t *); typedef int (SetVnetLE)(NetClientState *, bool); typedef int (SetVnetBE)(NetClientState *, bool); typedef struct SocketReadState SocketReadState; @@ -67,6 +68,7 @@ typedef void (SocketReadStateFinalize)(SocketReadState *rs); typedef void (NetAnnounce)(NetClientState *); typedef bool (SetSteeringEBPF)(NetClientState *, int); typedef bool (NetCheckPeerType)(NetClientState *, ObjectClass *, Error **); +typedef struct vhost_net *(GetVHostNet)(NetClientState *nc); typedef struct NetClientInfo { NetClientDriver type; @@ -89,9 +91,11 @@ typedef struct NetClientInfo { SetVnetHdrLen *set_vnet_hdr_len; SetVnetLE *set_vnet_le; SetVnetBE *set_vnet_be; + GetVnetHashSupportedTypes *get_vnet_hash_supported_types; NetAnnounce *announce; SetSteeringEBPF *set_steering_ebpf; NetCheckPeerType *check_peer_type; + GetVHostNet *get_vhost_net; } NetClientInfo; struct NetClientState { @@ -189,6 +193,7 @@ void qemu_set_offload(NetClientState *nc, int csum, int tso4, int tso6, int ecn, int ufo, int uso4, int uso6); int qemu_get_vnet_hdr_len(NetClientState *nc); void qemu_set_vnet_hdr_len(NetClientState *nc, int len); +bool qemu_get_vnet_hash_supported_types(NetClientState *nc, uint32_t *types); int qemu_set_vnet_le(NetClientState *nc, bool is_le); int qemu_set_vnet_be(NetClientState *nc, bool is_be); void qemu_macaddr_default_if_unset(MACAddr *macaddr); @@ -298,6 +303,7 @@ void net_client_parse(QemuOptsList *opts_list, const char *optstr); void show_netdevs(void); void net_init_clients(void); void net_check_clients(void); +void net_client_set_link(NetClientState **ncs, int queues, bool up); void net_cleanup(void); void hmp_host_net_add(Monitor *mon, const QDict *qdict); void hmp_host_net_remove(Monitor *mon, const QDict *qdict); diff --git a/include/net/tap.h b/include/net/tap.h index 5d58551..6f34f13 100644 --- a/include/net/tap.h +++ b/include/net/tap.h @@ -33,7 +33,4 @@ int tap_disable(NetClientState *nc); int tap_get_fd(NetClientState *nc); -struct vhost_net; -struct vhost_net *tap_get_vhost_net(NetClientState *nc); - #endif /* QEMU_NET_TAP_H */ diff --git a/include/net/vhost-user.h b/include/net/vhost-user.h deleted file mode 100644 index 35bf619..0000000 --- a/include/net/vhost-user.h +++ /dev/null @@ -1,19 +0,0 @@ -/* - * vhost-user.h - * - * Copyright (c) 2013 Virtual Open Systems Sarl. - * - * This work is licensed under the terms of the GNU GPL, version 2 or later. - * See the COPYING file in the top-level directory. - * - */ - -#ifndef VHOST_USER_H -#define VHOST_USER_H - -struct vhost_net; -struct vhost_net *vhost_user_get_vhost_net(NetClientState *nc); -uint64_t vhost_user_get_acked_features(NetClientState *nc); -void vhost_user_save_acked_features(NetClientState *nc); - -#endif /* VHOST_USER_H */ diff --git a/include/net/vhost-vdpa.h b/include/net/vhost-vdpa.h index b81f9a6..f8d7d6c 100644 --- a/include/net/vhost-vdpa.h +++ b/include/net/vhost-vdpa.h @@ -14,8 +14,4 @@ #define TYPE_VHOST_VDPA "vhost-vdpa" -struct vhost_net *vhost_vdpa_get_vhost_net(NetClientState *nc); - -extern const int vdpa_feature_bits[]; - #endif /* VHOST_VDPA_H */ diff --git a/include/net/vhost_net.h b/include/net/vhost_net.h index c6a5361..879781d 100644 --- a/include/net/vhost_net.h +++ b/include/net/vhost_net.h @@ -7,11 +7,19 @@ struct vhost_net; typedef struct vhost_net VHostNetState; +typedef uint64_t (GetAckedFeatures)(NetClientState *nc); +typedef void (SaveAcketFeatures)(NetClientState *nc); + typedef struct VhostNetOptions { VhostBackendType backend_type; NetClientState *net_backend; uint32_t busyloop_timeout; unsigned int nvqs; + const int *feature_bits; + int max_tx_queue_size; + bool is_vhost_user; + GetAckedFeatures *get_acked_features; + SaveAcketFeatures *save_acked_features; void *opaque; } VhostNetOptions; @@ -41,7 +49,7 @@ void vhost_net_config_mask(VHostNetState *net, VirtIODevice *dev, bool mask); int vhost_net_notify_migration_done(VHostNetState *net, char* mac_addr); VHostNetState *get_vhost_net(NetClientState *nc); -int vhost_set_vring_enable(NetClientState * nc, int enable); +int vhost_net_set_vring_enable(NetClientState *nc, int enable); uint64_t vhost_net_get_acked_features(VHostNetState *net); diff --git a/include/qemu/bswap.h b/include/qemu/bswap.h index 9a11764..39ba640 100644 --- a/include/qemu/bswap.h +++ b/include/qemu/bswap.h @@ -1,6 +1,8 @@ #ifndef BSWAP_H #define BSWAP_H +#include "qemu/target-info.h" + #undef bswap16 #define bswap16(_x) __builtin_bswap16(_x) #undef bswap32 @@ -432,4 +434,75 @@ DO_STN_LDN_P(be) #undef le_bswaps #undef be_bswaps + +/* Return ld{word}_{le,be}_p following target endianness. */ +#define LOAD_IMPL(word, args...) \ +do { \ + if (target_big_endian()) { \ + return glue(glue(ld, word), _be_p)(args); \ + } else { \ + return glue(glue(ld, word), _le_p)(args); \ + } \ +} while (0) + +static inline int lduw_p(const void *ptr) +{ + LOAD_IMPL(uw, ptr); +} + +static inline int ldsw_p(const void *ptr) +{ + LOAD_IMPL(sw, ptr); +} + +static inline int ldl_p(const void *ptr) +{ + LOAD_IMPL(l, ptr); +} + +static inline uint64_t ldq_p(const void *ptr) +{ + LOAD_IMPL(q, ptr); +} + +static inline uint64_t ldn_p(const void *ptr, int sz) +{ + LOAD_IMPL(n, ptr, sz); +} + +#undef LOAD_IMPL + +/* Call st{word}_{le,be}_p following target endianness. */ +#define STORE_IMPL(word, args...) \ +do { \ + if (target_big_endian()) { \ + glue(glue(st, word), _be_p)(args); \ + } else { \ + glue(glue(st, word), _le_p)(args); \ + } \ +} while (0) + + +static inline void stw_p(void *ptr, uint16_t v) +{ + STORE_IMPL(w, ptr, v); +} + +static inline void stl_p(void *ptr, uint32_t v) +{ + STORE_IMPL(l, ptr, v); +} + +static inline void stq_p(void *ptr, uint64_t v) +{ + STORE_IMPL(q, ptr, v); +} + +static inline void stn_p(void *ptr, int sz, uint64_t v) +{ + STORE_IMPL(n, ptr, sz, v); +} + +#undef STORE_IMPL + #endif /* BSWAP_H */ diff --git a/include/qemu/log.h b/include/qemu/log.h index 60da703..aae7298 100644 --- a/include/qemu/log.h +++ b/include/qemu/log.h @@ -84,6 +84,8 @@ typedef struct QEMULogItem { extern const QEMULogItem qemu_log_items[]; +ssize_t rust_fwrite(const void *ptr, size_t size, size_t nmemb, FILE *stream); + bool qemu_set_log(int log_flags, Error **errp); bool qemu_set_log_filename(const char *filename, Error **errp); bool qemu_set_log_filename_flags(const char *name, int flags, Error **errp); diff --git a/include/qemu/target-info-impl.h b/include/qemu/target-info-impl.h index 1b51cbc..17887f6 100644 --- a/include/qemu/target-info-impl.h +++ b/include/qemu/target-info-impl.h @@ -9,17 +9,21 @@ #ifndef QEMU_TARGET_INFO_IMPL_H #define QEMU_TARGET_INFO_IMPL_H -#include "qemu/target-info.h" +#include "qapi/qapi-types-machine.h" typedef struct TargetInfo { /* runtime equivalent of TARGET_NAME definition */ const char *target_name; + /* related to TARGET_ARCH definition */ + SysEmuTarget target_arch; /* runtime equivalent of TARGET_LONG_BITS definition */ unsigned long_bits; /* runtime equivalent of CPU_RESOLVING_TYPE definition */ const char *cpu_type; /* QOM typename machines for this binary must implement */ const char *machine_typename; + /* related to TARGET_BIG_ENDIAN definition */ + EndianMode endianness; } TargetInfo; /** diff --git a/include/qemu/target-info-qapi.h b/include/qemu/target-info-qapi.h new file mode 100644 index 0000000..d5ce052 --- /dev/null +++ b/include/qemu/target-info-qapi.h @@ -0,0 +1,29 @@ +/* + * QEMU target info API (returning QAPI types) + * + * Copyright (c) Linaro + * + * SPDX-License-Identifier: GPL-2.0-or-later + */ + +#ifndef QEMU_TARGET_INFO_EXTRA_H +#define QEMU_TARGET_INFO_EXTRA_H + +#include "qapi/qapi-types-common.h" +#include "qapi/qapi-types-machine.h" + +/** + * target_arch: + * + * Returns: QAPI SysEmuTarget enum (e.g. SYS_EMU_TARGET_X86_64). + */ +SysEmuTarget target_arch(void); + +/** + * target_endian_mode: + * + * Returns: QAPI EndianMode enum (e.g. ENDIAN_MODE_LITTLE). + */ +EndianMode target_endian_mode(void); + +#endif diff --git a/include/qemu/target-info.h b/include/qemu/target-info.h index 850a295..abcf25d 100644 --- a/include/qemu/target-info.h +++ b/include/qemu/target-info.h @@ -1,5 +1,5 @@ /* - * QEMU target info API + * QEMU target info API (returning native types) * * Copyright (c) Linaro * @@ -38,4 +38,16 @@ const char *target_machine_typename(void); */ const char *target_cpu_type(void); +/** + * target_big_endian: + * + * Returns: %true if the (default) endianness of the target is big endian, + * %false otherwise. + * + * Common code should normally never need to know about the endianness of + * the target, so please do *not* use this function unless you know very + * well what you are doing! + */ +bool target_big_endian(void); + #endif diff --git a/include/system/confidential-guest-support.h b/include/system/confidential-guest-support.h index ea46b50..0cc8b26 100644 --- a/include/system/confidential-guest-support.h +++ b/include/system/confidential-guest-support.h @@ -19,6 +19,7 @@ #define QEMU_CONFIDENTIAL_GUEST_SUPPORT_H #include "qom/object.h" +#include "exec/hwaddr.h" #define TYPE_CONFIDENTIAL_GUEST_SUPPORT "confidential-guest-support" OBJECT_DECLARE_TYPE(ConfidentialGuestSupport, @@ -26,6 +27,40 @@ OBJECT_DECLARE_TYPE(ConfidentialGuestSupport, CONFIDENTIAL_GUEST_SUPPORT) +typedef enum ConfidentialGuestPlatformType { + CGS_PLATFORM_SEV, + CGS_PLATFORM_SEV_ES, + CGS_PLATFORM_SEV_SNP, +} ConfidentialGuestPlatformType; + +typedef enum ConfidentialGuestMemoryType { + CGS_MEM_RAM, + CGS_MEM_RESERVED, + CGS_MEM_ACPI, + CGS_MEM_NVS, + CGS_MEM_UNUSABLE, +} ConfidentialGuestMemoryType; + +typedef struct ConfidentialGuestMemoryMapEntry { + uint64_t gpa; + uint64_t size; + ConfidentialGuestMemoryType type; +} ConfidentialGuestMemoryMapEntry; + +typedef enum ConfidentialGuestPageType { + CGS_PAGE_TYPE_NORMAL, + CGS_PAGE_TYPE_VMSA, + CGS_PAGE_TYPE_ZERO, + CGS_PAGE_TYPE_UNMEASURED, + CGS_PAGE_TYPE_SECRETS, + CGS_PAGE_TYPE_CPUID, + CGS_PAGE_TYPE_REQUIRED_MEMORY, +} ConfidentialGuestPageType; + +typedef enum ConfidentialGuestPolicyType { + GUEST_POLICY_SEV, +} ConfidentialGuestPolicyType; + struct ConfidentialGuestSupport { Object parent; @@ -64,6 +99,59 @@ typedef struct ConfidentialGuestSupportClass { int (*kvm_init)(ConfidentialGuestSupport *cgs, Error **errp); int (*kvm_reset)(ConfidentialGuestSupport *cgs, Error **errp); + + /* + * Check to see if this confidential guest supports a particular + * platform or configuration. + * + * Return true if supported or false if not supported. + */ + bool (*check_support)(ConfidentialGuestPlatformType platform, + uint16_t platform_version, uint8_t highest_vtl, + uint64_t shared_gpa_boundary); + + /* + * Configure part of the state of a guest for a particular set of data, page + * type and gpa. This can be used for example to pre-populate and measure + * guest memory contents, define private ranges or set the initial CPU state + * for one or more CPUs. + * + * If memory_type is CGS_PAGE_TYPE_VMSA then ptr points to the initial CPU + * context for a virtual CPU. The format of the data depends on the type of + * confidential virtual machine. For example, for SEV-ES ptr will point to a + * vmcb_save_area structure that should be copied into guest memory at the + * address specified in gpa. The cpu_index parameter contains the index of + * the CPU the VMSA applies to. + */ + int (*set_guest_state)(hwaddr gpa, uint8_t *ptr, uint64_t len, + ConfidentialGuestPageType memory_type, + uint16_t cpu_index, Error **errp); + + /* + * Set the guest policy. The policy can be used to configure the + * confidential platform, such as if debug is enabled or not and can contain + * information about expected launch measurements, signed verification of + * guest configuration and other platform data. + * + * The format of the policy data is specific to each platform. For example, + * SEV-SNP uses a policy bitfield in the 'policy' argument and provides an + * ID block and ID authentication in the 'policy_data' parameters. The type + * of policy data is identified by the 'policy_type' argument. + */ + int (*set_guest_policy)(ConfidentialGuestPolicyType policy_type, + uint64_t policy, + void *policy_data1, uint32_t policy_data1_size, + void *policy_data2, uint32_t policy_data2_size, + Error **errp); + + /* + * Iterate the system memory map, getting the entry with the given index + * that can be populated into guest memory. + * + * Returns 0 for ok, 1 if the index is out of range and -1 on error. + */ + int (*get_mem_map_entry)(int index, ConfidentialGuestMemoryMapEntry *entry, + Error **errp); } ConfidentialGuestSupportClass; static inline int confidential_guest_kvm_init(ConfidentialGuestSupport *cgs, diff --git a/include/system/igvm-cfg.h b/include/system/igvm-cfg.h new file mode 100644 index 0000000..944f23a --- /dev/null +++ b/include/system/igvm-cfg.h @@ -0,0 +1,49 @@ +/* + * QEMU IGVM interface + * + * Copyright (C) 2024 SUSE + * + * Authors: + * Roy Hopkins <roy.hopkins@randomman.co.uk> + * + * SPDX-License-Identifier: GPL-2.0-or-later + */ + +#ifndef QEMU_IGVM_CFG_H +#define QEMU_IGVM_CFG_H + +#include "qom/object.h" + +typedef struct IgvmCfg { + ObjectClass parent_class; + + /* + * filename: Filename that specifies a file that contains the configuration + * of the guest in Independent Guest Virtual Machine (IGVM) + * format. + */ + char *filename; +} IgvmCfg; + +typedef struct IgvmCfgClass { + ObjectClass parent_class; + + /* + * If an IGVM filename has been specified then process the IGVM file. + * Performs a no-op if no filename has been specified. + * If onlyVpContext is true then only the IGVM_VHT_VP_CONTEXT entries + * in the IGVM file will be processed, allowing information about the + * CPU state to be determined before processing the entire file. + * + * Returns 0 for ok and -1 on error. + */ + int (*process)(IgvmCfg *cfg, ConfidentialGuestSupport *cgs, + bool onlyVpContext, Error **errp); + +} IgvmCfgClass; + +#define TYPE_IGVM_CFG "igvm-cfg" + +OBJECT_DECLARE_TYPE(IgvmCfg, IgvmCfgClass, IGVM_CFG) + +#endif diff --git a/include/system/memory.h b/include/system/memory.h index 46248d4..e2cd6ed 100644 --- a/include/system/memory.h +++ b/include/system/memory.h @@ -19,7 +19,6 @@ #include "exec/memattrs.h" #include "exec/memop.h" #include "exec/ramlist.h" -#include "exec/tswap.h" #include "qemu/bswap.h" #include "qemu/queue.h" #include "qemu/int128.h" @@ -109,15 +108,34 @@ struct MemoryRegionSection { typedef struct IOMMUTLBEntry IOMMUTLBEntry; -/* See address_space_translate: bit 0 is read, bit 1 is write. */ +/* + * See address_space_translate: + * - bit 0 : read + * - bit 1 : write + * - bit 2 : exec + * - bit 3 : priv + * - bit 4 : global + * - bit 5 : untranslated only + */ typedef enum { IOMMU_NONE = 0, IOMMU_RO = 1, IOMMU_WO = 2, IOMMU_RW = 3, + IOMMU_EXEC = 4, + IOMMU_PRIV = 8, + IOMMU_GLOBAL = 16, + IOMMU_UNTRANSLATED_ONLY = 32, } IOMMUAccessFlags; -#define IOMMU_ACCESS_FLAG(r, w) (((r) ? IOMMU_RO : 0) | ((w) ? IOMMU_WO : 0)) +#define IOMMU_ACCESS_FLAG(r, w) (((r) ? IOMMU_RO : 0) | \ + ((w) ? IOMMU_WO : 0)) +#define IOMMU_ACCESS_FLAG_FULL(r, w, x, p, g, uo) \ + (IOMMU_ACCESS_FLAG(r, w) | \ + ((x) ? IOMMU_EXEC : 0) | \ + ((p) ? IOMMU_PRIV : 0) | \ + ((g) ? IOMMU_GLOBAL : 0) | \ + ((uo) ? IOMMU_UNTRANSLATED_ONLY : 0)) struct IOMMUTLBEntry { AddressSpace *target_as; @@ -125,6 +143,7 @@ struct IOMMUTLBEntry { hwaddr translated_addr; hwaddr addr_mask; /* 0xfff = 4k translation */ IOMMUAccessFlags perm; + uint32_t pasid; }; /* diff --git a/include/system/runstate.h b/include/system/runstate.h index fdd5c4a..b406a39 100644 --- a/include/system/runstate.h +++ b/include/system/runstate.h @@ -107,6 +107,7 @@ void qemu_system_vmstop_request(RunState reason); void qemu_system_vmstop_request_prepare(void); bool qemu_vmstop_requested(RunState *r); ShutdownCause qemu_shutdown_requested_get(void); +bool qemu_force_shutdown_requested(void); ShutdownCause qemu_reset_requested_get(void); void qemu_system_killed(int signal, pid_t pid); void qemu_system_reset(ShutdownCause reason); diff --git a/include/user/abitypes.h b/include/user/abitypes.h index 7528124..be7a876 100644 --- a/include/user/abitypes.h +++ b/include/user/abitypes.h @@ -6,7 +6,6 @@ #endif #include "exec/cpu-defs.h" -#include "exec/tswap.h" #include "user/tswap-target.h" #ifdef TARGET_ABI32 diff --git a/linux-headers/asm-x86/kvm.h b/linux-headers/asm-x86/kvm.h index cd275ae..f0c1a73 100644 --- a/linux-headers/asm-x86/kvm.h +++ b/linux-headers/asm-x86/kvm.h @@ -963,7 +963,13 @@ struct kvm_tdx_cmd { struct kvm_tdx_capabilities { __u64 supported_attrs; __u64 supported_xfam; - __u64 reserved[254]; + + __u64 kernel_tdvmcallinfo_1_r11; + __u64 user_tdvmcallinfo_1_r11; + __u64 kernel_tdvmcallinfo_1_r12; + __u64 user_tdvmcallinfo_1_r12; + + __u64 reserved[250]; /* Configurable CPUID bits for userspace */ struct kvm_cpuid2 cpuid; diff --git a/linux-headers/linux/kvm.h b/linux-headers/linux/kvm.h index 0690743..32c5885 100644 --- a/linux-headers/linux/kvm.h +++ b/linux-headers/linux/kvm.h @@ -459,6 +459,10 @@ struct kvm_run { __u64 leaf; __u64 r11, r12, r13, r14; } get_tdvmcall_info; + struct { + __u64 ret; + __u64 vector; + } setup_event_notify; }; } tdx; /* Fix the size of the union. */ diff --git a/linux-user/meson.build b/linux-user/meson.build index f47a213..efca843 100644 --- a/linux-user/meson.build +++ b/linux-user/meson.build @@ -27,7 +27,10 @@ linux_user_ss.add(libdw) linux_user_ss.add(when: 'TARGET_HAS_BFLT', if_true: files('flatload.c')) linux_user_ss.add(when: 'TARGET_I386', if_true: files('vm86.c')) linux_user_ss.add(when: 'CONFIG_ARM_COMPATIBLE_SEMIHOSTING', if_true: files('semihost.c')) -linux_user_ss.add(when: 'CONFIG_TCG_PLUGINS', if_true: files('plugin-api.c')) + +if get_option('plugins') + linux_user_ss.add(files('plugin-api.c')) +endif syscall_nr_generators = {} diff --git a/linux-user/plugin-api.c b/linux-user/plugin-api.c index 66755df..8d6fbb6 100644 --- a/linux-user/plugin-api.c +++ b/linux-user/plugin-api.c @@ -12,4 +12,5 @@ #include "qemu/osdep.h" #include "qemu.h" +#include "loader.h" #include "common-user/plugin-api.c.inc" diff --git a/meson.build b/meson.build index b5f74aa..c2bc3ee 100644 --- a/meson.build +++ b/meson.build @@ -1285,6 +1285,10 @@ if not get_option('slirp').auto() or have_system endif endif +enable_passt = get_option('passt') \ + .require(host_os == 'linux', error_message: 'passt is supported only on Linux') \ + .allowed() + vde = not_found if not get_option('vde').auto() or have_system or have_tools vde = cc.find_library('vdeplug', has_headers: ['libvdeplug.h'], @@ -1424,6 +1428,12 @@ if host_os == 'linux' and (have_system or have_tools) method: 'pkg-config', required: get_option('libudev')) endif +igvm = not_found +if not get_option('igvm').auto() or have_system + igvm = dependency('igvm', version: '>= 0.3.0', + method: 'pkg-config', + required: get_option('igvm')) +endif mpathlibs = [libudev] mpathpersist = not_found @@ -2538,6 +2548,7 @@ if seccomp.found() config_host_data.set('CONFIG_SECCOMP_SYSRAWRC', seccomp_has_sysrawrc) endif config_host_data.set('CONFIG_PIXMAN', pixman.found()) +config_host_data.set('CONFIG_PASST', enable_passt) config_host_data.set('CONFIG_SLIRP', slirp.found()) config_host_data.set('CONFIG_SNAPPY', snappy.found()) config_host_data.set('CONFIG_SOLARIS', host_os == 'sunos') @@ -2601,6 +2612,7 @@ config_host_data.set('CONFIG_CFI', get_option('cfi')) config_host_data.set('CONFIG_SELINUX', selinux.found()) config_host_data.set('CONFIG_XEN_BACKEND', xen.found()) config_host_data.set('CONFIG_LIBDW', libdw.found()) +config_host_data.set('CONFIG_IGVM', igvm.found()) if xen.found() # protect from xen.version() having less than three components xen_version = xen.version().split('.') + ['0', '0'] @@ -4926,6 +4938,7 @@ if host_os == 'darwin' summary_info += {'vmnet.framework support': vmnet} endif summary_info += {'AF_XDP support': libxdp} +summary_info += {'passt support': enable_passt} summary_info += {'slirp support': slirp} summary_info += {'vde support': vde} summary_info += {'netmap support': have_netmap} @@ -4965,6 +4978,7 @@ summary_info += {'seccomp support': seccomp} summary_info += {'GlusterFS support': glusterfs} summary_info += {'hv-balloon support': hv_balloon} summary_info += {'TPM support': have_tpm} +summary_info += {'IGVM support': igvm} summary_info += {'libssh support': libssh} summary_info += {'lzo support': lzo} summary_info += {'snappy support': snappy} diff --git a/meson_options.txt b/meson_options.txt index a442be2..fff1521 100644 --- a/meson_options.txt +++ b/meson_options.txt @@ -117,6 +117,8 @@ option('tpm', type : 'feature', value : 'auto', description: 'TPM support') option('valgrind', type : 'feature', value: 'auto', description: 'valgrind debug support for coroutine stacks') +option('igvm', type: 'feature', value: 'auto', + description: 'Independent Guest Virtual Machine (IGVM) file support') # Do not enable it by default even for Mingw32, because it doesn't # work on Wine. @@ -234,6 +236,8 @@ option('pixman', type : 'feature', value : 'auto', description: 'pixman support') option('slirp', type: 'feature', value: 'auto', description: 'libslirp user mode network backend support') +option('passt', type: 'feature', value: 'auto', + description: 'passt network backend support') option('vde', type : 'feature', value : 'auto', description: 'vde network backend support') option('vmnet', type : 'feature', value : 'auto', diff --git a/net/af-xdp.c b/net/af-xdp.c index 01c5fb9..14f302e 100644 --- a/net/af-xdp.c +++ b/net/af-xdp.c @@ -49,9 +49,12 @@ typedef struct AFXDPState { char *buffer; struct xsk_umem *umem; - uint32_t n_queues; uint32_t xdp_flags; bool inhibit; + + char *map_path; + int map_fd; + uint32_t map_start_index; } AFXDPState; #define AF_XDP_BATCH_SIZE 64 @@ -261,6 +264,7 @@ static void af_xdp_send(void *opaque) static void af_xdp_cleanup(NetClientState *nc) { AFXDPState *s = DO_UPCAST(AFXDPState, nc, nc); + int idx; qemu_purge_queued_packets(nc); @@ -275,13 +279,17 @@ static void af_xdp_cleanup(NetClientState *nc) qemu_vfree(s->buffer); s->buffer = NULL; - /* Remove the program if it's the last open queue. */ - if (!s->inhibit && nc->queue_index == s->n_queues - 1 && s->xdp_flags - && bpf_xdp_detach(s->ifindex, s->xdp_flags, NULL) != 0) { - fprintf(stderr, - "af-xdp: unable to remove XDP program from '%s', ifindex: %d\n", - s->ifname, s->ifindex); + if (s->map_fd >= 0) { + idx = nc->queue_index + s->map_start_index; + if (bpf_map_delete_elem(s->map_fd, &idx)) { + fprintf(stderr, "af-xdp: unable to remove AF_XDP socket from map" + " %s\n", s->map_path); + } + close(s->map_fd); + s->map_fd = -1; } + g_free(s->map_path); + s->map_path = NULL; } static int af_xdp_umem_create(AFXDPState *s, int sock_fd, Error **errp) @@ -323,7 +331,7 @@ static int af_xdp_umem_create(AFXDPState *s, int sock_fd, Error **errp) s->pool = g_new(uint64_t, n_descs); /* Fill the pool in the opposite order, because it's a LIFO queue. */ - for (i = n_descs; i >= 0; i--) { + for (i = n_descs - 1; i >= 0; i--) { s->pool[i] = i * XSK_UMEM__DEFAULT_FRAME_SIZE; } s->n_pool = n_descs; @@ -345,7 +353,6 @@ static int af_xdp_socket_create(AFXDPState *s, }; int queue_id, error = 0; - s->inhibit = opts->has_inhibit && opts->inhibit; if (s->inhibit) { cfg.libxdp_flags |= XSK_LIBXDP_FLAGS__INHIBIT_PROG_LOAD; } @@ -396,6 +403,35 @@ static int af_xdp_socket_create(AFXDPState *s, return 0; } +static int af_xdp_update_xsk_map(AFXDPState *s, Error **errp) +{ + int xsk_fd, idx, error = 0; + + if (!s->map_path) { + return 0; + } + + s->map_fd = bpf_obj_get(s->map_path); + if (s->map_fd < 0) { + error = errno; + } else { + xsk_fd = xsk_socket__fd(s->xsk); + idx = s->nc.queue_index + s->map_start_index; + if (bpf_map_update_elem(s->map_fd, &idx, &xsk_fd, 0)) { + error = errno; + } + } + + if (error) { + error_setg_errno(errp, error, + "failed to insert AF_XDP socket into map %s", + s->map_path); + return -1; + } + + return 0; +} + /* NetClientInfo methods. */ static NetClientInfo net_af_xdp_info = { .type = NET_CLIENT_DRIVER_AF_XDP, @@ -444,12 +480,14 @@ int net_init_af_xdp(const Netdev *netdev, { const NetdevAFXDPOptions *opts = &netdev->u.af_xdp; NetClientState *nc, *nc0 = NULL; + int32_t map_start_index; unsigned int ifindex; uint32_t prog_id = 0; g_autofree int *sock_fds = NULL; int64_t i, queues; Error *err = NULL; AFXDPState *s; + bool inhibit; ifindex = if_nametoindex(opts->ifname); if (!ifindex) { @@ -465,8 +503,28 @@ int net_init_af_xdp(const Netdev *netdev, return -1; } - if ((opts->has_inhibit && opts->inhibit) != !!opts->sock_fds) { - error_setg(errp, "'inhibit=on' requires 'sock-fds' and vice versa"); + inhibit = opts->has_inhibit && opts->inhibit; + if (inhibit && !opts->sock_fds && !opts->map_path) { + error_setg(errp, "'inhibit=on' requires 'sock-fds' or 'map-path'"); + return -1; + } + if (!inhibit && (opts->sock_fds || opts->map_path)) { + error_setg(errp, "'sock-fds' and 'map-path' require 'inhibit=on'"); + return -1; + } + if (opts->sock_fds && opts->map_path) { + error_setg(errp, "'sock-fds' and 'map-path' are mutually exclusive"); + return -1; + } + if (!opts->map_path && opts->has_map_start_index) { + error_setg(errp, "'map-start-index' requires 'map-path'"); + return -1; + } + + map_start_index = opts->has_map_start_index ? opts->map_start_index : 0; + if (map_start_index < 0) { + error_setg(errp, "'map-start-index' cannot be negative (%d)", + map_start_index); return -1; } @@ -490,21 +548,23 @@ int net_init_af_xdp(const Netdev *netdev, pstrcpy(s->ifname, sizeof(s->ifname), opts->ifname); s->ifindex = ifindex; - s->n_queues = queues; + s->inhibit = inhibit; + + s->map_path = g_strdup(opts->map_path); + s->map_start_index = map_start_index; + s->map_fd = -1; - if (af_xdp_umem_create(s, sock_fds ? sock_fds[i] : -1, errp) - || af_xdp_socket_create(s, opts, errp)) { - /* Make sure the XDP program will be removed. */ - s->n_queues = i; - error_propagate(errp, err); + if (af_xdp_umem_create(s, sock_fds ? sock_fds[i] : -1, &err) || + af_xdp_socket_create(s, opts, &err) || + af_xdp_update_xsk_map(s, &err)) { goto err; } } - if (nc0) { + if (nc0 && !inhibit) { s = DO_UPCAST(AFXDPState, nc, nc0); if (bpf_xdp_query_id(s->ifindex, s->xdp_flags, &prog_id) || !prog_id) { - error_setg_errno(errp, errno, + error_setg_errno(&err, errno, "no XDP program loaded on '%s', ifindex: %d", s->ifname, s->ifindex); goto err; @@ -518,6 +578,7 @@ int net_init_af_xdp(const Netdev *netdev, err: if (nc0) { qemu_del_net_client(nc0); + error_propagate(errp, err); } return -1; diff --git a/net/clients.h b/net/clients.h index be53794..e786ab4 100644 --- a/net/clients.h +++ b/net/clients.h @@ -29,6 +29,10 @@ int net_init_dump(const Netdev *netdev, const char *name, NetClientState *peer, Error **errp); +#ifdef CONFIG_PASST +int net_init_passt(const Netdev *netdev, const char *name, + NetClientState *peer, Error **errp); +#endif #ifdef CONFIG_SLIRP int net_init_slirp(const Netdev *netdev, const char *name, NetClientState *peer, Error **errp); @@ -285,6 +285,9 @@ void net_hub_check_clients(void) case NET_CLIENT_DRIVER_NIC: has_nic = 1; break; +#ifdef CONFIG_PASST + case NET_CLIENT_DRIVER_PASST: +#endif case NET_CLIENT_DRIVER_USER: case NET_CLIENT_DRIVER_TAP: case NET_CLIENT_DRIVER_SOCKET: diff --git a/net/meson.build b/net/meson.build index bb97b4d..da6ea63 100644 --- a/net/meson.build +++ b/net/meson.build @@ -1,6 +1,7 @@ system_ss.add(files( 'announce.c', 'checksum.c', + 'dgram.c', 'dump.c', 'eth.c', 'filter-buffer.c', @@ -12,7 +13,7 @@ system_ss.add(files( 'queue.c', 'socket.c', 'stream.c', - 'dgram.c', + 'stream_data.c', 'util.c', )) @@ -33,6 +34,9 @@ system_ss.add(when: 'CONFIG_TCG', if_true: files('filter-replay.c')) if have_l2tpv3 system_ss.add(files('l2tpv3.c')) endif +if enable_passt + system_ss.add(files('passt.c')) +endif system_ss.add(when: slirp, if_true: files('slirp.c')) system_ss.add(when: vde, if_true: files('vde.c')) if have_netmap @@ -573,6 +573,15 @@ void qemu_set_vnet_hdr_len(NetClientState *nc, int len) nc->info->set_vnet_hdr_len(nc, len); } +bool qemu_get_vnet_hash_supported_types(NetClientState *nc, uint32_t *types) +{ + if (!nc || !nc->info->get_vnet_hash_supported_types) { + return false; + } + + return nc->info->get_vnet_hash_supported_types(nc, types); +} + int qemu_set_vnet_le(NetClientState *nc, bool is_le) { #if HOST_BIG_ENDIAN @@ -1248,6 +1257,9 @@ static int (* const net_client_init_fun[NET_CLIENT_DRIVER__MAX])( const char *name, NetClientState *peer, Error **errp) = { [NET_CLIENT_DRIVER_NIC] = net_init_nic, +#ifdef CONFIG_PASST + [NET_CLIENT_DRIVER_PASST] = net_init_passt, +#endif #ifdef CONFIG_SLIRP [NET_CLIENT_DRIVER_USER] = net_init_slirp, #endif @@ -1353,6 +1365,7 @@ void show_netdevs(void) "dgram", "hubport", "tap", + "passt", #ifdef CONFIG_SLIRP "user", #endif @@ -1601,21 +1614,11 @@ void colo_notify_filters_event(int event, Error **errp) } } -void qmp_set_link(const char *name, bool up, Error **errp) +void net_client_set_link(NetClientState **ncs, int queues, bool up) { - NetClientState *ncs[MAX_QUEUE_NUM]; NetClientState *nc; - int queues, i; - - queues = qemu_find_net_clients_except(name, ncs, - NET_CLIENT_DRIVER__MAX, - MAX_QUEUE_NUM); + int i; - if (queues == 0) { - error_set(errp, ERROR_CLASS_DEVICE_NOT_FOUND, - "Device '%s' not found", name); - return; - } nc = ncs[0]; for (i = 0; i < queues; i++) { @@ -1646,6 +1649,24 @@ void qmp_set_link(const char *name, bool up, Error **errp) } } +void qmp_set_link(const char *name, bool up, Error **errp) +{ + NetClientState *ncs[MAX_QUEUE_NUM]; + int queues; + + queues = qemu_find_net_clients_except(name, ncs, + NET_CLIENT_DRIVER__MAX, + MAX_QUEUE_NUM); + + if (queues == 0) { + error_set(errp, ERROR_CLASS_DEVICE_NOT_FOUND, + "Device '%s' not found", name); + return; + } + + net_client_set_link(ncs, queues, up); +} + static void net_vm_change_state_handler(void *opaque, bool running, RunState state) { diff --git a/net/passt.c b/net/passt.c new file mode 100644 index 0000000..6f616ba --- /dev/null +++ b/net/passt.c @@ -0,0 +1,753 @@ +/* + * passt network backend + * + * Copyright Red Hat + * + * SPDX-License-Identifier: GPL-2.0-or-later + */ +#include "qemu/osdep.h" +#include <glib/gstdio.h> +#include "qemu/error-report.h" +#include <gio/gio.h> +#include "net/net.h" +#include "clients.h" +#include "qapi/error.h" +#include "io/net-listener.h" +#include "chardev/char-fe.h" +#include "net/vhost_net.h" +#include "hw/virtio/vhost.h" +#include "hw/virtio/vhost-user.h" +#include "standard-headers/linux/virtio_net.h" +#include "stream_data.h" + +#ifdef CONFIG_VHOST_USER +static const int user_feature_bits[] = { + VIRTIO_F_NOTIFY_ON_EMPTY, + VIRTIO_F_NOTIFICATION_DATA, + VIRTIO_RING_F_INDIRECT_DESC, + VIRTIO_RING_F_EVENT_IDX, + + VIRTIO_F_ANY_LAYOUT, + VIRTIO_F_VERSION_1, + VIRTIO_NET_F_CSUM, + VIRTIO_NET_F_GUEST_CSUM, + VIRTIO_NET_F_GSO, + VIRTIO_NET_F_GUEST_TSO4, + VIRTIO_NET_F_GUEST_TSO6, + VIRTIO_NET_F_GUEST_ECN, + VIRTIO_NET_F_GUEST_UFO, + VIRTIO_NET_F_HOST_TSO4, + VIRTIO_NET_F_HOST_TSO6, + VIRTIO_NET_F_HOST_ECN, + VIRTIO_NET_F_HOST_UFO, + VIRTIO_NET_F_MRG_RXBUF, + VIRTIO_NET_F_MTU, + VIRTIO_F_IOMMU_PLATFORM, + VIRTIO_F_RING_PACKED, + VIRTIO_F_RING_RESET, + VIRTIO_F_IN_ORDER, + VIRTIO_NET_F_RSS, + VIRTIO_NET_F_RSC_EXT, + VIRTIO_NET_F_HASH_REPORT, + VIRTIO_NET_F_GUEST_USO4, + VIRTIO_NET_F_GUEST_USO6, + VIRTIO_NET_F_HOST_USO, + + /* This bit implies RARP isn't sent by QEMU out of band */ + VIRTIO_NET_F_GUEST_ANNOUNCE, + + VIRTIO_NET_F_MQ, + + VHOST_INVALID_FEATURE_BIT +}; +#endif + +typedef struct NetPasstState { + NetStreamData data; + GPtrArray *args; + gchar *pidfile; + pid_t pid; +#ifdef CONFIG_VHOST_USER + /* vhost user */ + VhostUserState *vhost_user; + VHostNetState *vhost_net; + CharBackend vhost_chr; + guint vhost_watch; + uint64_t acked_features; + bool started; +#endif +} NetPasstState; + +static int net_passt_stream_start(NetPasstState *s, Error **errp); + +static void net_passt_cleanup(NetClientState *nc) +{ + NetPasstState *s = DO_UPCAST(NetPasstState, data.nc, nc); + +#ifdef CONFIG_VHOST_USER + if (s->vhost_net) { + vhost_net_cleanup(s->vhost_net); + g_free(s->vhost_net); + s->vhost_net = NULL; + } + if (s->vhost_watch) { + g_source_remove(s->vhost_watch); + s->vhost_watch = 0; + } + qemu_chr_fe_deinit(&s->vhost_chr, true); + if (s->vhost_user) { + vhost_user_cleanup(s->vhost_user); + g_free(s->vhost_user); + s->vhost_user = NULL; + } +#endif + + kill(s->pid, SIGTERM); + g_remove(s->pidfile); + g_free(s->pidfile); + g_ptr_array_free(s->args, TRUE); +} + +static ssize_t net_passt_receive(NetClientState *nc, const uint8_t *buf, + size_t size) +{ + NetStreamData *d = DO_UPCAST(NetStreamData, nc, nc); + + return net_stream_data_receive(d, buf, size); +} + +static gboolean net_passt_send(QIOChannel *ioc, GIOCondition condition, + gpointer data) +{ + if (net_stream_data_send(ioc, condition, data) == G_SOURCE_REMOVE) { + NetPasstState *s = DO_UPCAST(NetPasstState, data, data); + Error *error; + + /* we need to restart passt */ + kill(s->pid, SIGTERM); + if (net_passt_stream_start(s, &error) == -1) { + error_report_err(error); + } + + return G_SOURCE_REMOVE; + } + + return G_SOURCE_CONTINUE; +} + +#ifdef CONFIG_VHOST_USER +static int passt_set_vnet_endianness(NetClientState *nc, bool enable) +{ + assert(nc->info->type == NET_CLIENT_DRIVER_PASST); + + return 0; +} + +static bool passt_has_vnet_hdr(NetClientState *nc) +{ + NetPasstState *s = DO_UPCAST(NetPasstState, data.nc, nc); + + assert(nc->info->type == NET_CLIENT_DRIVER_PASST); + + return s->vhost_user != NULL; +} + +static bool passt_has_ufo(NetClientState *nc) +{ + NetPasstState *s = DO_UPCAST(NetPasstState, data.nc, nc); + + assert(nc->info->type == NET_CLIENT_DRIVER_PASST); + + return s->vhost_user != NULL; +} + +static bool passt_check_peer_type(NetClientState *nc, ObjectClass *oc, + Error **errp) +{ + NetPasstState *s = DO_UPCAST(NetPasstState, data.nc, nc); + const char *driver = object_class_get_name(oc); + + assert(nc->info->type == NET_CLIENT_DRIVER_PASST); + + if (s->vhost_user == NULL) { + return true; + } + + if (!g_str_has_prefix(driver, "virtio-net-")) { + error_setg(errp, "vhost-user requires frontend driver virtio-net-*"); + return false; + } + + return true; +} + +static struct vhost_net *passt_get_vhost_net(NetClientState *nc) +{ + NetPasstState *s = DO_UPCAST(NetPasstState, data.nc, nc); + + assert(nc->info->type == NET_CLIENT_DRIVER_PASST); + + return s->vhost_net; +} + +static uint64_t passt_get_acked_features(NetClientState *nc) +{ + NetPasstState *s = DO_UPCAST(NetPasstState, data.nc, nc); + + assert(nc->info->type == NET_CLIENT_DRIVER_PASST); + + return s->acked_features; +} + +static void passt_save_acked_features(NetClientState *nc) +{ + NetPasstState *s = DO_UPCAST(NetPasstState, data.nc, nc); + + assert(nc->info->type == NET_CLIENT_DRIVER_PASST); + + if (s->vhost_net) { + uint64_t features = vhost_net_get_acked_features(s->vhost_net); + if (features) { + s->acked_features = features; + } + } +} +#endif + +static NetClientInfo net_passt_info = { + .type = NET_CLIENT_DRIVER_PASST, + .size = sizeof(NetPasstState), + .receive = net_passt_receive, + .cleanup = net_passt_cleanup, +#ifdef CONFIG_VHOST_USER + .has_vnet_hdr = passt_has_vnet_hdr, + .has_ufo = passt_has_ufo, + .set_vnet_be = passt_set_vnet_endianness, + .set_vnet_le = passt_set_vnet_endianness, + .check_peer_type = passt_check_peer_type, + .get_vhost_net = passt_get_vhost_net, +#endif +}; + +static void net_passt_client_connected(QIOTask *task, gpointer opaque) +{ + NetPasstState *s = opaque; + + if (net_stream_data_client_connected(task, &s->data) == 0) { + qemu_set_info_str(&s->data.nc, "stream,connected to pid %d", s->pid); + } +} + +static int net_passt_start_daemon(NetPasstState *s, int sock, Error **errp) +{ + g_autoptr(GSubprocess) daemon = NULL; + g_autofree gchar *contents = NULL; + g_autoptr(GError) error = NULL; + GSubprocessLauncher *launcher; + + qemu_set_info_str(&s->data.nc, "launching passt"); + + launcher = g_subprocess_launcher_new(G_SUBPROCESS_FLAGS_NONE); + g_subprocess_launcher_take_fd(launcher, sock, 3); + + daemon = g_subprocess_launcher_spawnv(launcher, + (const gchar *const *)s->args->pdata, + &error); + g_object_unref(launcher); + + if (!daemon) { + error_setg(errp, "Error creating daemon: %s", error->message); + return -1; + } + + if (!g_subprocess_wait(daemon, NULL, &error)) { + error_setg(errp, "Error waiting for daemon: %s", error->message); + return -1; + } + + if (g_subprocess_get_if_exited(daemon) && + g_subprocess_get_exit_status(daemon)) { + return -1; + } + + if (!g_file_get_contents(s->pidfile, &contents, NULL, &error)) { + error_setg(errp, "Cannot read passt pid: %s", error->message); + return -1; + } + + s->pid = (pid_t)g_ascii_strtoll(contents, NULL, 10); + if (s->pid <= 0) { + error_setg(errp, "File '%s' did not contain a valid PID.", s->pidfile); + return -1; + } + + return 0; +} + +static int net_passt_stream_start(NetPasstState *s, Error **errp) +{ + QIOChannelSocket *sioc; + SocketAddress *addr; + int sv[2]; + + if (socketpair(PF_UNIX, SOCK_STREAM, 0, sv) == -1) { + error_setg_errno(errp, errno, "socketpair() failed"); + return -1; + } + + /* connect to passt */ + qemu_set_info_str(&s->data.nc, "connecting to passt"); + + /* create socket channel */ + sioc = qio_channel_socket_new(); + s->data.ioc = QIO_CHANNEL(sioc); + s->data.nc.link_down = true; + s->data.send = net_passt_send; + + addr = g_new0(SocketAddress, 1); + addr->type = SOCKET_ADDRESS_TYPE_FD; + addr->u.fd.str = g_strdup_printf("%d", sv[0]); + + qio_channel_socket_connect_async(sioc, addr, + net_passt_client_connected, s, + NULL, NULL); + + qapi_free_SocketAddress(addr); + + /* start passt */ + if (net_passt_start_daemon(s, sv[1], errp) == -1) { + close(sv[0]); + close(sv[1]); + return -1; + } + close(sv[1]); + + return 0; +} + +#ifdef CONFIG_VHOST_USER +static gboolean passt_vhost_user_watch(void *do_not_use, GIOCondition cond, + void *opaque) +{ + NetPasstState *s = opaque; + + qemu_chr_fe_disconnect(&s->vhost_chr); + + return G_SOURCE_CONTINUE; +} + +static void passt_vhost_user_event(void *opaque, QEMUChrEvent event); + +static void chr_closed_bh(void *opaque) +{ + NetPasstState *s = opaque; + + passt_save_acked_features(&s->data.nc); + + net_client_set_link(&(NetClientState *){ &s->data.nc }, 1, false); + + qemu_chr_fe_set_handlers(&s->vhost_chr, NULL, NULL, passt_vhost_user_event, + NULL, s, NULL, true); +} + +static void passt_vhost_user_stop(NetPasstState *s) +{ + passt_save_acked_features(&s->data.nc); + vhost_net_cleanup(s->vhost_net); +} + +static int passt_vhost_user_start(NetPasstState *s, VhostUserState *be) +{ + struct vhost_net *net = NULL; + VhostNetOptions options; + + options.backend_type = VHOST_BACKEND_TYPE_USER; + options.net_backend = &s->data.nc; + options.opaque = be; + options.busyloop_timeout = 0; + options.nvqs = 2; + options.feature_bits = user_feature_bits; + options.max_tx_queue_size = VIRTQUEUE_MAX_SIZE; + options.get_acked_features = passt_get_acked_features; + options.save_acked_features = passt_save_acked_features; + options.is_vhost_user = true; + + net = vhost_net_init(&options); + if (!net) { + error_report("failed to init passt vhost_net"); + goto err; + } + + if (s->vhost_net) { + vhost_net_cleanup(s->vhost_net); + g_free(s->vhost_net); + } + s->vhost_net = net; + + return 0; +err: + if (net) { + vhost_net_cleanup(net); + g_free(net); + } + passt_vhost_user_stop(s); + return -1; +} + +static void passt_vhost_user_event(void *opaque, QEMUChrEvent event) +{ + NetPasstState *s = opaque; + Error *err = NULL; + + switch (event) { + case CHR_EVENT_OPENED: + if (passt_vhost_user_start(s, s->vhost_user) < 0) { + qemu_chr_fe_disconnect(&s->vhost_chr); + return; + } + s->vhost_watch = qemu_chr_fe_add_watch(&s->vhost_chr, G_IO_HUP, + passt_vhost_user_watch, s); + net_client_set_link(&(NetClientState *){ &s->data.nc }, 1, true); + s->started = true; + break; + case CHR_EVENT_CLOSED: + if (s->vhost_watch) { + AioContext *ctx = qemu_get_current_aio_context(); + + g_source_remove(s->vhost_watch); + s->vhost_watch = 0; + qemu_chr_fe_set_handlers(&s->vhost_chr, NULL, NULL, NULL, NULL, + NULL, NULL, false); + + aio_bh_schedule_oneshot(ctx, chr_closed_bh, s); + } + break; + case CHR_EVENT_BREAK: + case CHR_EVENT_MUX_IN: + case CHR_EVENT_MUX_OUT: + /* Ignore */ + break; + } + + if (err) { + error_report_err(err); + } +} + +static int net_passt_vhost_user_init(NetPasstState *s, Error **errp) +{ + Chardev *chr; + int sv[2]; + + if (socketpair(PF_UNIX, SOCK_STREAM, 0, sv) == -1) { + error_setg_errno(errp, errno, "socketpair() failed"); + return -1; + } + + /* connect to passt */ + qemu_set_info_str(&s->data.nc, "connecting to passt"); + + /* create chardev */ + + chr = CHARDEV(object_new(TYPE_CHARDEV_SOCKET)); + if (!chr || qemu_chr_add_client(chr, sv[0]) == -1) { + object_unref(OBJECT(chr)); + error_setg(errp, "Failed to make socket chardev"); + goto err; + } + + s->vhost_user = g_new0(struct VhostUserState, 1); + if (!qemu_chr_fe_init(&s->vhost_chr, chr, errp) || + !vhost_user_init(s->vhost_user, &s->vhost_chr, errp)) { + goto err; + } + + /* start passt */ + if (net_passt_start_daemon(s, sv[1], errp) == -1) { + goto err; + } + + do { + if (qemu_chr_fe_wait_connected(&s->vhost_chr, errp) < 0) { + goto err; + } + + qemu_chr_fe_set_handlers(&s->vhost_chr, NULL, NULL, + passt_vhost_user_event, NULL, s, NULL, + true); + } while (!s->started); + + qemu_set_info_str(&s->data.nc, "vhost-user,connected to pid %d", s->pid); + + close(sv[1]); + return 0; +err: + close(sv[0]); + close(sv[1]); + + return -1; +} +#else +static int net_passt_vhost_user_init(NetPasstState *s, Error **errp) +{ + error_setg(errp, "vhost-user support has not been built"); + + return -1; +} +#endif + +static GPtrArray *net_passt_decode_args(const NetDevPasstOptions *passt, + gchar *pidfile, Error **errp) +{ + GPtrArray *args = g_ptr_array_new_with_free_func(g_free); + + if (passt->path) { + g_ptr_array_add(args, g_strdup(passt->path)); + } else { + g_ptr_array_add(args, g_strdup("passt")); + } + + if (passt->has_vhost_user && passt->vhost_user) { + g_ptr_array_add(args, g_strdup("--vhost-user")); + } + + /* by default, be quiet */ + if (!passt->has_quiet || passt->quiet) { + g_ptr_array_add(args, g_strdup("--quiet")); + } + + if (passt->has_mtu) { + g_ptr_array_add(args, g_strdup("--mtu")); + g_ptr_array_add(args, g_strdup_printf("%"PRId64, passt->mtu)); + } + + if (passt->address) { + g_ptr_array_add(args, g_strdup("--address")); + g_ptr_array_add(args, g_strdup(passt->address)); + } + + if (passt->netmask) { + g_ptr_array_add(args, g_strdup("--netmask")); + g_ptr_array_add(args, g_strdup(passt->netmask)); + } + + if (passt->mac) { + g_ptr_array_add(args, g_strdup("--mac-addr")); + g_ptr_array_add(args, g_strdup(passt->mac)); + } + + if (passt->gateway) { + g_ptr_array_add(args, g_strdup("--gateway")); + g_ptr_array_add(args, g_strdup(passt->gateway)); + } + + if (passt->interface) { + g_ptr_array_add(args, g_strdup("--interface")); + g_ptr_array_add(args, g_strdup(passt->interface)); + } + + if (passt->outbound) { + g_ptr_array_add(args, g_strdup("--outbound")); + g_ptr_array_add(args, g_strdup(passt->outbound)); + } + + if (passt->outbound_if4) { + g_ptr_array_add(args, g_strdup("--outbound-if4")); + g_ptr_array_add(args, g_strdup(passt->outbound_if4)); + } + + if (passt->outbound_if6) { + g_ptr_array_add(args, g_strdup("--outbound-if6")); + g_ptr_array_add(args, g_strdup(passt->outbound_if6)); + } + + if (passt->dns) { + g_ptr_array_add(args, g_strdup("--dns")); + g_ptr_array_add(args, g_strdup(passt->dns)); + } + if (passt->fqdn) { + g_ptr_array_add(args, g_strdup("--fqdn")); + g_ptr_array_add(args, g_strdup(passt->fqdn)); + } + + if (passt->has_dhcp_dns && !passt->dhcp_dns) { + g_ptr_array_add(args, g_strdup("--no-dhcp-dns")); + } + + if (passt->has_dhcp_search && !passt->dhcp_search) { + g_ptr_array_add(args, g_strdup("--no-dhcp-search")); + } + + if (passt->map_host_loopback) { + g_ptr_array_add(args, g_strdup("--map-host-loopback")); + g_ptr_array_add(args, g_strdup(passt->map_host_loopback)); + } + + if (passt->map_guest_addr) { + g_ptr_array_add(args, g_strdup("--map-guest-addr")); + g_ptr_array_add(args, g_strdup(passt->map_guest_addr)); + } + + if (passt->dns_forward) { + g_ptr_array_add(args, g_strdup("--dns-forward")); + g_ptr_array_add(args, g_strdup(passt->dns_forward)); + } + + if (passt->dns_host) { + g_ptr_array_add(args, g_strdup("--dns-host")); + g_ptr_array_add(args, g_strdup(passt->dns_host)); + } + + if (passt->has_tcp && !passt->tcp) { + g_ptr_array_add(args, g_strdup("--no-tcp")); + } + + if (passt->has_udp && !passt->udp) { + g_ptr_array_add(args, g_strdup("--no-udp")); + } + + if (passt->has_icmp && !passt->icmp) { + g_ptr_array_add(args, g_strdup("--no-icmp")); + } + + if (passt->has_dhcp && !passt->dhcp) { + g_ptr_array_add(args, g_strdup("--no-dhcp")); + } + + if (passt->has_ndp && !passt->ndp) { + g_ptr_array_add(args, g_strdup("--no-ndp")); + } + if (passt->has_dhcpv6 && !passt->dhcpv6) { + g_ptr_array_add(args, g_strdup("--no-dhcpv6")); + } + + if (passt->has_ra && !passt->ra) { + g_ptr_array_add(args, g_strdup("--no-ra")); + } + + if (passt->has_freebind && passt->freebind) { + g_ptr_array_add(args, g_strdup("--freebind")); + } + + if (passt->has_ipv4 && !passt->ipv4) { + g_ptr_array_add(args, g_strdup("--ipv6-only")); + } + + if (passt->has_ipv6 && !passt->ipv6) { + g_ptr_array_add(args, g_strdup("--ipv4-only")); + } + + if (passt->has_search && passt->search) { + const StringList *list = passt->search; + GString *domains = g_string_new(list->value->str); + + list = list->next; + while (list) { + g_string_append(domains, " "); + g_string_append(domains, list->value->str); + list = list->next; + } + + g_ptr_array_add(args, g_strdup("--search")); + g_ptr_array_add(args, g_string_free(domains, FALSE)); + } + + if (passt->has_tcp_ports && passt->tcp_ports) { + const StringList *list = passt->tcp_ports; + GString *tcp_ports = g_string_new(list->value->str); + + list = list->next; + while (list) { + g_string_append(tcp_ports, ","); + g_string_append(tcp_ports, list->value->str); + list = list->next; + } + + g_ptr_array_add(args, g_strdup("--tcp-ports")); + g_ptr_array_add(args, g_string_free(tcp_ports, FALSE)); + } + + if (passt->has_udp_ports && passt->udp_ports) { + const StringList *list = passt->udp_ports; + GString *udp_ports = g_string_new(list->value->str); + + list = list->next; + while (list) { + g_string_append(udp_ports, ","); + g_string_append(udp_ports, list->value->str); + list = list->next; + } + + g_ptr_array_add(args, g_strdup("--udp-ports")); + g_ptr_array_add(args, g_string_free(udp_ports, FALSE)); + } + + if (passt->has_param && passt->param) { + const StringList *list = passt->param; + + while (list) { + g_ptr_array_add(args, g_strdup(list->value->str)); + list = list->next; + } + } + + /* provide a pid file to be able to kil passt on exit */ + g_ptr_array_add(args, g_strdup("--pid")); + g_ptr_array_add(args, g_strdup(pidfile)); + + /* g_subprocess_launcher_take_fd() will set the socket on fd 3 */ + g_ptr_array_add(args, g_strdup("--fd")); + g_ptr_array_add(args, g_strdup("3")); + + g_ptr_array_add(args, NULL); + + return args; +} + +int net_init_passt(const Netdev *netdev, const char *name, + NetClientState *peer, Error **errp) +{ + g_autoptr(GError) error = NULL; + NetClientState *nc; + NetPasstState *s; + GPtrArray *args; + gchar *pidfile; + int pidfd; + + assert(netdev->type == NET_CLIENT_DRIVER_PASST); + + pidfd = g_file_open_tmp("passt-XXXXXX.pid", &pidfile, &error); + if (pidfd == -1) { + error_setg(errp, "Failed to create temporary file: %s", error->message); + return -1; + } + close(pidfd); + + args = net_passt_decode_args(&netdev->u.passt, pidfile, errp); + if (args == NULL) { + g_free(pidfile); + return -1; + } + + nc = qemu_new_net_client(&net_passt_info, peer, "passt", name); + s = DO_UPCAST(NetPasstState, data.nc, nc); + + s->args = args; + s->pidfile = pidfile; + + if (netdev->u.passt.has_vhost_user && netdev->u.passt.vhost_user) { + if (net_passt_vhost_user_init(s, errp) == -1) { + qemu_del_net_client(nc); + return -1; + } + + return 0; + } + + if (net_passt_stream_start(s, errp) == -1) { + qemu_del_net_client(nc); + return -1; + } + + return 0; +} diff --git a/net/stream.c b/net/stream.c index 6152d2a..d893f02 100644 --- a/net/stream.c +++ b/net/stream.c @@ -27,173 +27,50 @@ #include "net/net.h" #include "clients.h" -#include "monitor/monitor.h" #include "qapi/error.h" -#include "qemu/error-report.h" -#include "qemu/option.h" -#include "qemu/sockets.h" -#include "qemu/iov.h" -#include "qemu/main-loop.h" -#include "qemu/cutils.h" -#include "io/channel.h" -#include "io/channel-socket.h" #include "io/net-listener.h" #include "qapi/qapi-events-net.h" #include "qapi/qapi-visit-sockets.h" #include "qapi/clone-visitor.h" +#include "stream_data.h" + typedef struct NetStreamState { - NetClientState nc; - QIOChannel *listen_ioc; - QIONetListener *listener; - QIOChannel *ioc; - guint ioc_read_tag; - guint ioc_write_tag; - SocketReadState rs; - unsigned int send_index; /* number of bytes sent*/ + NetStreamData data; uint32_t reconnect_ms; guint timer_tag; SocketAddress *addr; } NetStreamState; -static void net_stream_listen(QIONetListener *listener, - QIOChannelSocket *cioc, - void *opaque); static void net_stream_arm_reconnect(NetStreamState *s); -static gboolean net_stream_writable(QIOChannel *ioc, - GIOCondition condition, - gpointer data) -{ - NetStreamState *s = data; - - s->ioc_write_tag = 0; - - qemu_flush_queued_packets(&s->nc); - - return G_SOURCE_REMOVE; -} - static ssize_t net_stream_receive(NetClientState *nc, const uint8_t *buf, size_t size) { - NetStreamState *s = DO_UPCAST(NetStreamState, nc, nc); - uint32_t len = htonl(size); - struct iovec iov[] = { - { - .iov_base = &len, - .iov_len = sizeof(len), - }, { - .iov_base = (void *)buf, - .iov_len = size, - }, - }; - struct iovec local_iov[2]; - unsigned int nlocal_iov; - size_t remaining; - ssize_t ret; - - remaining = iov_size(iov, 2) - s->send_index; - nlocal_iov = iov_copy(local_iov, 2, iov, 2, s->send_index, remaining); - ret = qio_channel_writev(s->ioc, local_iov, nlocal_iov, NULL); - if (ret == QIO_CHANNEL_ERR_BLOCK) { - ret = 0; /* handled further down */ - } - if (ret == -1) { - s->send_index = 0; - return -errno; - } - if (ret < (ssize_t)remaining) { - s->send_index += ret; - s->ioc_write_tag = qio_channel_add_watch(s->ioc, G_IO_OUT, - net_stream_writable, s, NULL); - return 0; - } - s->send_index = 0; - return size; -} - -static gboolean net_stream_send(QIOChannel *ioc, - GIOCondition condition, - gpointer data); - -static void net_stream_send_completed(NetClientState *nc, ssize_t len) -{ - NetStreamState *s = DO_UPCAST(NetStreamState, nc, nc); - - if (!s->ioc_read_tag) { - s->ioc_read_tag = qio_channel_add_watch(s->ioc, G_IO_IN, - net_stream_send, s, NULL); - } -} + NetStreamData *d = DO_UPCAST(NetStreamData, nc, nc); -static void net_stream_rs_finalize(SocketReadState *rs) -{ - NetStreamState *s = container_of(rs, NetStreamState, rs); - - if (qemu_send_packet_async(&s->nc, rs->buf, - rs->packet_len, - net_stream_send_completed) == 0) { - if (s->ioc_read_tag) { - g_source_remove(s->ioc_read_tag); - s->ioc_read_tag = 0; - } - } + return net_stream_data_receive(d, buf, size); } static gboolean net_stream_send(QIOChannel *ioc, GIOCondition condition, gpointer data) { - NetStreamState *s = data; - int size; - int ret; - QEMU_UNINITIALIZED char buf1[NET_BUFSIZE]; - const char *buf; - - size = qio_channel_read(s->ioc, buf1, sizeof(buf1), NULL); - if (size < 0) { - if (errno != EWOULDBLOCK) { - goto eoc; - } - } else if (size == 0) { - /* end of connection */ - eoc: - s->ioc_read_tag = 0; - if (s->ioc_write_tag) { - g_source_remove(s->ioc_write_tag); - s->ioc_write_tag = 0; - } - if (s->listener) { - qemu_set_info_str(&s->nc, "listening"); - qio_net_listener_set_client_func(s->listener, net_stream_listen, - s, NULL); - } - object_unref(OBJECT(s->ioc)); - s->ioc = NULL; - - net_socket_rs_init(&s->rs, net_stream_rs_finalize, false); - s->nc.link_down = true; + if (net_stream_data_send(ioc, condition, data) == G_SOURCE_REMOVE) { + NetStreamState *s = DO_UPCAST(NetStreamState, data, data); - qapi_event_send_netdev_stream_disconnected(s->nc.name); + qapi_event_send_netdev_stream_disconnected(s->data.nc.name); net_stream_arm_reconnect(s); return G_SOURCE_REMOVE; } - buf = buf1; - - ret = net_fill_rstate(&s->rs, (const uint8_t *)buf, size); - - if (ret == -1) { - goto eoc; - } return G_SOURCE_CONTINUE; } static void net_stream_cleanup(NetClientState *nc) { - NetStreamState *s = DO_UPCAST(NetStreamState, nc, nc); + NetStreamState *s = DO_UPCAST(NetStreamState, data.nc, nc); if (s->timer_tag) { g_source_remove(s->timer_tag); s->timer_tag = 0; @@ -202,28 +79,28 @@ static void net_stream_cleanup(NetClientState *nc) qapi_free_SocketAddress(s->addr); s->addr = NULL; } - if (s->ioc) { - if (QIO_CHANNEL_SOCKET(s->ioc)->fd != -1) { - if (s->ioc_read_tag) { - g_source_remove(s->ioc_read_tag); - s->ioc_read_tag = 0; + if (s->data.ioc) { + if (QIO_CHANNEL_SOCKET(s->data.ioc)->fd != -1) { + if (s->data.ioc_read_tag) { + g_source_remove(s->data.ioc_read_tag); + s->data.ioc_read_tag = 0; } - if (s->ioc_write_tag) { - g_source_remove(s->ioc_write_tag); - s->ioc_write_tag = 0; + if (s->data.ioc_write_tag) { + g_source_remove(s->data.ioc_write_tag); + s->data.ioc_write_tag = 0; } } - object_unref(OBJECT(s->ioc)); - s->ioc = NULL; + object_unref(OBJECT(s->data.ioc)); + s->data.ioc = NULL; } - if (s->listen_ioc) { - if (s->listener) { - qio_net_listener_disconnect(s->listener); - object_unref(OBJECT(s->listener)); - s->listener = NULL; + if (s->data.listen_ioc) { + if (s->data.listener) { + qio_net_listener_disconnect(s->data.listener); + object_unref(OBJECT(s->data.listener)); + s->data.listener = NULL; } - object_unref(OBJECT(s->listen_ioc)); - s->listen_ioc = NULL; + object_unref(OBJECT(s->data.listen_ioc)); + s->data.listen_ioc = NULL; } } @@ -235,23 +112,13 @@ static NetClientInfo net_stream_info = { }; static void net_stream_listen(QIONetListener *listener, - QIOChannelSocket *cioc, - void *opaque) + QIOChannelSocket *cioc, gpointer data) { - NetStreamState *s = opaque; + NetStreamData *d = data; SocketAddress *addr; char *uri; - object_ref(OBJECT(cioc)); - - qio_net_listener_set_client_func(s->listener, NULL, s, NULL); - - s->ioc = QIO_CHANNEL(cioc); - qio_channel_set_name(s->ioc, "stream-server"); - s->nc.link_down = false; - - s->ioc_read_tag = qio_channel_add_watch(s->ioc, G_IO_IN, net_stream_send, - s, NULL); + net_stream_data_listen(listener, cioc, data); if (cioc->localAddr.ss_family == AF_UNIX) { addr = qio_channel_socket_get_local_address(cioc, NULL); @@ -260,22 +127,22 @@ static void net_stream_listen(QIONetListener *listener, } g_assert(addr != NULL); uri = socket_uri(addr); - qemu_set_info_str(&s->nc, "%s", uri); + qemu_set_info_str(&d->nc, "%s", uri); g_free(uri); - qapi_event_send_netdev_stream_connected(s->nc.name, addr); + qapi_event_send_netdev_stream_connected(d->nc.name, addr); qapi_free_SocketAddress(addr); } static void net_stream_server_listening(QIOTask *task, gpointer opaque) { - NetStreamState *s = opaque; - QIOChannelSocket *listen_sioc = QIO_CHANNEL_SOCKET(s->listen_ioc); + NetStreamData *d = opaque; + QIOChannelSocket *listen_sioc = QIO_CHANNEL_SOCKET(d->listen_ioc); SocketAddress *addr; int ret; Error *err = NULL; if (qio_task_propagate_error(task, &err)) { - qemu_set_info_str(&s->nc, "error: %s", error_get_pretty(err)); + qemu_set_info_str(&d->nc, "error: %s", error_get_pretty(err)); error_free(err); return; } @@ -284,20 +151,21 @@ static void net_stream_server_listening(QIOTask *task, gpointer opaque) g_assert(addr != NULL); ret = qemu_socket_try_set_nonblock(listen_sioc->fd); if (addr->type == SOCKET_ADDRESS_TYPE_FD && ret < 0) { - qemu_set_info_str(&s->nc, "can't use file descriptor %s (errno %d)", + qemu_set_info_str(&d->nc, "can't use file descriptor %s (errno %d)", addr->u.fd.str, -ret); return; } g_assert(ret == 0); qapi_free_SocketAddress(addr); - s->nc.link_down = true; - s->listener = qio_net_listener_new(); + d->nc.link_down = true; + d->listener = qio_net_listener_new(); - qemu_set_info_str(&s->nc, "listening"); - net_socket_rs_init(&s->rs, net_stream_rs_finalize, false); - qio_net_listener_set_client_func(s->listener, net_stream_listen, s, NULL); - qio_net_listener_add(s->listener, listen_sioc); + qemu_set_info_str(&d->nc, "listening"); + net_socket_rs_init(&d->rs, net_stream_data_rs_finalize, false); + qio_net_listener_set_client_func(d->listener, d->listen, d, + NULL); + qio_net_listener_add(d->listener, listen_sioc); } static int net_stream_server_init(NetClientState *peer, @@ -307,16 +175,18 @@ static int net_stream_server_init(NetClientState *peer, Error **errp) { NetClientState *nc; - NetStreamState *s; + NetStreamData *d; QIOChannelSocket *listen_sioc = qio_channel_socket_new(); nc = qemu_new_net_client(&net_stream_info, peer, model, name); - s = DO_UPCAST(NetStreamState, nc, nc); - qemu_set_info_str(&s->nc, "initializing"); + d = DO_UPCAST(NetStreamData, nc, nc); + d->send = net_stream_send; + d->listen = net_stream_listen; + qemu_set_info_str(&d->nc, "initializing"); - s->listen_ioc = QIO_CHANNEL(listen_sioc); + d->listen_ioc = QIO_CHANNEL(listen_sioc); qio_channel_socket_listen_async(listen_sioc, addr, 0, - net_stream_server_listening, s, + net_stream_server_listening, d, NULL, NULL); return 0; @@ -325,49 +195,23 @@ static int net_stream_server_init(NetClientState *peer, static void net_stream_client_connected(QIOTask *task, gpointer opaque) { NetStreamState *s = opaque; - QIOChannelSocket *sioc = QIO_CHANNEL_SOCKET(s->ioc); + NetStreamData *d = &s->data; + QIOChannelSocket *sioc = QIO_CHANNEL_SOCKET(d->ioc); SocketAddress *addr; gchar *uri; - int ret; - Error *err = NULL; - if (qio_task_propagate_error(task, &err)) { - qemu_set_info_str(&s->nc, "error: %s", error_get_pretty(err)); - error_free(err); - goto error; + if (net_stream_data_client_connected(task, d) == -1) { + net_stream_arm_reconnect(s); + return; } addr = qio_channel_socket_get_remote_address(sioc, NULL); g_assert(addr != NULL); uri = socket_uri(addr); - qemu_set_info_str(&s->nc, "%s", uri); + qemu_set_info_str(&d->nc, "%s", uri); g_free(uri); - - ret = qemu_socket_try_set_nonblock(sioc->fd); - if (addr->type == SOCKET_ADDRESS_TYPE_FD && ret < 0) { - qemu_set_info_str(&s->nc, "can't use file descriptor %s (errno %d)", - addr->u.fd.str, -ret); - qapi_free_SocketAddress(addr); - goto error; - } - g_assert(ret == 0); - - net_socket_rs_init(&s->rs, net_stream_rs_finalize, false); - - /* Disable Nagle algorithm on TCP sockets to reduce latency */ - qio_channel_set_delay(s->ioc, false); - - s->ioc_read_tag = qio_channel_add_watch(s->ioc, G_IO_IN, net_stream_send, - s, NULL); - s->nc.link_down = false; - qapi_event_send_netdev_stream_connected(s->nc.name, addr); + qapi_event_send_netdev_stream_connected(d->nc.name, addr); qapi_free_SocketAddress(addr); - - return; -error: - object_unref(OBJECT(s->ioc)); - s->ioc = NULL; - net_stream_arm_reconnect(s); } static gboolean net_stream_reconnect(gpointer data) @@ -378,7 +222,7 @@ static gboolean net_stream_reconnect(gpointer data) s->timer_tag = 0; sioc = qio_channel_socket_new(); - s->ioc = QIO_CHANNEL(sioc); + s->data.ioc = QIO_CHANNEL(sioc); qio_channel_socket_connect_async(sioc, s->addr, net_stream_client_connected, s, NULL, NULL); @@ -388,7 +232,7 @@ static gboolean net_stream_reconnect(gpointer data) static void net_stream_arm_reconnect(NetStreamState *s) { if (s->reconnect_ms && s->timer_tag == 0) { - qemu_set_info_str(&s->nc, "connecting"); + qemu_set_info_str(&s->data.nc, "connecting"); s->timer_tag = g_timeout_add(s->reconnect_ms, net_stream_reconnect, s); } } @@ -405,11 +249,13 @@ static int net_stream_client_init(NetClientState *peer, QIOChannelSocket *sioc = qio_channel_socket_new(); nc = qemu_new_net_client(&net_stream_info, peer, model, name); - s = DO_UPCAST(NetStreamState, nc, nc); - qemu_set_info_str(&s->nc, "connecting"); + s = DO_UPCAST(NetStreamState, data.nc, nc); + qemu_set_info_str(&s->data.nc, "connecting"); - s->ioc = QIO_CHANNEL(sioc); - s->nc.link_down = true; + s->data.ioc = QIO_CHANNEL(sioc); + s->data.nc.link_down = true; + s->data.send = net_stream_send; + s->data.listen = net_stream_listen; s->reconnect_ms = reconnect_ms; if (reconnect_ms) { diff --git a/net/stream_data.c b/net/stream_data.c new file mode 100644 index 0000000..5af27e0 --- /dev/null +++ b/net/stream_data.c @@ -0,0 +1,193 @@ +/* + * net stream generic functions + * + * Copyright Red Hat + * + * SPDX-License-Identifier: GPL-2.0-or-later + */ + +#include "qemu/osdep.h" +#include "qemu/iov.h" +#include "qapi/error.h" +#include "net/net.h" +#include "io/channel.h" +#include "io/net-listener.h" + +#include "stream_data.h" + +static gboolean net_stream_data_writable(QIOChannel *ioc, + GIOCondition condition, gpointer data) +{ + NetStreamData *d = data; + + d->ioc_write_tag = 0; + + qemu_flush_queued_packets(&d->nc); + + return G_SOURCE_REMOVE; +} + +ssize_t net_stream_data_receive(NetStreamData *d, const uint8_t *buf, + size_t size) +{ + uint32_t len = htonl(size); + struct iovec iov[] = { + { + .iov_base = &len, + .iov_len = sizeof(len), + }, { + .iov_base = (void *)buf, + .iov_len = size, + }, + }; + struct iovec local_iov[2]; + unsigned int nlocal_iov; + size_t remaining; + ssize_t ret; + + remaining = iov_size(iov, 2) - d->send_index; + nlocal_iov = iov_copy(local_iov, 2, iov, 2, d->send_index, remaining); + ret = qio_channel_writev(d->ioc, local_iov, nlocal_iov, NULL); + if (ret == QIO_CHANNEL_ERR_BLOCK) { + ret = 0; /* handled further down */ + } + if (ret == -1) { + d->send_index = 0; + return -errno; + } + if (ret < (ssize_t)remaining) { + d->send_index += ret; + d->ioc_write_tag = qio_channel_add_watch(d->ioc, G_IO_OUT, + net_stream_data_writable, d, + NULL); + return 0; + } + d->send_index = 0; + return size; +} + +static void net_stream_data_send_completed(NetClientState *nc, ssize_t len) +{ + NetStreamData *d = DO_UPCAST(NetStreamData, nc, nc); + + if (!d->ioc_read_tag) { + d->ioc_read_tag = qio_channel_add_watch(d->ioc, G_IO_IN, d->send, d, + NULL); + } +} + +void net_stream_data_rs_finalize(SocketReadState *rs) +{ + NetStreamData *d = container_of(rs, NetStreamData, rs); + + if (qemu_send_packet_async(&d->nc, rs->buf, + rs->packet_len, + net_stream_data_send_completed) == 0) { + if (d->ioc_read_tag) { + g_source_remove(d->ioc_read_tag); + d->ioc_read_tag = 0; + } + } +} + +gboolean net_stream_data_send(QIOChannel *ioc, GIOCondition condition, + NetStreamData *d) +{ + int size; + int ret; + QEMU_UNINITIALIZED char buf1[NET_BUFSIZE]; + const char *buf; + + size = qio_channel_read(d->ioc, buf1, sizeof(buf1), NULL); + if (size < 0) { + if (errno != EWOULDBLOCK) { + goto eoc; + } + } else if (size == 0) { + /* end of connection */ + eoc: + d->ioc_read_tag = 0; + if (d->ioc_write_tag) { + g_source_remove(d->ioc_write_tag); + d->ioc_write_tag = 0; + } + if (d->listener) { + qemu_set_info_str(&d->nc, "listening"); + qio_net_listener_set_client_func(d->listener, + d->listen, d, NULL); + } + object_unref(OBJECT(d->ioc)); + d->ioc = NULL; + + net_socket_rs_init(&d->rs, net_stream_data_rs_finalize, false); + d->nc.link_down = true; + + return G_SOURCE_REMOVE; + } + buf = buf1; + + ret = net_fill_rstate(&d->rs, (const uint8_t *)buf, size); + + if (ret == -1) { + goto eoc; + } + + return G_SOURCE_CONTINUE; +} + +void net_stream_data_listen(QIONetListener *listener, + QIOChannelSocket *cioc, + NetStreamData *d) +{ + object_ref(OBJECT(cioc)); + + qio_net_listener_set_client_func(d->listener, NULL, d, NULL); + + d->ioc = QIO_CHANNEL(cioc); + qio_channel_set_name(d->ioc, "stream-server"); + d->nc.link_down = false; + + d->ioc_read_tag = qio_channel_add_watch(d->ioc, G_IO_IN, d->send, d, NULL); +} + +int net_stream_data_client_connected(QIOTask *task, NetStreamData *d) +{ + QIOChannelSocket *sioc = QIO_CHANNEL_SOCKET(d->ioc); + SocketAddress *addr; + int ret; + Error *err = NULL; + + if (qio_task_propagate_error(task, &err)) { + qemu_set_info_str(&d->nc, "error: %s", error_get_pretty(err)); + error_free(err); + goto error; + } + + addr = qio_channel_socket_get_remote_address(sioc, NULL); + g_assert(addr != NULL); + + ret = qemu_socket_try_set_nonblock(sioc->fd); + if (addr->type == SOCKET_ADDRESS_TYPE_FD && ret < 0) { + qemu_set_info_str(&d->nc, "can't use file descriptor %s (errno %d)", + addr->u.fd.str, -ret); + qapi_free_SocketAddress(addr); + goto error; + } + g_assert(ret == 0); + qapi_free_SocketAddress(addr); + + net_socket_rs_init(&d->rs, net_stream_data_rs_finalize, false); + + /* Disable Nagle algorithm on TCP sockets to reduce latency */ + qio_channel_set_delay(d->ioc, false); + + d->ioc_read_tag = qio_channel_add_watch(d->ioc, G_IO_IN, d->send, d, NULL); + d->nc.link_down = false; + + return 0; +error: + object_unref(OBJECT(d->ioc)); + d->ioc = NULL; + + return -1; +} diff --git a/net/stream_data.h b/net/stream_data.h new file mode 100644 index 0000000..b868625 --- /dev/null +++ b/net/stream_data.h @@ -0,0 +1,31 @@ +/* + * net stream generic functions + * + * Copyright Red Hat + * + * SPDX-License-Identifier: GPL-2.0-or-later + */ + +typedef struct NetStreamData { + NetClientState nc; + QIOChannel *ioc; + guint ioc_read_tag; + guint ioc_write_tag; + SocketReadState rs; + unsigned int send_index; /* number of bytes sent*/ + QIOChannelFunc send; + /* server data */ + QIOChannel *listen_ioc; + QIONetListener *listener; + QIONetListenerClientFunc listen; +} NetStreamData; + +ssize_t net_stream_data_receive(NetStreamData *d, const uint8_t *buf, + size_t size); +void net_stream_data_rs_finalize(SocketReadState *rs); +gboolean net_stream_data_send(QIOChannel *ioc, GIOCondition condition, + NetStreamData *d); +int net_stream_data_client_connected(QIOTask *task, NetStreamData *d); +void net_stream_data_listen(QIONetListener *listener, + QIOChannelSocket *cioc, + NetStreamData *d); diff --git a/net/tap-win32.c b/net/tap-win32.c index 671dee9..38baf90 100644 --- a/net/tap-win32.c +++ b/net/tap-win32.c @@ -704,11 +704,6 @@ static void tap_win32_send(void *opaque) } } -struct vhost_net *tap_get_vhost_net(NetClientState *nc) -{ - return NULL; -} - static NetClientInfo net_tap_win32_info = { .type = NET_CLIENT_DRIVER_TAP, .size = sizeof(TAPState), @@ -42,11 +42,29 @@ #include "qemu/error-report.h" #include "qemu/main-loop.h" #include "qemu/sockets.h" +#include "hw/virtio/vhost.h" #include "net/tap.h" #include "net/vhost_net.h" +static const int kernel_feature_bits[] = { + VIRTIO_F_NOTIFY_ON_EMPTY, + VIRTIO_RING_F_INDIRECT_DESC, + VIRTIO_RING_F_EVENT_IDX, + VIRTIO_NET_F_MRG_RXBUF, + VIRTIO_F_VERSION_1, + VIRTIO_NET_F_MTU, + VIRTIO_F_IOMMU_PLATFORM, + VIRTIO_F_RING_PACKED, + VIRTIO_F_RING_RESET, + VIRTIO_F_IN_ORDER, + VIRTIO_F_NOTIFICATION_DATA, + VIRTIO_NET_F_RSC_EXT, + VIRTIO_NET_F_HASH_REPORT, + VHOST_INVALID_FEATURE_BIT +}; + typedef struct TAPState { NetClientState nc; int fd; @@ -329,6 +347,18 @@ int tap_get_fd(NetClientState *nc) return s->fd; } +/* + * tap_get_vhost_net() can return NULL if a tap net-device backend is + * created with 'vhost=off' option, 'vhostforce=off' or no vhost or + * vhostforce or vhostfd options at all. Please see net_init_tap_one(). + */ +static VHostNetState *tap_get_vhost_net(NetClientState *nc) +{ + TAPState *s = DO_UPCAST(TAPState, nc, nc); + assert(nc->info->type == NET_CLIENT_DRIVER_TAP); + return s->vhost_net; +} + /* fd support */ static NetClientInfo net_tap_info = { @@ -347,6 +377,7 @@ static NetClientInfo net_tap_info = { .set_vnet_le = tap_set_vnet_le, .set_vnet_be = tap_set_vnet_be, .set_steering_ebpf = tap_set_steering_ebpf, + .get_vhost_net = tap_get_vhost_net, }; static TAPState *net_tap_fd_init(NetClientState *peer, @@ -712,6 +743,11 @@ static void net_init_tap_one(const NetdevTapOptions *tap, NetClientState *peer, } options.opaque = (void *)(uintptr_t)vhostfd; options.nvqs = 2; + options.feature_bits = kernel_feature_bits; + options.get_acked_features = NULL; + options.save_acked_features = NULL; + options.max_tx_queue_size = 0; + options.is_vhost_user = false; s->vhost_net = vhost_net_init(&options); if (!s->vhost_net) { @@ -980,13 +1016,6 @@ free_fail: return 0; } -VHostNetState *tap_get_vhost_net(NetClientState *nc) -{ - TAPState *s = DO_UPCAST(TAPState, nc, nc); - assert(nc->info->type == NET_CLIENT_DRIVER_TAP); - return s->vhost_net; -} - int tap_enable(NetClientState *nc) { TAPState *s = DO_UPCAST(TAPState, nc, nc); diff --git a/net/vhost-user-stub.c b/net/vhost-user-stub.c index 52ab4e1..283dee8 100644 --- a/net/vhost-user-stub.c +++ b/net/vhost-user-stub.c @@ -11,7 +11,6 @@ #include "qemu/osdep.h" #include "clients.h" #include "net/vhost_net.h" -#include "net/vhost-user.h" #include "qemu/error-report.h" #include "qapi/error.h" diff --git a/net/vhost-user.c b/net/vhost-user.c index 0b235e5..1c3b8b3 100644 --- a/net/vhost-user.c +++ b/net/vhost-user.c @@ -11,8 +11,9 @@ #include "qemu/osdep.h" #include "clients.h" #include "net/vhost_net.h" -#include "net/vhost-user.h" +#include "hw/virtio/vhost.h" #include "hw/virtio/vhost-user.h" +#include "standard-headers/linux/virtio_net.h" #include "chardev/char-fe.h" #include "qapi/error.h" #include "qapi/qapi-commands-net.h" @@ -22,6 +23,46 @@ #include "qemu/option.h" #include "trace.h" +static const int user_feature_bits[] = { + VIRTIO_F_NOTIFY_ON_EMPTY, + VIRTIO_F_NOTIFICATION_DATA, + VIRTIO_RING_F_INDIRECT_DESC, + VIRTIO_RING_F_EVENT_IDX, + + VIRTIO_F_ANY_LAYOUT, + VIRTIO_F_VERSION_1, + VIRTIO_NET_F_CSUM, + VIRTIO_NET_F_GUEST_CSUM, + VIRTIO_NET_F_GSO, + VIRTIO_NET_F_GUEST_TSO4, + VIRTIO_NET_F_GUEST_TSO6, + VIRTIO_NET_F_GUEST_ECN, + VIRTIO_NET_F_GUEST_UFO, + VIRTIO_NET_F_HOST_TSO4, + VIRTIO_NET_F_HOST_TSO6, + VIRTIO_NET_F_HOST_ECN, + VIRTIO_NET_F_HOST_UFO, + VIRTIO_NET_F_MRG_RXBUF, + VIRTIO_NET_F_MTU, + VIRTIO_F_IOMMU_PLATFORM, + VIRTIO_F_RING_PACKED, + VIRTIO_F_RING_RESET, + VIRTIO_F_IN_ORDER, + VIRTIO_NET_F_RSS, + VIRTIO_NET_F_RSC_EXT, + VIRTIO_NET_F_HASH_REPORT, + VIRTIO_NET_F_GUEST_USO4, + VIRTIO_NET_F_GUEST_USO6, + VIRTIO_NET_F_HOST_USO, + + /* This bit implies RARP isn't sent by QEMU out of band */ + VIRTIO_NET_F_GUEST_ANNOUNCE, + + VIRTIO_NET_F_MQ, + + VHOST_INVALID_FEATURE_BIT +}; + typedef struct NetVhostUserState { NetClientState nc; CharBackend chr; /* only queue index 0 */ @@ -32,21 +73,21 @@ typedef struct NetVhostUserState { bool started; } NetVhostUserState; -VHostNetState *vhost_user_get_vhost_net(NetClientState *nc) +static struct vhost_net *vhost_user_get_vhost_net(NetClientState *nc) { NetVhostUserState *s = DO_UPCAST(NetVhostUserState, nc, nc); assert(nc->info->type == NET_CLIENT_DRIVER_VHOST_USER); return s->vhost_net; } -uint64_t vhost_user_get_acked_features(NetClientState *nc) +static uint64_t vhost_user_get_acked_features(NetClientState *nc) { NetVhostUserState *s = DO_UPCAST(NetVhostUserState, nc, nc); assert(nc->info->type == NET_CLIENT_DRIVER_VHOST_USER); return s->acked_features; } -void vhost_user_save_acked_features(NetClientState *nc) +static void vhost_user_save_acked_features(NetClientState *nc) { NetVhostUserState *s; @@ -96,6 +137,12 @@ static int vhost_user_start(int queues, NetClientState *ncs[], options.opaque = be; options.busyloop_timeout = 0; options.nvqs = 2; + options.feature_bits = user_feature_bits; + options.max_tx_queue_size = VIRTQUEUE_MAX_SIZE; + options.get_acked_features = vhost_user_get_acked_features; + options.save_acked_features = vhost_user_save_acked_features; + options.is_vhost_user = true; + net = vhost_net_init(&options); if (!net) { error_report("failed to init vhost_net for queue %d", i); @@ -231,6 +278,7 @@ static NetClientInfo net_vhost_user_info = { .set_vnet_be = vhost_user_set_vnet_endianness, .set_vnet_le = vhost_user_set_vnet_endianness, .check_peer_type = vhost_user_check_peer_type, + .get_vhost_net = vhost_user_get_vhost_net, }; static gboolean net_vhost_user_watch(void *do_not_use, GIOCondition cond, @@ -264,7 +312,7 @@ static void chr_closed_bh(void *opaque) vhost_user_save_acked_features(ncs[i]); } - qmp_set_link(name, false, &err); + net_client_set_link(ncs, queues, false); qemu_chr_fe_set_handlers(&s->chr, NULL, NULL, net_vhost_user_event, NULL, opaque, NULL, true); @@ -300,7 +348,7 @@ static void net_vhost_user_event(void *opaque, QEMUChrEvent event) } s->watch = qemu_chr_fe_add_watch(&s->chr, G_IO_HUP, net_vhost_user_watch, s); - qmp_set_link(name, true, &err); + net_client_set_link(ncs, queues, true); s->started = true; qapi_event_send_netdev_vhost_user_connected(name, chr->label); break; diff --git a/net/vhost-vdpa.c b/net/vhost-vdpa.c index 58d7389..6a30a44 100644 --- a/net/vhost-vdpa.c +++ b/net/vhost-vdpa.c @@ -55,7 +55,7 @@ typedef struct VhostVDPAState { * with the exception of VHOST_INVALID_FEATURE_BIT, * which should always be the last entry. */ -const int vdpa_feature_bits[] = { +static const int vdpa_feature_bits[] = { VIRTIO_F_ANY_LAYOUT, VIRTIO_F_IOMMU_PLATFORM, VIRTIO_F_NOTIFY_ON_EMPTY, @@ -132,7 +132,7 @@ static const uint64_t vdpa_svq_device_features = #define VHOST_VDPA_NET_CVQ_ASID 1 -VHostNetState *vhost_vdpa_get_vhost_net(NetClientState *nc) +static struct vhost_net *vhost_vdpa_get_vhost_net(NetClientState *nc) { VhostVDPAState *s = DO_UPCAST(VhostVDPAState, nc, nc); assert(nc->info->type == NET_CLIENT_DRIVER_VHOST_VDPA); @@ -201,6 +201,11 @@ static int vhost_vdpa_add(NetClientState *ncs, void *be, options.opaque = be; options.busyloop_timeout = 0; options.nvqs = nvqs; + options.feature_bits = vdpa_feature_bits; + options.get_acked_features = NULL; + options.save_acked_features = NULL; + options.max_tx_queue_size = VIRTQUEUE_MAX_SIZE; + options.is_vhost_user = false; net = vhost_net_init(&options); if (!net) { @@ -239,15 +244,35 @@ static void vhost_vdpa_cleanup(NetClientState *nc) g_free(s->vhost_vdpa.shared); } -/** Dummy SetSteeringEBPF to support RSS for vhost-vdpa backend */ -static bool vhost_vdpa_set_steering_ebpf(NetClientState *nc, int prog_fd) +static bool vhost_vdpa_has_vnet_hdr(NetClientState *nc) { + assert(nc->info->type == NET_CLIENT_DRIVER_VHOST_VDPA); + return true; } -static bool vhost_vdpa_has_vnet_hdr(NetClientState *nc) +static bool vhost_vdpa_get_vnet_hash_supported_types(NetClientState *nc, + uint32_t *types) { assert(nc->info->type == NET_CLIENT_DRIVER_VHOST_VDPA); + VhostVDPAState *s = DO_UPCAST(VhostVDPAState, nc, nc); + uint64_t features = s->vhost_vdpa.dev->features; + int fd = s->vhost_vdpa.shared->device_fd; + struct { + struct vhost_vdpa_config hdr; + uint32_t supported_hash_types; + } config; + + if (!virtio_has_feature(features, VIRTIO_NET_F_HASH_REPORT) && + !virtio_has_feature(features, VIRTIO_NET_F_RSS)) { + return false; + } + + config.hdr.off = offsetof(struct virtio_net_config, supported_hash_types); + config.hdr.len = sizeof(config.supported_hash_types); + + assert(!ioctl(fd, VHOST_VDPA_GET_CONFIG, &config)); + *types = le32_to_cpu(config.supported_hash_types); return true; } @@ -428,10 +453,11 @@ static NetClientInfo net_vhost_vdpa_info = { .stop = vhost_vdpa_net_client_stop, .cleanup = vhost_vdpa_cleanup, .has_vnet_hdr = vhost_vdpa_has_vnet_hdr, + .get_vnet_hash_supported_types = vhost_vdpa_get_vnet_hash_supported_types, .has_ufo = vhost_vdpa_has_ufo, .set_vnet_le = vhost_vdpa_set_vnet_le, .check_peer_type = vhost_vdpa_check_peer_type, - .set_steering_ebpf = vhost_vdpa_set_steering_ebpf, + .get_vhost_net = vhost_vdpa_get_vhost_net, }; static int64_t vhost_vdpa_get_vring_group(int device_fd, unsigned vq_index, @@ -838,13 +864,13 @@ static int vhost_vdpa_net_load_rss(VhostVDPAState *s, const VirtIONet *n, * configuration only at live migration. */ if (!n->rss_data.enabled || - n->rss_data.hash_types == VIRTIO_NET_HASH_REPORT_NONE) { + n->rss_data.runtime_hash_types == VIRTIO_NET_HASH_REPORT_NONE) { return 0; } table = g_malloc_n(n->rss_data.indirections_len, sizeof(n->rss_data.indirections_table[0])); - cfg.hash_types = cpu_to_le32(n->rss_data.hash_types); + cfg.hash_types = cpu_to_le32(n->rss_data.runtime_hash_types); if (do_rss) { /* @@ -1284,9 +1310,10 @@ static NetClientInfo net_vhost_vdpa_cvq_info = { .stop = vhost_vdpa_net_cvq_stop, .cleanup = vhost_vdpa_cleanup, .has_vnet_hdr = vhost_vdpa_has_vnet_hdr, + .get_vnet_hash_supported_types = vhost_vdpa_get_vnet_hash_supported_types, .has_ufo = vhost_vdpa_has_ufo, .check_peer_type = vhost_vdpa_check_peer_type, - .set_steering_ebpf = vhost_vdpa_set_steering_ebpf, + .get_vhost_net = vhost_vdpa_get_vhost_net, }; /* diff --git a/pc-bios/hppa-firmware.img b/pc-bios/hppa-firmware.img Binary files differindex d5f6f2f..d5f6f2f 100755..100644 --- a/pc-bios/hppa-firmware.img +++ b/pc-bios/hppa-firmware.img diff --git a/pc-bios/hppa-firmware64.img b/pc-bios/hppa-firmware64.img Binary files differindex 577b0a1..577b0a1 100755..100644 --- a/pc-bios/hppa-firmware64.img +++ b/pc-bios/hppa-firmware64.img diff --git a/qapi/net.json b/qapi/net.json index 97ea183..1f40bf4 100644 --- a/qapi/net.json +++ b/qapi/net.json @@ -113,6 +113,119 @@ 'str': 'str' } } ## +# @NetDevPasstOptions: +# +# Unprivileged user-mode network connectivity using passt +# +# @path: Filename of the passt program to run (by default 'passt', and use PATH) +# +# @quiet: don't print informational messages (default, passed as '--quiet') +# +# @vhost-user: enable vhost-user +# +# @mtu: assign MTU via DHCP/NDP +# +# @address: IPv4 or IPv6 address +# +# @netmask: IPv4 mask +# +# @mac: source MAC address +# +# @gateway: IPv4 or IPv6 address as gateway +# +# @interface: interface for addresses and routes +# +# @outbound: bind to address as outbound source +# +# @outbound-if4: bind to outbound interface for IPv4 +# +# @outbound-if6: bind to outbound interface for IPv6 +# +# @dns: IPv4 or IPv6 address as DNS +# +# @search: search domains +# +# @fqdn: FQDN to configure client with +# +# @dhcp-dns: enable/disable DNS list in DHCP/DHCPv6/NDP +# +# @dhcp-search: enable/disable list in DHCP/DHCPv6/NDP +# +# @map-host-loopback: addresse to refer to host +# +# @map-guest-addr: addr to translate to guest's address +# +# @dns-forward: forward DNS queries sent to +# +# @dns-host: host nameserver to direct queries to +# +# @tcp: enable/disable TCP +# +# @udp: enable/disable UDP +# +# @icmp: enable/disable ICMP +# +# @dhcp: enable/disable DHCP +# +# @ndp: enable/disable NDP +# +# @dhcpv6: enable/disable DHCPv6 +# +# @ra: enable/disable route advertisements +# +# @freebind: bind to any address for forwarding +# +# @ipv4: enable/disable IPv4 +# +# @ipv6: enable/disable IPv6 +# +# @tcp-ports: TCP ports to forward +# +# @udp-ports: UDP ports to forward +# +# @param: parameter to pass to passt command +# +# Since: 10.1 +## +{ 'struct': 'NetDevPasstOptions', + 'data': { + '*path': 'str', + '*quiet': 'bool', + '*vhost-user': 'bool', + '*mtu': 'int', + '*address': 'str', + '*netmask': 'str', + '*mac': 'str', + '*gateway': 'str', + '*interface': 'str', + '*outbound': 'str', + '*outbound-if4': 'str', + '*outbound-if6': 'str', + '*dns': 'str', + '*search': ['String'], + '*fqdn': 'str', + '*dhcp-dns': 'bool', + '*dhcp-search': 'bool', + '*map-host-loopback': 'str', + '*map-guest-addr': 'str', + '*dns-forward': 'str', + '*dns-host': 'str', + '*tcp': 'bool', + '*udp': 'bool', + '*icmp': 'bool', + '*dhcp': 'bool', + '*ndp': 'bool', + '*dhcpv6': 'bool', + '*ra': 'bool', + '*freebind': 'bool', + '*ipv4': 'bool', + '*ipv6': 'bool', + '*tcp-ports': ['String'], + '*udp-ports': ['String'], + '*param': ['String'] }, + 'if': 'CONFIG_PASST' } + +## # @NetdevUserOptions: # # Use the user mode network stack which requires no administrator @@ -454,25 +567,34 @@ # (default: 0). # # @inhibit: Don't load a default XDP program, use one already loaded -# to the interface (default: false). Requires @sock-fds. +# to the interface (default: false). Requires @sock-fds or @map-path. # # @sock-fds: A colon (:) separated list of file descriptors for # already open but not bound AF_XDP sockets in the queue order. # One fd per queue. These descriptors should already be added -# into XDP socket map for corresponding queues. Requires -# @inhibit. +# into XDP socket map for corresponding queues. @sock-fds and +# @map-path are mutually exclusive. Requires @inhibit. +# +# @map-path: The path to a pinned xsk map to push file descriptors +# for bound AF_XDP sockets into. @map-path and @sock-fds are +# mutually exclusive. Requires @inhibit. (Since 10.1) +# +# @map-start-index: Use @map-path to insert xsk sockets starting from +# this index number (default: 0). Requires @map-path. (Since 10.1) # # Since: 8.2 ## { 'struct': 'NetdevAFXDPOptions', 'data': { - 'ifname': 'str', - '*mode': 'AFXDPMode', - '*force-copy': 'bool', - '*queues': 'int', - '*start-queue': 'int', - '*inhibit': 'bool', - '*sock-fds': 'str' }, + 'ifname': 'str', + '*mode': 'AFXDPMode', + '*force-copy': 'bool', + '*queues': 'int', + '*start-queue': 'int', + '*inhibit': 'bool', + '*sock-fds': 'str', + '*map-path': 'str', + '*map-start-index': 'int32' }, 'if': 'CONFIG_AF_XDP' } ## @@ -729,12 +851,15 @@ # # @af-xdp: since 8.2 # +# @passt: since 10.1 +# # Since: 2.7 ## { 'enum': 'NetClientDriver', 'data': [ 'none', 'nic', 'user', 'tap', 'l2tpv3', 'socket', 'stream', 'dgram', 'vde', 'bridge', 'hubport', 'netmap', 'vhost-user', 'vhost-vdpa', + { 'name': 'passt', 'if': 'CONFIG_PASST' }, { 'name': 'af-xdp', 'if': 'CONFIG_AF_XDP' }, { 'name': 'vmnet-host', 'if': 'CONFIG_VMNET' }, { 'name': 'vmnet-shared', 'if': 'CONFIG_VMNET' }, @@ -756,6 +881,8 @@ 'discriminator': 'type', 'data': { 'nic': 'NetLegacyNicOptions', + 'passt': { 'type': 'NetDevPasstOptions', + 'if': 'CONFIG_PASST' }, 'user': 'NetdevUserOptions', 'tap': 'NetdevTapOptions', 'l2tpv3': 'NetdevL2TPv3Options', diff --git a/qapi/qom.json b/qapi/qom.json index b133b06..bbdb56d 100644 --- a/qapi/qom.json +++ b/qapi/qom.json @@ -933,6 +933,19 @@ 'if': 'CONFIG_POSIX' } ## +# @IgvmCfgProperties: +# +# Properties common to objects that handle IGVM files. +# +# @file: IGVM file to use to configure guest +# +# Since: 10.1 +## +{ 'struct': 'IgvmCfgProperties', + 'if': 'CONFIG_IGVM', + 'data': { 'file': 'str' } } + +## # @SevCommonProperties: # # Properties common to objects that are derivatives of sev-common. @@ -1142,6 +1155,8 @@ 'filter-redirector', 'filter-replay', 'filter-rewriter', + { 'name': 'igvm-cfg', + 'if': 'CONFIG_IGVM' }, 'input-barrier', { 'name': 'input-linux', 'if': 'CONFIG_LINUX' }, @@ -1218,6 +1233,8 @@ 'filter-redirector': 'FilterRedirectorProperties', 'filter-replay': 'NetfilterProperties', 'filter-rewriter': 'FilterRewriterProperties', + 'igvm-cfg': { 'type': 'IgvmCfgProperties', + 'if': 'CONFIG_IGVM' }, 'input-barrier': 'InputBarrierProperties', 'input-linux': { 'type': 'InputLinuxProperties', 'if': 'CONFIG_LINUX' }, diff --git a/qemu-options.hx b/qemu-options.hx index ac09abf..ab23f14 100644 --- a/qemu-options.hx +++ b/qemu-options.hx @@ -38,6 +38,7 @@ DEF("machine", HAS_ARG, QEMU_OPTION_machine, \ " nvdimm=on|off controls NVDIMM support (default=off)\n" " memory-encryption=@var{} memory encryption object to use (default=none)\n" " hmat=on|off controls ACPI HMAT support (default=off)\n" + " spcr=on|off controls ACPI SPCR support (default=on)\n" #ifdef CONFIG_POSIX " aux-ram-share=on|off allocate auxiliary guest RAM as shared (default: off)\n" #endif @@ -105,6 +106,10 @@ SRST Enables or disables ACPI Heterogeneous Memory Attribute Table (HMAT) support. The default is off. + ``spcr=on|off`` + Enables or disables ACPI Serial Port Console Redirection Table + (SPCR) support. The default is on. + ``aux-ram-share=on|off`` Allocate auxiliary guest RAM as an anonymous file that is shareable with an external process. This option applies to @@ -2809,6 +2814,26 @@ DEFHEADING() DEFHEADING(Network options:) DEF("netdev", HAS_ARG, QEMU_OPTION_netdev, +#ifdef CONFIG_PASST + "-netdev passt,id=str[,path=file][,quiet=on|off][,vhost-user=on|off]\n" + "[,mtu=mtu][,address=addr][,netmask=mask][,mac=addr][,gateway=addr]\n" + " [,interface=name][,outbound=address][,outbound-if4=name]\n" + " [,outbound-if6=name][,dns=addr][,search=list][,fqdn=name]\n" + " [,dhcp-dns=on|off][,dhcp-search=on|off][,map-host-loopback=addr]\n" + " [,map-guest-addr=addr][,dns-forward=addr][,dns-host=addr]\n" + " [,tcp=on|off][,udp=on|off][,icmp=on|off][,dhcp=on|off]\n" + " [,ndp=on|off][,dhcpv6=on|off][,ra=on|off][,freebind=on|off]\n" + " [,ipv4=on|off][,ipv6=on|off][,tcp-ports=spec][,udp-ports=spec]\n" + " [,param=list]\n" + " configure a passt network backend with ID 'str'\n" + " if 'path' is not provided 'passt' will be started according to PATH\n" + " by default, informational message of passt are not displayed (quiet=on)\n" + " to display this message, use 'quiet=off'\n" + " by default, passt will be started in socket-based mode, to enable vhost-mode,\n" + " use 'vhost-user=on'\n" + " for details on other options, refer to passt(1)\n" + " 'param' allows to pass any option defined by passt(1)\n" +#endif #ifdef CONFIG_SLIRP "-netdev user,id=str[,ipv4=on|off][,net=addr[/mask]][,host=addr]\n" " [,ipv6=on|off][,ipv6-net=addr[/int]][,ipv6-host=addr]\n" @@ -2922,6 +2947,7 @@ DEF("netdev", HAS_ARG, QEMU_OPTION_netdev, #ifdef CONFIG_AF_XDP "-netdev af-xdp,id=str,ifname=name[,mode=native|skb][,force-copy=on|off]\n" " [,queues=n][,start-queue=m][,inhibit=on|off][,sock-fds=x:y:...:z]\n" + " [,map-path=/path/to/socket/map][,map-start-index=i]\n" " attach to the existing network interface 'name' with AF_XDP socket\n" " use 'mode=MODE' to specify an XDP program attach mode\n" " use 'force-copy=on|off' to force XDP copy mode even if device supports zero-copy (default: off)\n" @@ -2929,6 +2955,8 @@ DEF("netdev", HAS_ARG, QEMU_OPTION_netdev, " with inhibit=on,\n" " use 'sock-fds' to provide file descriptors for already open AF_XDP sockets\n" " added to a socket map in XDP program. One socket per queue.\n" + " use 'map-path' to provide the socket map location to populate AF_XDP sockets with,\n" + " and use 'map-start-index' to specify the starting index for the map (default: 0) (Since 10.1)\n" " use 'queues=n' to specify how many queues of a multiqueue interface should be used\n" " use 'start-queue=m' to specify the first queue that should be used\n" #endif @@ -2965,6 +2993,9 @@ DEF("netdev", HAS_ARG, QEMU_OPTION_netdev, " configure a hub port on the hub with ID 'n'\n", QEMU_ARCH_ALL) DEF("nic", HAS_ARG, QEMU_OPTION_nic, "-nic [tap|bridge|" +#ifdef CONFIG_PASST + "passt|" +#endif #ifdef CONFIG_SLIRP "user|" #endif @@ -2997,6 +3028,9 @@ DEF("net", HAS_ARG, QEMU_OPTION_net, " configure or create an on-board (or machine default) NIC and\n" " connect it to hub 0 (please use -nic unless you need a hub)\n" "-net [" +#ifdef CONFIG_PASST + "passt|" +#endif #ifdef CONFIG_SLIRP "user|" #endif @@ -3018,7 +3052,7 @@ DEF("net", HAS_ARG, QEMU_OPTION_net, " old way to initialize a host network interface\n" " (use the -netdev option if possible instead)\n", QEMU_ARCH_ALL) SRST -``-nic [tap|bridge|user|l2tpv3|vde|netmap|af-xdp|vhost-user|socket][,...][,mac=macaddr][,model=mn]`` +``-nic [tap|passt|bridge|user|l2tpv3|vde|netmap|af-xdp|vhost-user|socket][,...][,mac=macaddr][,model=mn]`` This option is a shortcut for configuring both the on-board (default) guest NIC hardware and the host network backend in one go. The host backend options are the same as with the corresponding @@ -3040,6 +3074,129 @@ SRST network backend) which is activated if no other networking options are provided. +``-netdev passt,id=str[,option][,...]`` + Configure a passt network backend which requires no administrator + privilege to run. Valid options are: + + ``id=id`` + Assign symbolic name for use in monitor commands. + + ``path=file`` + Filename of the passt program to run. If it is not provided, + passt command will be started with the help of the PATH environment + variable. + + ``quiet=on|off`` + By default, ``quiet=on`` to disable informational message from + passt. ``quiet=on`` is passed as ``--quiet`` to passt. + + ``vhost-user=on|off`` + By default, ``vhost-user=off`` and QEMU uses the stream network + backend to communicate with passt. If ``vhost-user=on``, passt is + started with ``--vhost-user`` and QEMU uses the vhost-user network + backend to communicate with passt. + + ``@mtu`` + Assign MTU via DHCP/NDP + + ``address`` + IPv4 or IPv6 address + + ``netmask`` + IPv4 mask + + ``mac`` + source MAC address + + ``gateway`` + IPv4 or IPv6 address as gateway + + ``interface`` + Interface for addresses and routes + + ``outbound`` + Bind to address as outbound source + + ``outbound-if4`` + Bind to outbound interface for IPv4 + + ``outbound-if6`` + Bind to outbound interface for IPv6 + + ``dns`` + IPv4 or IPv6 address as DNS + + ``search`` + Search domains + + ``fqdn`` + FQDN to configure client with + + ``dhcp-dns`` + Enable/disable DNS list in DHCP/DHCPv6/NDP + + ``dhcp-search`` + Enable/disable list in DHCP/DHCPv6/NDP + + ``map-host-loopback`` + Addresse to refer to host + + ``map-guest-addr`` + Addr to translate to guest's address + + ``dns-forward`` + Forward DNS queries sent to + + ``dns-host`` + Host nameserver to direct queries to + + ``tcp`` + Enable/disable TCP + + ``udp`` + Enable/disable UDP + + ``icmp`` + Enable/disable ICMP + + ``dhcp`` + Enable/disable DHCP + + ``ndp`` + Enable/disable NDP + + ``dhcpv6`` + Enable/disable DHCPv6 + + ``ra`` + Enable/disable route advertisements + + ``freebind`` + Bind to any address for forwarding + + ``ipv4`` + Enable/disable IPv4 + + ``ipv6`` + Enable/disable IPv6 + + ``tcp-ports`` + TCP ports to forward + + ``udp-ports`` + UDP ports to forward + + ``param=string`` + ``string`` will be passed to passt has a command line parameter, + we can have multiple occurences of the ``param`` parameter to + pass multiple parameters to passt. + + For instance, to pass ``--trace --log=trace.log``: + + .. parsed-literal:: + + |qemu_system| -nic passt,param=--trace,param=--log=trace.log + ``-netdev user,id=id[,option][,option][,...]`` Configure user mode host network backend which requires no administrator privilege to run. Valid options are: @@ -3623,7 +3780,7 @@ SRST # launch QEMU instance |qemu_system| linux.img -nic vde,sock=/tmp/myswitch -``-netdev af-xdp,id=str,ifname=name[,mode=native|skb][,force-copy=on|off][,queues=n][,start-queue=m][,inhibit=on|off][,sock-fds=x:y:...:z]`` +``-netdev af-xdp,id=str,ifname=name[,mode=native|skb][,force-copy=on|off][,queues=n][,start-queue=m][,inhibit=on|off][,sock-fds=x:y:...:z][,map-path=/path/to/socket/map][,map-start-index=i]`` Configure AF_XDP backend to connect to a network interface 'name' using AF_XDP socket. A specific program attach mode for a default XDP program can be forced with 'mode', defaults to best-effort, @@ -3663,7 +3820,8 @@ SRST -netdev af-xdp,id=n1,ifname=eth0,queues=1,start-queue=1 XDP program can also be loaded externally. In this case 'inhibit' option - should be set to 'on' and 'sock-fds' provided with file descriptors for + should be set to 'on'. Either 'sock-fds' or 'map-path' can be used with + 'inhibit' enabled. 'sock-fds' can be provided with file descriptors for already open but not bound XDP sockets already added to a socket map for corresponding queues. One socket per queue. @@ -3672,6 +3830,21 @@ SRST |qemu_system| linux.img -device virtio-net-pci,netdev=n1 \\ -netdev af-xdp,id=n1,ifname=eth0,queues=3,inhibit=on,sock-fds=15:16:17 + For the 'inhibit' option set to 'on' used together with 'map-path' it is + expected that the XDP program with the socket map is already loaded on + the networking device and the map pinned into BPF file system. The path + to the pinned map is then passed to QEMU which then creates the file + descriptors and inserts them into the existing socket map. + + .. parsed-literal:: + + |qemu_system| linux.img -device virtio-net-pci,netdev=n1 \\ + -netdev af-xdp,id=n1,ifname=eth0,queues=2,inhibit=on,map-path=/sys/fs/bpf/xsks_map + + Additionally, 'map-start-index' can be used to specify the start offset + for insertion into the socket map. The combination of 'map-path' and + 'sock-fds' together is not supported. + ``-netdev vhost-user,chardev=id[,vhostforce=on|off][,queues=n]`` Establish a vhost-user netdev, backed by a chardev id. The chardev should be a unix domain socket backed one. The vhost-user uses a @@ -3724,7 +3897,7 @@ SRST Use ``-net nic,model=help`` for a list of available devices for your target. -``-net user|tap|bridge|socket|l2tpv3|vde[,...][,name=name]`` +``-net user|passt|tap|bridge|socket|l2tpv3|vde[,...][,name=name]`` Configure a host network backend (with the options corresponding to the same ``-netdev`` option) and connect it to the emulated hub 0 (the default hub). Use name to specify the name of the hub port. @@ -6005,6 +6178,34 @@ SRST -machine ...,memory-encryption=sev0 \\ ..... + ``-object igvm-cfg,file=file`` + Create an IGVM configuration object that defines the initial state + of the guest using a file in that conforms to the Independent Guest + Virtual Machine (IGVM) file format. + + This is currently only supported by ``-machine q35`` and + ``-machine pc``. + + The ``file`` parameter is used to specify the IGVM file to load. + When provided, the IGVM file is used to populate the initial + memory of the virtual machine and, depending on the platform, can + define the initial processor state, memory map and parameters. + + The IGVM file is expected to contain the firmware for the virtual + machine, therefore an ``igvm-cfg`` object cannot be provided along + with other ways of specifying firmware, such as the ``-bios`` + parameter on x86 machines. + + e.g to launch a machine providing the firmware in an IGVM file + + .. parsed-literal:: + + # |qemu_system_x86| \\ + ...... \\ + -object igvm-cfg,id=igvm0,file=bios.igvm \\ + -machine ...,igvm-cfg=igvm0 \\ + ..... + ``-object authz-simple,id=id,identity=string`` Create an authorization object that will control access to network services. diff --git a/rust/qemu-api-macros/meson.build b/rust/qemu-api-macros/meson.build index 8610ce1..2152bcb 100644 --- a/rust/qemu-api-macros/meson.build +++ b/rust/qemu-api-macros/meson.build @@ -17,3 +17,6 @@ _qemu_api_macros_rs = rust.proc_macro( qemu_api_macros = declare_dependency( link_with: _qemu_api_macros_rs, ) + +rust.test('rust-qemu-api-macros-tests', _qemu_api_macros_rs, + suite: ['unit', 'rust']) diff --git a/rust/qemu-api-macros/src/bits.rs b/rust/qemu-api-macros/src/bits.rs index 5ba8475..a80a3b9 100644 --- a/rust/qemu-api-macros/src/bits.rs +++ b/rust/qemu-api-macros/src/bits.rs @@ -6,8 +6,7 @@ use proc_macro2::{ Delimiter, Group, Ident, Punct, Spacing, Span, TokenStream, TokenTree, TokenTree as TT, }; - -use crate::utils::MacroError; +use syn::Error; pub struct BitsConstInternal { typ: TokenTree, @@ -36,27 +35,21 @@ impl BitsConstInternal { tok: TokenTree, it: &mut dyn Iterator<Item = TokenTree>, out: &mut TokenStream, - ) -> Result<Option<TokenTree>, MacroError> { + ) -> Result<Option<TokenTree>, Error> { let next = match tok { TT::Group(ref g) => { if g.delimiter() != Delimiter::Parenthesis && g.delimiter() != Delimiter::None { - return Err(MacroError::Message("expected parenthesis".into(), g.span())); + return Err(Error::new(g.span(), "expected parenthesis")); } let mut stream = g.stream().into_iter(); let Some(first_tok) = stream.next() else { - return Err(MacroError::Message( - "expected operand, found ')'".into(), - g.span(), - )); + return Err(Error::new(g.span(), "expected operand, found ')'")); }; let mut output = TokenStream::new(); // start from the lowest precedence let next = self.parse_or(first_tok, &mut stream, &mut output)?; if let Some(tok) = next { - return Err(MacroError::Message( - format!("unexpected token {tok}"), - tok.span(), - )); + return Err(Error::new(tok.span(), format!("unexpected token {tok}"))); } out.extend(Some(paren(output))); it.next() @@ -74,20 +67,17 @@ impl BitsConstInternal { } TT::Punct(ref p) => { if p.as_char() != '!' { - return Err(MacroError::Message("expected operand".into(), p.span())); + return Err(Error::new(p.span(), "expected operand")); } let Some(rhs_tok) = it.next() else { - return Err(MacroError::Message( - "expected operand at end of input".into(), - p.span(), - )); + return Err(Error::new(p.span(), "expected operand at end of input")); }; let next = self.parse_primary(rhs_tok, it, out)?; out.extend([punct('.'), ident("invert"), paren(TokenStream::new())]); next } _ => { - return Err(MacroError::Message("unexpected literal".into(), tok.span())); + return Err(Error::new(tok.span(), "unexpected literal")); } }; Ok(next) @@ -99,7 +89,7 @@ impl BitsConstInternal { TokenTree, &mut dyn Iterator<Item = TokenTree>, &mut TokenStream, - ) -> Result<Option<TokenTree>, MacroError>, + ) -> Result<Option<TokenTree>, Error>, >( &self, tok: TokenTree, @@ -108,7 +98,7 @@ impl BitsConstInternal { ch: char, f: F, method: &'static str, - ) -> Result<Option<TokenTree>, MacroError> { + ) -> Result<Option<TokenTree>, Error> { let mut next = f(self, tok, it, out)?; while next.is_some() { let op = next.as_ref().unwrap(); @@ -118,10 +108,7 @@ impl BitsConstInternal { } let Some(rhs_tok) = it.next() else { - return Err(MacroError::Message( - "expected operand at end of input".into(), - p.span(), - )); + return Err(Error::new(p.span(), "expected operand at end of input")); }; let mut rhs = TokenStream::new(); next = f(self, rhs_tok, it, &mut rhs)?; @@ -136,7 +123,7 @@ impl BitsConstInternal { tok: TokenTree, it: &mut dyn Iterator<Item = TokenTree>, out: &mut TokenStream, - ) -> Result<Option<TokenTree>, MacroError> { + ) -> Result<Option<TokenTree>, Error> { self.parse_binop(tok, it, out, '-', Self::parse_primary, "difference") } @@ -146,7 +133,7 @@ impl BitsConstInternal { tok: TokenTree, it: &mut dyn Iterator<Item = TokenTree>, out: &mut TokenStream, - ) -> Result<Option<TokenTree>, MacroError> { + ) -> Result<Option<TokenTree>, Error> { self.parse_binop(tok, it, out, '&', Self::parse_sub, "intersection") } @@ -156,7 +143,7 @@ impl BitsConstInternal { tok: TokenTree, it: &mut dyn Iterator<Item = TokenTree>, out: &mut TokenStream, - ) -> Result<Option<TokenTree>, MacroError> { + ) -> Result<Option<TokenTree>, Error> { self.parse_binop(tok, it, out, '^', Self::parse_and, "symmetric_difference") } @@ -166,13 +153,13 @@ impl BitsConstInternal { tok: TokenTree, it: &mut dyn Iterator<Item = TokenTree>, out: &mut TokenStream, - ) -> Result<Option<TokenTree>, MacroError> { + ) -> Result<Option<TokenTree>, Error> { self.parse_binop(tok, it, out, '|', Self::parse_xor, "union") } pub fn parse( it: &mut dyn Iterator<Item = TokenTree>, - ) -> Result<proc_macro2::TokenStream, MacroError> { + ) -> Result<proc_macro2::TokenStream, Error> { let mut pos = Span::call_site(); let mut typ = proc_macro2::TokenStream::new(); @@ -198,15 +185,15 @@ impl BitsConstInternal { }; let Some(tok) = next else { - return Err(MacroError::Message( - "expected expression, do not call this macro directly".into(), + return Err(Error::new( pos, + "expected expression, do not call this macro directly", )); }; let TT::Group(ref _group) = tok else { - return Err(MacroError::Message( - "expected parenthesis, do not call this macro directly".into(), + return Err(Error::new( tok.span(), + "expected parenthesis, do not call this macro directly", )); }; let mut out = TokenStream::new(); @@ -219,10 +206,7 @@ impl BitsConstInternal { // A parenthesized expression is a single production of the grammar, // so the input must have reached the last token. if let Some(tok) = next { - return Err(MacroError::Message( - format!("unexpected token {tok}"), - tok.span(), - )); + return Err(Error::new(tok.span(), format!("unexpected token {tok}"))); } Ok(out) } diff --git a/rust/qemu-api-macros/src/lib.rs b/rust/qemu-api-macros/src/lib.rs index c18bb4e..b525d89 100644 --- a/rust/qemu-api-macros/src/lib.rs +++ b/rust/qemu-api-macros/src/lib.rs @@ -6,83 +6,82 @@ use proc_macro::TokenStream; use quote::quote; use syn::{ parse_macro_input, parse_quote, punctuated::Punctuated, spanned::Spanned, token::Comma, Data, - DeriveInput, Field, Fields, FieldsUnnamed, Ident, Meta, Path, Token, Variant, + DeriveInput, Error, Field, Fields, FieldsUnnamed, Ident, Meta, Path, Token, Variant, }; - -mod utils; -use utils::MacroError; - mod bits; use bits::BitsConstInternal; +#[cfg(test)] +mod tests; + fn get_fields<'a>( input: &'a DeriveInput, msg: &str, -) -> Result<&'a Punctuated<Field, Comma>, MacroError> { +) -> Result<&'a Punctuated<Field, Comma>, Error> { let Data::Struct(ref s) = &input.data else { - return Err(MacroError::Message( - format!("Struct required for {msg}"), + return Err(Error::new( input.ident.span(), + format!("Struct required for {msg}"), )); }; let Fields::Named(ref fs) = &s.fields else { - return Err(MacroError::Message( - format!("Named fields required for {msg}"), + return Err(Error::new( input.ident.span(), + format!("Named fields required for {msg}"), )); }; Ok(&fs.named) } -fn get_unnamed_field<'a>(input: &'a DeriveInput, msg: &str) -> Result<&'a Field, MacroError> { +fn get_unnamed_field<'a>(input: &'a DeriveInput, msg: &str) -> Result<&'a Field, Error> { let Data::Struct(ref s) = &input.data else { - return Err(MacroError::Message( - format!("Struct required for {msg}"), + return Err(Error::new( input.ident.span(), + format!("Struct required for {msg}"), )); }; let Fields::Unnamed(FieldsUnnamed { ref unnamed, .. }) = &s.fields else { - return Err(MacroError::Message( - format!("Tuple struct required for {msg}"), + return Err(Error::new( s.fields.span(), + format!("Tuple struct required for {msg}"), )); }; if unnamed.len() != 1 { - return Err(MacroError::Message( - format!("A single field is required for {msg}"), + return Err(Error::new( s.fields.span(), + format!("A single field is required for {msg}"), )); } Ok(&unnamed[0]) } -fn is_c_repr(input: &DeriveInput, msg: &str) -> Result<(), MacroError> { +fn is_c_repr(input: &DeriveInput, msg: &str) -> Result<(), Error> { let expected = parse_quote! { #[repr(C)] }; if input.attrs.iter().any(|attr| attr == &expected) { Ok(()) } else { - Err(MacroError::Message( - format!("#[repr(C)] required for {msg}"), + Err(Error::new( input.ident.span(), + format!("#[repr(C)] required for {msg}"), )) } } -fn is_transparent_repr(input: &DeriveInput, msg: &str) -> Result<(), MacroError> { +fn is_transparent_repr(input: &DeriveInput, msg: &str) -> Result<(), Error> { let expected = parse_quote! { #[repr(transparent)] }; if input.attrs.iter().any(|attr| attr == &expected) { Ok(()) } else { - Err(MacroError::Message( - format!("#[repr(transparent)] required for {msg}"), + Err(Error::new( input.ident.span(), + format!("#[repr(transparent)] required for {msg}"), )) } } -fn derive_object_or_error(input: DeriveInput) -> Result<proc_macro2::TokenStream, MacroError> { +fn derive_object_or_error(input: DeriveInput) -> Result<proc_macro2::TokenStream, Error> { is_c_repr(&input, "#[derive(Object)]")?; let name = &input.ident; @@ -103,12 +102,13 @@ fn derive_object_or_error(input: DeriveInput) -> Result<proc_macro2::TokenStream #[proc_macro_derive(Object)] pub fn derive_object(input: TokenStream) -> TokenStream { let input = parse_macro_input!(input as DeriveInput); - let expanded = derive_object_or_error(input).unwrap_or_else(Into::into); - TokenStream::from(expanded) + derive_object_or_error(input) + .unwrap_or_else(syn::Error::into_compile_error) + .into() } -fn derive_opaque_or_error(input: DeriveInput) -> Result<proc_macro2::TokenStream, MacroError> { +fn derive_opaque_or_error(input: DeriveInput) -> Result<proc_macro2::TokenStream, Error> { is_transparent_repr(&input, "#[derive(Wrapper)]")?; let name = &input.ident; @@ -149,13 +149,14 @@ fn derive_opaque_or_error(input: DeriveInput) -> Result<proc_macro2::TokenStream #[proc_macro_derive(Wrapper)] pub fn derive_opaque(input: TokenStream) -> TokenStream { let input = parse_macro_input!(input as DeriveInput); - let expanded = derive_opaque_or_error(input).unwrap_or_else(Into::into); - TokenStream::from(expanded) + derive_opaque_or_error(input) + .unwrap_or_else(syn::Error::into_compile_error) + .into() } #[allow(non_snake_case)] -fn get_repr_uN(input: &DeriveInput, msg: &str) -> Result<Path, MacroError> { +fn get_repr_uN(input: &DeriveInput, msg: &str) -> Result<Path, Error> { let repr = input.attrs.iter().find(|attr| attr.path().is_ident("repr")); if let Some(repr) = repr { let nested = repr.parse_args_with(Punctuated::<Meta, Token![,]>::parse_terminated)?; @@ -170,23 +171,23 @@ fn get_repr_uN(input: &DeriveInput, msg: &str) -> Result<Path, MacroError> { } } - Err(MacroError::Message( - format!("#[repr(u8/u16/u32/u64) required for {msg}"), + Err(Error::new( input.ident.span(), + format!("#[repr(u8/u16/u32/u64) required for {msg}"), )) } -fn get_variants(input: &DeriveInput) -> Result<&Punctuated<Variant, Comma>, MacroError> { +fn get_variants(input: &DeriveInput) -> Result<&Punctuated<Variant, Comma>, Error> { let Data::Enum(ref e) = &input.data else { - return Err(MacroError::Message( - "Cannot derive TryInto for union or struct.".to_string(), + return Err(Error::new( input.ident.span(), + "Cannot derive TryInto for union or struct.", )); }; if let Some(v) = e.variants.iter().find(|v| v.fields != Fields::Unit) { - return Err(MacroError::Message( - "Cannot derive TryInto for enum with non-unit variants.".to_string(), + return Err(Error::new( v.fields.span(), + "Cannot derive TryInto for enum with non-unit variants.", )); } Ok(&e.variants) @@ -197,11 +198,11 @@ fn derive_tryinto_body( name: &Ident, variants: &Punctuated<Variant, Comma>, repr: &Path, -) -> Result<proc_macro2::TokenStream, MacroError> { +) -> Result<proc_macro2::TokenStream, Error> { let discriminants: Vec<&Ident> = variants.iter().map(|f| &f.ident).collect(); Ok(quote! { - #(const #discriminants: #repr = #name::#discriminants as #repr;)*; + #(const #discriminants: #repr = #name::#discriminants as #repr;)* match value { #(#discriminants => core::result::Result::Ok(#name::#discriminants),)* _ => core::result::Result::Err(value), @@ -210,7 +211,7 @@ fn derive_tryinto_body( } #[rustfmt::skip::macros(quote)] -fn derive_tryinto_or_error(input: DeriveInput) -> Result<proc_macro2::TokenStream, MacroError> { +fn derive_tryinto_or_error(input: DeriveInput) -> Result<proc_macro2::TokenStream, Error> { let repr = get_repr_uN(&input, "#[derive(TryInto)]")?; let name = &input.ident; let body = derive_tryinto_body(name, get_variants(&input)?, &repr)?; @@ -229,7 +230,7 @@ fn derive_tryinto_or_error(input: DeriveInput) -> Result<proc_macro2::TokenStrea #body }) { Ok(x) => x, - Err(_) => panic!(#errmsg) + Err(_) => panic!(#errmsg), } } } @@ -247,9 +248,10 @@ fn derive_tryinto_or_error(input: DeriveInput) -> Result<proc_macro2::TokenStrea #[proc_macro_derive(TryInto)] pub fn derive_tryinto(input: TokenStream) -> TokenStream { let input = parse_macro_input!(input as DeriveInput); - let expanded = derive_tryinto_or_error(input).unwrap_or_else(Into::into); - TokenStream::from(expanded) + derive_tryinto_or_error(input) + .unwrap_or_else(syn::Error::into_compile_error) + .into() } #[proc_macro] @@ -257,6 +259,7 @@ pub fn bits_const_internal(ts: TokenStream) -> TokenStream { let ts = proc_macro2::TokenStream::from(ts); let mut it = ts.into_iter(); - let expanded = BitsConstInternal::parse(&mut it).unwrap_or_else(Into::into); - TokenStream::from(expanded) + BitsConstInternal::parse(&mut it) + .unwrap_or_else(syn::Error::into_compile_error) + .into() } diff --git a/rust/qemu-api-macros/src/tests.rs b/rust/qemu-api-macros/src/tests.rs new file mode 100644 index 0000000..d6dcd62 --- /dev/null +++ b/rust/qemu-api-macros/src/tests.rs @@ -0,0 +1,137 @@ +// Copyright 2025, Linaro Limited +// Author(s): Manos Pitsidianakis <manos.pitsidianakis@linaro.org> +// SPDX-License-Identifier: GPL-2.0-or-later + +use quote::quote; + +use super::*; + +macro_rules! derive_compile_fail { + ($derive_fn:ident, $input:expr, $error_msg:expr) => {{ + let input: proc_macro2::TokenStream = $input; + let error_msg: &str = $error_msg; + let derive_fn: fn(input: syn::DeriveInput) -> Result<proc_macro2::TokenStream, syn::Error> = + $derive_fn; + + let input: syn::DeriveInput = syn::parse2(input).unwrap(); + let result = derive_fn(input); + let err = result.unwrap_err().into_compile_error(); + assert_eq!( + err.to_string(), + quote! { ::core::compile_error! { #error_msg } }.to_string() + ); + }}; +} + +macro_rules! derive_compile { + ($derive_fn:ident, $input:expr, $($expected:tt)*) => {{ + let input: proc_macro2::TokenStream = $input; + let expected: proc_macro2::TokenStream = $($expected)*; + let derive_fn: fn(input: syn::DeriveInput) -> Result<proc_macro2::TokenStream, syn::Error> = + $derive_fn; + + let input: syn::DeriveInput = syn::parse2(input).unwrap(); + let result = derive_fn(input).unwrap(); + assert_eq!(result.to_string(), expected.to_string()); + }}; +} + +#[test] +fn test_derive_object() { + derive_compile_fail!( + derive_object_or_error, + quote! { + #[derive(Object)] + struct Foo { + _unused: [u8; 0], + } + }, + "#[repr(C)] required for #[derive(Object)]" + ); + derive_compile!( + derive_object_or_error, + quote! { + #[derive(Object)] + #[repr(C)] + struct Foo { + _unused: [u8; 0], + } + }, + quote! { + ::qemu_api::assert_field_type!( + Foo, + _unused, + ::qemu_api::qom::ParentField<<Foo as ::qemu_api::qom::ObjectImpl>::ParentType> + ); + ::qemu_api::module_init! { + MODULE_INIT_QOM => unsafe { + ::qemu_api::bindings::type_register_static(&<Foo as ::qemu_api::qom::ObjectImpl>::TYPE_INFO); + } + } + } + ); +} + +#[test] +fn test_derive_tryinto() { + derive_compile_fail!( + derive_tryinto_or_error, + quote! { + #[derive(TryInto)] + struct Foo { + _unused: [u8; 0], + } + }, + "#[repr(u8/u16/u32/u64) required for #[derive(TryInto)]" + ); + derive_compile!( + derive_tryinto_or_error, + quote! { + #[derive(TryInto)] + #[repr(u8)] + enum Foo { + First = 0, + Second, + } + }, + quote! { + impl Foo { + #[allow(dead_code)] + pub const fn into_bits(self) -> u8 { + self as u8 + } + + #[allow(dead_code)] + pub const fn from_bits(value: u8) -> Self { + match ({ + const First: u8 = Foo::First as u8; + const Second: u8 = Foo::Second as u8; + match value { + First => core::result::Result::Ok(Foo::First), + Second => core::result::Result::Ok(Foo::Second), + _ => core::result::Result::Err(value), + } + }) { + Ok(x) => x, + Err(_) => panic!("invalid value for Foo"), + } + } + } + + impl core::convert::TryFrom<u8> for Foo { + type Error = u8; + + #[allow(ambiguous_associated_items)] + fn try_from(value: u8) -> Result<Self, u8> { + const First: u8 = Foo::First as u8; + const Second: u8 = Foo::Second as u8; + match value { + First => core::result::Result::Ok(Foo::First), + Second => core::result::Result::Ok(Foo::Second), + _ => core::result::Result::Err(value), + } + } + } + } + ); +} diff --git a/rust/qemu-api-macros/src/utils.rs b/rust/qemu-api-macros/src/utils.rs deleted file mode 100644 index 02c91ae..0000000 --- a/rust/qemu-api-macros/src/utils.rs +++ /dev/null @@ -1,26 +0,0 @@ -// Procedural macro utilities. -// Author(s): Paolo Bonzini <pbonzini@redhat.com> -// SPDX-License-Identifier: GPL-2.0-or-later - -use proc_macro2::Span; -use quote::quote_spanned; - -pub enum MacroError { - Message(String, Span), - ParseError(syn::Error), -} - -impl From<syn::Error> for MacroError { - fn from(err: syn::Error) -> Self { - MacroError::ParseError(err) - } -} - -impl From<MacroError> for proc_macro2::TokenStream { - fn from(err: MacroError) -> Self { - match err { - MacroError::Message(msg, span) => quote_spanned! { span => compile_error!(#msg); }, - MacroError::ParseError(err) => err.into_compile_error(), - } - } -} diff --git a/rust/qemu-api/build.rs b/rust/qemu-api/build.rs index 7849486..29d0945 100644 --- a/rust/qemu-api/build.rs +++ b/rust/qemu-api/build.rs @@ -9,12 +9,14 @@ use std::os::windows::fs::symlink_file; use std::{env, fs::remove_file, io::Result, path::Path}; fn main() -> Result<()> { - // Placing bindings.inc.rs in the source directory is supported - // but not documented or encouraged. - let path = env::var("MESON_BUILD_ROOT") - .unwrap_or_else(|_| format!("{}/src", env!("CARGO_MANIFEST_DIR"))); + let file = if let Ok(root) = env::var("MESON_BUILD_ROOT") { + format!("{root}/rust/qemu-api/bindings.inc.rs") + } else { + // Placing bindings.inc.rs in the source directory is supported + // but not documented or encouraged. + format!("{}/src/bindings.inc.rs", env!("CARGO_MANIFEST_DIR")) + }; - let file = format!("{path}/rust/qemu-api/bindings.inc.rs"); let file = Path::new(&file); if !Path::new(&file).exists() { panic!(concat!( diff --git a/rust/qemu-api/src/bindings.rs b/rust/qemu-api/src/bindings.rs index 057de4b..b8104de 100644 --- a/rust/qemu-api/src/bindings.rs +++ b/rust/qemu-api/src/bindings.rs @@ -6,6 +6,7 @@ non_camel_case_types, non_snake_case, non_upper_case_globals, + unnecessary_transmutes, unsafe_op_in_unsafe_fn, clippy::pedantic, clippy::restriction, @@ -13,7 +14,8 @@ clippy::missing_const_for_fn, clippy::ptr_offset_with_cast, clippy::useless_transmute, - clippy::missing_safety_doc + clippy::missing_safety_doc, + clippy::too_many_arguments )] //! `bindgen`-generated declarations. diff --git a/rust/qemu-api/src/log.rs b/rust/qemu-api/src/log.rs index d6c3d6c..a441b8c 100644 --- a/rust/qemu-api/src/log.rs +++ b/rust/qemu-api/src/log.rs @@ -3,6 +3,13 @@ //! Bindings for QEMU's logging infrastructure +use std::{ + io::{self, Write}, + ptr::NonNull, +}; + +use crate::{bindings, errno}; + #[repr(u32)] /// Represents specific error categories within QEMU's logging system. /// @@ -11,11 +18,82 @@ pub enum Log { /// Log invalid access caused by the guest. /// Corresponds to `LOG_GUEST_ERROR` in the C implementation. - GuestError = crate::bindings::LOG_GUEST_ERROR, + GuestError = bindings::LOG_GUEST_ERROR, /// Log guest access of unimplemented functionality. /// Corresponds to `LOG_UNIMP` in the C implementation. - Unimp = crate::bindings::LOG_UNIMP, + Unimp = bindings::LOG_UNIMP, +} + +/// A RAII guard for QEMU's logging infrastructure. Creating the guard +/// locks the log file, and dropping it (letting it go out of scope) unlocks +/// the file. +/// +/// As long as the guard lives, it can be written to using [`std::io::Write`]. +/// +/// The locking is recursive, therefore owning a guard does not prevent +/// using [`log_mask_ln!()`](crate::log_mask_ln). +pub struct LogGuard(NonNull<bindings::FILE>); + +impl LogGuard { + /// Return a RAII guard that writes to QEMU's logging infrastructure. + /// The log file is locked while the guard exists, ensuring that there + /// is no tearing of the messages. + /// + /// Return `None` if the log file is closed and could not be opened. + /// Do *not* use `unwrap()` on the result; failure can be handled simply + /// by not logging anything. + /// + /// # Examples + /// + /// ``` + /// # use qemu_api::log::LogGuard; + /// # use std::io::Write; + /// if let Some(mut log) = LogGuard::new() { + /// writeln!(log, "test"); + /// } + /// ``` + pub fn new() -> Option<Self> { + let f = unsafe { bindings::qemu_log_trylock() }.cast(); + NonNull::new(f).map(Self) + } + + /// Writes a formatted string into the log, returning any error encountered. + /// + /// This method is primarily used by the + /// [`log_mask_ln!()`](crate::log_mask_ln) macro, and it is rare for it + /// to be called explicitly. It is public because it is the only way to + /// examine the error, which `log_mask_ln!()` ignores + /// + /// Unlike `log_mask_ln!()`, it does *not* append a newline at the end. + pub fn log_fmt(args: std::fmt::Arguments) -> io::Result<()> { + if let Some(mut log) = Self::new() { + log.write_fmt(args)?; + } + Ok(()) + } +} + +impl Write for LogGuard { + fn write(&mut self, bytes: &[u8]) -> io::Result<usize> { + let ret = unsafe { + bindings::rust_fwrite(bytes.as_ptr().cast(), 1, bytes.len(), self.0.as_ptr()) + }; + errno::into_io_result(ret) + } + + fn flush(&mut self) -> io::Result<()> { + // Do nothing, dropping the guard takes care of flushing + Ok(()) + } +} + +impl Drop for LogGuard { + fn drop(&mut self) { + unsafe { + bindings::qemu_log_unlock(self.0.as_ptr()); + } + } } /// A macro to log messages conditionally based on a provided mask. @@ -24,6 +102,8 @@ pub enum Log { /// log level and, if so, formats and logs the message. It is the Rust /// counterpart of the `qemu_log_mask()` macro in the C implementation. /// +/// Errors from writing to the log are ignored. +/// /// # Parameters /// /// - `$mask`: A log level mask. This should be a variant of the `Log` enum. @@ -62,12 +142,8 @@ macro_rules! log_mask_ln { if unsafe { (::qemu_api::bindings::qemu_loglevel & ($mask as std::os::raw::c_int)) != 0 } { - let formatted_string = format!("{}\n", format_args!($fmt $($args)*)); - let c_string = std::ffi::CString::new(formatted_string).unwrap(); - - unsafe { - ::qemu_api::bindings::qemu_log(c_string.as_ptr()); - } + _ = ::qemu_api::log::LogGuard::log_fmt( + format_args!("{}\n", format_args!($fmt $($args)*))); } }}; } diff --git a/scripts/meson-buildoptions.sh b/scripts/meson-buildoptions.sh index 73e0770..e850468 100644 --- a/scripts/meson-buildoptions.sh +++ b/scripts/meson-buildoptions.sh @@ -130,6 +130,7 @@ meson_options_help() { printf "%s\n" ' hv-balloon hv-balloon driver (requires Glib 2.68+ GTree API)' printf "%s\n" ' hvf HVF acceleration support' printf "%s\n" ' iconv Font glyph conversion support' + printf "%s\n" ' igvm IGVM file support' printf "%s\n" ' jack JACK sound support' printf "%s\n" ' keyring Linux keyring support' printf "%s\n" ' kvm KVM acceleration support' @@ -162,6 +163,7 @@ meson_options_help() { printf "%s\n" ' oss OSS sound support' printf "%s\n" ' pa PulseAudio sound support' printf "%s\n" ' parallels parallels image format support' + printf "%s\n" ' passt passt network backend support' printf "%s\n" ' pipewire PipeWire sound support' printf "%s\n" ' pixman pixman support' printf "%s\n" ' plugins TCG plugins via shared library loading' @@ -346,6 +348,8 @@ _meson_option_parse() { --iasl=*) quote_sh "-Diasl=$2" ;; --enable-iconv) printf "%s" -Diconv=enabled ;; --disable-iconv) printf "%s" -Diconv=disabled ;; + --enable-igvm) printf "%s" -Digvm=enabled ;; + --disable-igvm) printf "%s" -Digvm=disabled ;; --includedir=*) quote_sh "-Dincludedir=$2" ;; --enable-install-blobs) printf "%s" -Dinstall_blobs=true ;; --disable-install-blobs) printf "%s" -Dinstall_blobs=false ;; @@ -422,6 +426,8 @@ _meson_option_parse() { --disable-pa) printf "%s" -Dpa=disabled ;; --enable-parallels) printf "%s" -Dparallels=enabled ;; --disable-parallels) printf "%s" -Dparallels=disabled ;; + --enable-passt) printf "%s" -Dpasst=enabled ;; + --disable-passt) printf "%s" -Dpasst=disabled ;; --enable-pipewire) printf "%s" -Dpipewire=enabled ;; --disable-pipewire) printf "%s" -Dpipewire=disabled ;; --enable-pixman) printf "%s" -Dpixman=enabled ;; diff --git a/system/memory.c b/system/memory.c index e8d9b15..38da62f 100644 --- a/system/memory.c +++ b/system/memory.c @@ -22,6 +22,7 @@ #include "qemu/error-report.h" #include "qemu/main-loop.h" #include "qemu/qemu-print.h" +#include "qemu/target-info.h" #include "qom/object.h" #include "trace.h" #include "system/ram_addr.h" diff --git a/system/qtest.c b/system/qtest.c index 301b03b..fa42c9f 100644 --- a/system/qtest.c +++ b/system/qtest.c @@ -29,6 +29,7 @@ #include "qemu/error-report.h" #include "qemu/module.h" #include "qemu/cutils.h" +#include "qemu/target-info.h" #include "qom/object_interfaces.h" #define MAX_IRQ 256 diff --git a/system/runstate.c b/system/runstate.c index 38900c9..e18eb8c 100644 --- a/system/runstate.c +++ b/system/runstate.c @@ -437,6 +437,7 @@ static ShutdownCause reset_requested; static ShutdownCause shutdown_requested; static int shutdown_exit_code = EXIT_SUCCESS; static int shutdown_signal; +static bool force_shutdown; static pid_t shutdown_pid; static int powerdown_requested; static int debug_requested; @@ -457,6 +458,11 @@ ShutdownCause qemu_shutdown_requested_get(void) return shutdown_requested; } +bool qemu_force_shutdown_requested(void) +{ + return force_shutdown; +} + ShutdownCause qemu_reset_requested_get(void) { return reset_requested; @@ -805,6 +811,7 @@ void qemu_system_killed(int signal, pid_t pid) * we are in a signal handler. */ shutdown_requested = SHUTDOWN_CAUSE_HOST_SIGNAL; + force_shutdown = true; qemu_notify_event(); } @@ -820,6 +827,9 @@ void qemu_system_shutdown_request(ShutdownCause reason) trace_qemu_system_shutdown_request(reason); replay_shutdown_request(reason); shutdown_requested = reason; + if (reason == SHUTDOWN_CAUSE_HOST_QMP_QUIT) { + force_shutdown = true; + } qemu_notify_event(); } diff --git a/target-info-stub.c b/target-info-stub.c index fecc0e7..ca0caa3 100644 --- a/target-info-stub.c +++ b/target-info-stub.c @@ -14,9 +14,11 @@ static const TargetInfo target_info_stub = { .target_name = TARGET_NAME, + .target_arch = SYS_EMU_TARGET__MAX, .long_bits = TARGET_LONG_BITS, .cpu_type = CPU_RESOLVING_TYPE, .machine_typename = TYPE_MACHINE, + .endianness = TARGET_BIG_ENDIAN ? ENDIAN_MODE_BIG : ENDIAN_MODE_LITTLE, }; const TargetInfo *target_info(void) diff --git a/target-info.c b/target-info.c index 16fdca7..3110ab3 100644 --- a/target-info.c +++ b/target-info.c @@ -8,7 +8,9 @@ #include "qemu/osdep.h" #include "qemu/target-info.h" +#include "qemu/target-info-qapi.h" #include "qemu/target-info-impl.h" +#include "qapi/error.h" const char *target_name(void) { @@ -20,6 +22,17 @@ unsigned target_long_bits(void) return target_info()->long_bits; } +SysEmuTarget target_arch(void) +{ + SysEmuTarget arch = target_info()->target_arch; + + if (arch == SYS_EMU_TARGET__MAX) { + arch = qapi_enum_parse(&SysEmuTarget_lookup, target_name(), -1, + &error_abort); + } + return arch; +} + const char *target_cpu_type(void) { return target_info()->cpu_type; @@ -29,3 +42,13 @@ const char *target_machine_typename(void) { return target_info()->machine_typename; } + +EndianMode target_endian_mode(void) +{ + return target_info()->endianness; +} + +bool target_big_endian(void) +{ + return target_endian_mode() == ENDIAN_MODE_BIG; +} diff --git a/target/alpha/cpu.c b/target/alpha/cpu.c index 2082db4..bf1787a 100644 --- a/target/alpha/cpu.c +++ b/target/alpha/cpu.c @@ -286,6 +286,7 @@ static void alpha_cpu_class_init(ObjectClass *oc, const void *data) cc->get_pc = alpha_cpu_get_pc; cc->gdb_read_register = alpha_cpu_gdb_read_register; cc->gdb_write_register = alpha_cpu_gdb_write_register; + cc->gdb_core_xml_file = "alpha-core.xml"; #ifndef CONFIG_USER_ONLY dc->vmsd = &vmstate_alpha_cpu; cc->sysemu_ops = &alpha_sysemu_ops; diff --git a/target/arm/arm-qmp-cmds.c b/target/arm/arm-qmp-cmds.c index cefd235..d292c97 100644 --- a/target/arm/arm-qmp-cmds.c +++ b/target/arm/arm-qmp-cmds.c @@ -21,6 +21,7 @@ */ #include "qemu/osdep.h" +#include "qemu/target-info.h" #include "hw/boards.h" #include "kvm_arm.h" #include "qapi/error.h" @@ -241,7 +242,7 @@ CpuDefinitionInfoList *qmp_query_cpu_definitions(Error **errp) CpuDefinitionInfoList *cpu_list = NULL; GSList *list; - list = object_class_get_list(TYPE_ARM_CPU, false); + list = object_class_get_list(target_cpu_type(), false); g_slist_foreach(list, arm_cpu_add_definition, &cpu_list); g_slist_free(list); diff --git a/target/arm/cpu.c b/target/arm/cpu.c index 08c43f6..e2b2337 100644 --- a/target/arm/cpu.c +++ b/target/arm/cpu.c @@ -23,7 +23,6 @@ #include "qemu/timer.h" #include "qemu/log.h" #include "exec/page-vary.h" -#include "exec/tswap.h" #include "target/arm/idau.h" #include "qemu/module.h" #include "qapi/error.h" diff --git a/target/i386/cpu.c b/target/i386/cpu.c index 0d35e95..da7d8dc 100644 --- a/target/i386/cpu.c +++ b/target/i386/cpu.c @@ -28,6 +28,7 @@ #include "system/hvf.h" #include "hvf/hvf-i386.h" #include "kvm/kvm_i386.h" +#include "kvm/tdx.h" #include "sev.h" #include "qapi/error.h" #include "qemu/error-report.h" @@ -44,6 +45,7 @@ #include "hw/boards.h" #include "hw/i386/sgx-epc.h" #endif +#include "system/qtest.h" #include "tcg/tcg-cpu.h" #include "disas/capstone.h" @@ -66,6 +68,7 @@ struct CPUID2CacheDescriptorInfo { /* * Known CPUID 2 cache descriptors. + * TLB, prefetch and sectored cache related descriptors are not included. * From Intel SDM Volume 2A, CPUID instruction */ struct CPUID2CacheDescriptorInfo cpuid2_cache_descriptors[] = { @@ -87,18 +90,29 @@ struct CPUID2CacheDescriptorInfo cpuid2_cache_descriptors[] = { .associativity = 2, .line_size = 64, }, [0x21] = { .level = 2, .type = UNIFIED_CACHE, .size = 256 * KiB, .associativity = 8, .line_size = 64, }, - /* lines per sector is not supported cpuid2_cache_descriptor(), - * so descriptors 0x22, 0x23 are not included - */ + /* + * lines per sector is not supported cpuid2_cache_descriptor(), + * so descriptors 0x22, 0x23 are not included + */ [0x24] = { .level = 2, .type = UNIFIED_CACHE, .size = 1 * MiB, .associativity = 16, .line_size = 64, }, - /* lines per sector is not supported cpuid2_cache_descriptor(), - * so descriptors 0x25, 0x20 are not included - */ + /* + * lines per sector is not supported cpuid2_cache_descriptor(), + * so descriptors 0x25, 0x29 are not included + */ [0x2C] = { .level = 1, .type = DATA_CACHE, .size = 32 * KiB, .associativity = 8, .line_size = 64, }, [0x30] = { .level = 1, .type = INSTRUCTION_CACHE, .size = 32 * KiB, .associativity = 8, .line_size = 64, }, + /* + * Newer Intel CPUs (having the cores without L3, e.g., Intel MTL, ARL) + * use CPUID 0x4 leaf to describe cache topology, by encoding CPUID 0x2 + * leaf with 0xFF. For older CPUs (without 0x4 leaf), it's also valid + * to just ignore L3's code if there's no L3. + * + * This already covers all the cases in QEMU, so code 0x40 is not + * included. + */ [0x41] = { .level = 2, .type = UNIFIED_CACHE, .size = 128 * KiB, .associativity = 4, .line_size = 32, }, [0x42] = { .level = 2, .type = UNIFIED_CACHE, .size = 256 * KiB, @@ -115,7 +129,18 @@ struct CPUID2CacheDescriptorInfo cpuid2_cache_descriptors[] = { .associativity = 8, .line_size = 64, }, [0x48] = { .level = 2, .type = UNIFIED_CACHE, .size = 3 * MiB, .associativity = 12, .line_size = 64, }, - /* Descriptor 0x49 depends on CPU family/model, so it is not included */ + /* + * Descriptor 0x49 has 2 cases: + * - 2nd-level cache: 4 MByte, 16-way set associative, 64 byte line size. + * - 3rd-level cache: 4MB, 16-way set associative, 64-byte line size + * (Intel Xeon processor MP, Family 0FH, Model 06H). + * + * When it represents L3, then it depends on CPU family/model. Fortunately, + * the legacy cache/CPU models don't have such special L3. So, just add it + * to represent the general L2 case. + */ + [0x49] = { .level = 2, .type = UNIFIED_CACHE, .size = 4 * MiB, + .associativity = 16, .line_size = 64, }, [0x4A] = { .level = 3, .type = UNIFIED_CACHE, .size = 6 * MiB, .associativity = 12, .line_size = 64, }, [0x4B] = { .level = 3, .type = UNIFIED_CACHE, .size = 8 * MiB, @@ -136,9 +161,10 @@ struct CPUID2CacheDescriptorInfo cpuid2_cache_descriptors[] = { .associativity = 4, .line_size = 64, }, [0x78] = { .level = 2, .type = UNIFIED_CACHE, .size = 1 * MiB, .associativity = 4, .line_size = 64, }, - /* lines per sector is not supported cpuid2_cache_descriptor(), - * so descriptors 0x79, 0x7A, 0x7B, 0x7C are not included. - */ + /* + * lines per sector is not supported cpuid2_cache_descriptor(), + * so descriptors 0x79, 0x7A, 0x7B, 0x7C are not included. + */ [0x7D] = { .level = 2, .type = UNIFIED_CACHE, .size = 2 * MiB, .associativity = 8, .line_size = 64, }, [0x7F] = { .level = 2, .type = UNIFIED_CACHE, .size = 512 * KiB, @@ -199,7 +225,7 @@ struct CPUID2CacheDescriptorInfo cpuid2_cache_descriptors[] = { * Return a CPUID 2 cache descriptor for a given cache. * If no known descriptor is found, return CACHE_DESCRIPTOR_UNAVAILABLE */ -static uint8_t cpuid2_cache_descriptor(CPUCacheInfo *cache) +static uint8_t cpuid2_cache_descriptor(CPUCacheInfo *cache, bool *unmacthed) { int i; @@ -216,9 +242,46 @@ static uint8_t cpuid2_cache_descriptor(CPUCacheInfo *cache) } } + *unmacthed |= true; return CACHE_DESCRIPTOR_UNAVAILABLE; } +static const CPUCaches legacy_intel_cpuid2_cache_info; + +/* Encode cache info for CPUID[2] */ +static void encode_cache_cpuid2(X86CPU *cpu, + const CPUCaches *caches, + uint32_t *eax, uint32_t *ebx, + uint32_t *ecx, uint32_t *edx) +{ + CPUX86State *env = &cpu->env; + int l1d, l1i, l2, l3; + bool unmatched = false; + + *eax = 1; /* Number of CPUID[EAX=2] calls required */ + *ebx = *ecx = *edx = 0; + + l1d = cpuid2_cache_descriptor(caches->l1d_cache, &unmatched); + l1i = cpuid2_cache_descriptor(caches->l1i_cache, &unmatched); + l2 = cpuid2_cache_descriptor(caches->l2_cache, &unmatched); + l3 = cpuid2_cache_descriptor(caches->l3_cache, &unmatched); + + if (!cpu->consistent_cache || + (env->cpuid_min_level < 0x4 && !unmatched)) { + /* + * Though SDM defines code 0x40 for cases with no L2 or L3. It's + * also valid to just ignore l3's code if there's no l2. + */ + if (cpu->enable_l3_cache) { + *ecx = l3; + } + *edx = (l1d << 16) | (l1i << 8) | l2; + } else { + *ecx = 0; + *edx = CACHE_DESCRIPTOR_UNAVAILABLE; + } +} + /* CPUID Leaf 4 constants: */ /* EAX: */ @@ -286,11 +349,17 @@ static void encode_cache_cpuid4(CPUCacheInfo *cache, assert(cache->size == cache->line_size * cache->associativity * cache->partitions * cache->sets); + /* + * The following fields have bit-width limitations, so consider the + * maximum values to avoid overflow: + * Bits 25-14: maximum 4095. + * Bits 31-26: maximum 63. + */ *eax = CACHE_TYPE(cache->type) | CACHE_LEVEL(cache->level) | (cache->self_init ? CACHE_SELF_INIT_LEVEL : 0) | - (max_core_ids_in_package(topo_info) << 26) | - (max_thread_ids_for_cache(topo_info, cache->share_level) << 14); + (MIN(max_core_ids_in_package(topo_info), 63) << 26) | + (MIN(max_thread_ids_for_cache(topo_info, cache->share_level), 4095) << 14); assert(cache->line_size > 0); assert(cache->partitions > 0); @@ -430,7 +499,6 @@ static void encode_topo_cpuid1f(CPUX86State *env, uint32_t count, static uint32_t encode_cache_cpuid80000005(CPUCacheInfo *cache) { assert(cache->size % 1024 == 0); - assert(cache->lines_per_tag > 0); assert(cache->associativity > 0); assert(cache->line_size > 0); return ((cache->size / 1024) << 24) | (cache->associativity << 16) | @@ -439,8 +507,8 @@ static uint32_t encode_cache_cpuid80000005(CPUCacheInfo *cache) #define ASSOC_FULL 0xFF -/* AMD associativity encoding used on CPUID Leaf 0x80000006: */ -#define AMD_ENC_ASSOC(a) (a <= 1 ? a : \ +/* x86 associativity encoding used on CPUID Leaf 0x80000006: */ +#define X86_ENC_ASSOC(a) (a <= 1 ? a : \ a == 2 ? 0x2 : \ a == 4 ? 0x4 : \ a == 8 ? 0x6 : \ @@ -463,19 +531,18 @@ static void encode_cache_cpuid80000006(CPUCacheInfo *l2, { assert(l2->size % 1024 == 0); assert(l2->associativity > 0); - assert(l2->lines_per_tag > 0); assert(l2->line_size > 0); *ecx = ((l2->size / 1024) << 16) | - (AMD_ENC_ASSOC(l2->associativity) << 12) | + (X86_ENC_ASSOC(l2->associativity) << 12) | (l2->lines_per_tag << 8) | (l2->line_size); + /* For Intel, EDX is reserved. */ if (l3) { assert(l3->size % (512 * 1024) == 0); assert(l3->associativity > 0); - assert(l3->lines_per_tag > 0); assert(l3->line_size > 0); *edx = ((l3->size / (512 * 1024)) << 18) | - (AMD_ENC_ASSOC(l3->associativity) << 12) | + (X86_ENC_ASSOC(l3->associativity) << 12) | (l3->lines_per_tag << 8) | (l3->line_size); } else { *edx = 0; @@ -493,7 +560,8 @@ static void encode_cache_cpuid8000001d(CPUCacheInfo *cache, *eax = CACHE_TYPE(cache->type) | CACHE_LEVEL(cache->level) | (cache->self_init ? CACHE_SELF_INIT_LEVEL : 0); - *eax |= max_thread_ids_for_cache(topo_info, cache->share_level) << 14; + /* Bits 25:14 - NumSharingCache: maximum 4095. */ + *eax |= MIN(max_thread_ids_for_cache(topo_info, cache->share_level), 4095) << 14; assert(cache->line_size > 0); assert(cache->partitions > 0); @@ -573,117 +641,172 @@ static void encode_topo_cpuid8000001e(X86CPU *cpu, X86CPUTopoInfo *topo_info, * These are legacy cache values. If there is a need to change any * of these values please use builtin_x86_defs */ - -/* L1 data cache: */ -static CPUCacheInfo legacy_l1d_cache = { - .type = DATA_CACHE, - .level = 1, - .size = 32 * KiB, - .self_init = 1, - .line_size = 64, - .associativity = 8, - .sets = 64, - .partitions = 1, - .no_invd_sharing = true, - .share_level = CPU_TOPOLOGY_LEVEL_CORE, -}; - -/*FIXME: CPUID leaf 0x80000005 is inconsistent with leaves 2 & 4 */ -static CPUCacheInfo legacy_l1d_cache_amd = { - .type = DATA_CACHE, - .level = 1, - .size = 64 * KiB, - .self_init = 1, - .line_size = 64, - .associativity = 2, - .sets = 512, - .partitions = 1, - .lines_per_tag = 1, - .no_invd_sharing = true, - .share_level = CPU_TOPOLOGY_LEVEL_CORE, -}; - -/* L1 instruction cache: */ -static CPUCacheInfo legacy_l1i_cache = { - .type = INSTRUCTION_CACHE, - .level = 1, - .size = 32 * KiB, - .self_init = 1, - .line_size = 64, - .associativity = 8, - .sets = 64, - .partitions = 1, - .no_invd_sharing = true, - .share_level = CPU_TOPOLOGY_LEVEL_CORE, -}; - -/*FIXME: CPUID leaf 0x80000005 is inconsistent with leaves 2 & 4 */ -static CPUCacheInfo legacy_l1i_cache_amd = { - .type = INSTRUCTION_CACHE, - .level = 1, - .size = 64 * KiB, - .self_init = 1, - .line_size = 64, - .associativity = 2, - .sets = 512, - .partitions = 1, - .lines_per_tag = 1, - .no_invd_sharing = true, - .share_level = CPU_TOPOLOGY_LEVEL_CORE, -}; - -/* Level 2 unified cache: */ -static CPUCacheInfo legacy_l2_cache = { - .type = UNIFIED_CACHE, - .level = 2, - .size = 4 * MiB, - .self_init = 1, - .line_size = 64, - .associativity = 16, - .sets = 4096, - .partitions = 1, - .no_invd_sharing = true, - .share_level = CPU_TOPOLOGY_LEVEL_CORE, -}; - -/*FIXME: CPUID leaf 2 descriptor is inconsistent with CPUID leaf 4 */ -static CPUCacheInfo legacy_l2_cache_cpuid2 = { - .type = UNIFIED_CACHE, - .level = 2, - .size = 2 * MiB, - .line_size = 64, - .associativity = 8, - .share_level = CPU_TOPOLOGY_LEVEL_INVALID, +static const CPUCaches legacy_amd_cache_info = { + .l1d_cache = &(CPUCacheInfo) { + .type = DATA_CACHE, + .level = 1, + .size = 64 * KiB, + .self_init = 1, + .line_size = 64, + .associativity = 2, + .sets = 512, + .partitions = 1, + .lines_per_tag = 1, + .no_invd_sharing = true, + .share_level = CPU_TOPOLOGY_LEVEL_CORE, + }, + .l1i_cache = &(CPUCacheInfo) { + .type = INSTRUCTION_CACHE, + .level = 1, + .size = 64 * KiB, + .self_init = 1, + .line_size = 64, + .associativity = 2, + .sets = 512, + .partitions = 1, + .lines_per_tag = 1, + .no_invd_sharing = true, + .share_level = CPU_TOPOLOGY_LEVEL_CORE, + }, + .l2_cache = &(CPUCacheInfo) { + .type = UNIFIED_CACHE, + .level = 2, + .size = 512 * KiB, + .line_size = 64, + .lines_per_tag = 1, + .associativity = 16, + .sets = 512, + .partitions = 1, + .share_level = CPU_TOPOLOGY_LEVEL_CORE, + }, + .l3_cache = &(CPUCacheInfo) { + .type = UNIFIED_CACHE, + .level = 3, + .size = 16 * MiB, + .line_size = 64, + .associativity = 16, + .sets = 16384, + .partitions = 1, + .lines_per_tag = 1, + .self_init = true, + .inclusive = true, + .complex_indexing = true, + .share_level = CPU_TOPOLOGY_LEVEL_DIE, + }, }; - -/*FIXME: CPUID leaf 0x80000006 is inconsistent with leaves 2 & 4 */ -static CPUCacheInfo legacy_l2_cache_amd = { - .type = UNIFIED_CACHE, - .level = 2, - .size = 512 * KiB, - .line_size = 64, - .lines_per_tag = 1, - .associativity = 16, - .sets = 512, - .partitions = 1, - .share_level = CPU_TOPOLOGY_LEVEL_CORE, +/* + * Only used for the CPU models with CPUID level < 4. + * These CPUs (CPUID level < 4) only use CPUID leaf 2 to present + * cache information. + * + * Note: This cache model is just a default one, and is not + * guaranteed to match real hardwares. + */ +static const CPUCaches legacy_intel_cpuid2_cache_info = { + .l1d_cache = &(CPUCacheInfo) { + .type = DATA_CACHE, + .level = 1, + .size = 32 * KiB, + .self_init = 1, + .line_size = 64, + .associativity = 8, + .sets = 64, + .partitions = 1, + .no_invd_sharing = true, + .share_level = CPU_TOPOLOGY_LEVEL_CORE, + }, + .l1i_cache = &(CPUCacheInfo) { + .type = INSTRUCTION_CACHE, + .level = 1, + .size = 32 * KiB, + .self_init = 1, + .line_size = 64, + .associativity = 8, + .sets = 64, + .partitions = 1, + .no_invd_sharing = true, + .share_level = CPU_TOPOLOGY_LEVEL_CORE, + }, + .l2_cache = &(CPUCacheInfo) { + .type = UNIFIED_CACHE, + .level = 2, + .size = 2 * MiB, + .self_init = 1, + .line_size = 64, + .associativity = 8, + .sets = 4096, + .partitions = 1, + .no_invd_sharing = true, + .share_level = CPU_TOPOLOGY_LEVEL_CORE, + }, + .l3_cache = &(CPUCacheInfo) { + .type = UNIFIED_CACHE, + .level = 3, + .size = 16 * MiB, + .line_size = 64, + .associativity = 16, + .sets = 16384, + .partitions = 1, + .lines_per_tag = 1, + .self_init = true, + .inclusive = true, + .complex_indexing = true, + .share_level = CPU_TOPOLOGY_LEVEL_DIE, + }, }; -/* Level 3 unified cache: */ -static CPUCacheInfo legacy_l3_cache = { - .type = UNIFIED_CACHE, - .level = 3, - .size = 16 * MiB, - .line_size = 64, - .associativity = 16, - .sets = 16384, - .partitions = 1, - .lines_per_tag = 1, - .self_init = true, - .inclusive = true, - .complex_indexing = true, - .share_level = CPU_TOPOLOGY_LEVEL_DIE, +static const CPUCaches legacy_intel_cache_info = { + .l1d_cache = &(CPUCacheInfo) { + .type = DATA_CACHE, + .level = 1, + .size = 32 * KiB, + .self_init = 1, + .line_size = 64, + .associativity = 8, + .sets = 64, + .partitions = 1, + .no_invd_sharing = true, + .share_level = CPU_TOPOLOGY_LEVEL_CORE, + }, + .l1i_cache = &(CPUCacheInfo) { + .type = INSTRUCTION_CACHE, + .level = 1, + .size = 32 * KiB, + .self_init = 1, + .line_size = 64, + .associativity = 8, + .sets = 64, + .partitions = 1, + .no_invd_sharing = true, + .share_level = CPU_TOPOLOGY_LEVEL_CORE, + }, + .l2_cache = &(CPUCacheInfo) { + .type = UNIFIED_CACHE, + .level = 2, + .size = 4 * MiB, + .self_init = 1, + .line_size = 64, + .associativity = 16, + .sets = 4096, + .partitions = 1, + .no_invd_sharing = true, + .share_level = CPU_TOPOLOGY_LEVEL_CORE, + }, + .l3_cache = &(CPUCacheInfo) { + .type = UNIFIED_CACHE, + .level = 3, + .size = 16 * MiB, + .line_size = 64, + .associativity = 16, + .sets = 16384, + .partitions = 1, + .lines_per_tag = 1, + .self_init = true, + .inclusive = true, + .complex_indexing = true, + .share_level = CPU_TOPOLOGY_LEVEL_DIE, + }, }; /* TLB definitions: */ @@ -1943,7 +2066,7 @@ uint32_t xsave_area_size(uint64_t mask, bool compacted) static inline bool accel_uses_host_cpuid(void) { - return kvm_enabled() || hvf_enabled(); + return !tcg_enabled() && !qtest_enabled(); } static inline uint64_t x86_cpu_xsave_xcr0_components(X86CPU *cpu) @@ -2764,6 +2887,378 @@ static const CPUCaches epyc_turin_cache_info = { .no_invd_sharing = true, .complex_indexing = false, .share_level = CPU_TOPOLOGY_LEVEL_DIE, + } +}; + +static const CPUCaches xeon_spr_cache_info = { + .l1d_cache = &(CPUCacheInfo) { + /* CPUID 0x4.0x0.EAX */ + .type = DATA_CACHE, + .level = 1, + .self_init = true, + + /* CPUID 0x4.0x0.EBX */ + .line_size = 64, + .partitions = 1, + .associativity = 12, + + /* CPUID 0x4.0x0.ECX */ + .sets = 64, + + /* CPUID 0x4.0x0.EDX */ + .no_invd_sharing = false, + .inclusive = false, + .complex_indexing = false, + + .size = 48 * KiB, + .share_level = CPU_TOPOLOGY_LEVEL_CORE, + }, + .l1i_cache = &(CPUCacheInfo) { + /* CPUID 0x4.0x1.EAX */ + .type = INSTRUCTION_CACHE, + .level = 1, + .self_init = true, + + /* CPUID 0x4.0x1.EBX */ + .line_size = 64, + .partitions = 1, + .associativity = 8, + + /* CPUID 0x4.0x1.ECX */ + .sets = 64, + + /* CPUID 0x4.0x1.EDX */ + .no_invd_sharing = false, + .inclusive = false, + .complex_indexing = false, + + .size = 32 * KiB, + .share_level = CPU_TOPOLOGY_LEVEL_CORE, + }, + .l2_cache = &(CPUCacheInfo) { + /* CPUID 0x4.0x2.EAX */ + .type = UNIFIED_CACHE, + .level = 2, + .self_init = true, + + /* CPUID 0x4.0x2.EBX */ + .line_size = 64, + .partitions = 1, + .associativity = 16, + + /* CPUID 0x4.0x2.ECX */ + .sets = 2048, + + /* CPUID 0x4.0x2.EDX */ + .no_invd_sharing = false, + .inclusive = false, + .complex_indexing = false, + + .size = 2 * MiB, + .share_level = CPU_TOPOLOGY_LEVEL_CORE, + }, + .l3_cache = &(CPUCacheInfo) { + /* CPUID 0x4.0x3.EAX */ + .type = UNIFIED_CACHE, + .level = 3, + .self_init = true, + + /* CPUID 0x4.0x3.EBX */ + .line_size = 64, + .partitions = 1, + .associativity = 15, + + /* CPUID 0x4.0x3.ECX */ + .sets = 65536, + + /* CPUID 0x4.0x3.EDX */ + .no_invd_sharing = false, + .inclusive = false, + .complex_indexing = true, + + .size = 60 * MiB, + .share_level = CPU_TOPOLOGY_LEVEL_SOCKET, + }, +}; + +static const CPUCaches xeon_gnr_cache_info = { + .l1d_cache = &(CPUCacheInfo) { + /* CPUID 0x4.0x0.EAX */ + .type = DATA_CACHE, + .level = 1, + .self_init = true, + + /* CPUID 0x4.0x0.EBX */ + .line_size = 64, + .partitions = 1, + .associativity = 12, + + /* CPUID 0x4.0x0.ECX */ + .sets = 64, + + /* CPUID 0x4.0x0.EDX */ + .no_invd_sharing = false, + .inclusive = false, + .complex_indexing = false, + + .size = 48 * KiB, + .share_level = CPU_TOPOLOGY_LEVEL_CORE, + }, + .l1i_cache = &(CPUCacheInfo) { + /* CPUID 0x4.0x1.EAX */ + .type = INSTRUCTION_CACHE, + .level = 1, + .self_init = true, + + /* CPUID 0x4.0x1.EBX */ + .line_size = 64, + .partitions = 1, + .associativity = 16, + + /* CPUID 0x4.0x1.ECX */ + .sets = 64, + + /* CPUID 0x4.0x1.EDX */ + .no_invd_sharing = false, + .inclusive = false, + .complex_indexing = false, + + .size = 64 * KiB, + .share_level = CPU_TOPOLOGY_LEVEL_CORE, + }, + .l2_cache = &(CPUCacheInfo) { + /* CPUID 0x4.0x2.EAX */ + .type = UNIFIED_CACHE, + .level = 2, + .self_init = true, + + /* CPUID 0x4.0x2.EBX */ + .line_size = 64, + .partitions = 1, + .associativity = 16, + + /* CPUID 0x4.0x2.ECX */ + .sets = 2048, + + /* CPUID 0x4.0x2.EDX */ + .no_invd_sharing = false, + .inclusive = false, + .complex_indexing = false, + + .size = 2 * MiB, + .share_level = CPU_TOPOLOGY_LEVEL_CORE, + }, + .l3_cache = &(CPUCacheInfo) { + /* CPUID 0x4.0x3.EAX */ + .type = UNIFIED_CACHE, + .level = 3, + .self_init = true, + + /* CPUID 0x4.0x3.EBX */ + .line_size = 64, + .partitions = 1, + .associativity = 16, + + /* CPUID 0x4.0x3.ECX */ + .sets = 294912, + + /* CPUID 0x4.0x3.EDX */ + .no_invd_sharing = false, + .inclusive = false, + .complex_indexing = true, + + .size = 288 * MiB, + .share_level = CPU_TOPOLOGY_LEVEL_SOCKET, + }, +}; + +static const CPUCaches xeon_srf_cache_info = { + .l1d_cache = &(CPUCacheInfo) { + /* CPUID 0x4.0x0.EAX */ + .type = DATA_CACHE, + .level = 1, + .self_init = true, + + /* CPUID 0x4.0x0.EBX */ + .line_size = 64, + .partitions = 1, + .associativity = 8, + + /* CPUID 0x4.0x0.ECX */ + .sets = 64, + + /* CPUID 0x4.0x0.EDX */ + .no_invd_sharing = false, + .inclusive = false, + .complex_indexing = false, + + .size = 32 * KiB, + .share_level = CPU_TOPOLOGY_LEVEL_CORE, + }, + .l1i_cache = &(CPUCacheInfo) { + /* CPUID 0x4.0x1.EAX */ + .type = INSTRUCTION_CACHE, + .level = 1, + .self_init = true, + + /* CPUID 0x4.0x1.EBX */ + .line_size = 64, + .partitions = 1, + .associativity = 8, + + /* CPUID 0x4.0x1.ECX */ + .sets = 128, + + /* CPUID 0x4.0x1.EDX */ + .no_invd_sharing = false, + .inclusive = false, + .complex_indexing = false, + + .size = 64 * KiB, + .share_level = CPU_TOPOLOGY_LEVEL_CORE, + }, + .l2_cache = &(CPUCacheInfo) { + /* CPUID 0x4.0x2.EAX */ + .type = UNIFIED_CACHE, + .level = 2, + .self_init = true, + + /* CPUID 0x4.0x2.EBX */ + .line_size = 64, + .partitions = 1, + .associativity = 16, + + /* CPUID 0x4.0x2.ECX */ + .sets = 4096, + + /* CPUID 0x4.0x2.EDX */ + .no_invd_sharing = false, + .inclusive = false, + .complex_indexing = false, + + .size = 4 * MiB, + .share_level = CPU_TOPOLOGY_LEVEL_MODULE, + }, + .l3_cache = &(CPUCacheInfo) { + /* CPUID 0x4.0x3.EAX */ + .type = UNIFIED_CACHE, + .level = 3, + .self_init = true, + + /* CPUID 0x4.0x3.EBX */ + .line_size = 64, + .partitions = 1, + .associativity = 12, + + /* CPUID 0x4.0x3.ECX */ + .sets = 147456, + + /* CPUID 0x4.0x3.EDX */ + .no_invd_sharing = false, + .inclusive = false, + .complex_indexing = true, + + .size = 108 * MiB, + .share_level = CPU_TOPOLOGY_LEVEL_SOCKET, + }, +}; + +static const CPUCaches yongfeng_cache_info = { + .l1d_cache = &(CPUCacheInfo) { + /* CPUID 0x4.0x0.EAX */ + .type = DATA_CACHE, + .level = 1, + .self_init = true, + + /* CPUID 0x4.0x0.EBX */ + .line_size = 64, + .partitions = 1, + .associativity = 8, + + /* CPUID 0x4.0x0.ECX */ + .sets = 64, + + /* CPUID 0x4.0x0.EDX */ + .no_invd_sharing = false, + .inclusive = false, + .complex_indexing = false, + + /* CPUID 0x80000005.ECX */ + .lines_per_tag = 1, + .size = 32 * KiB, + + .share_level = CPU_TOPOLOGY_LEVEL_CORE, + }, + .l1i_cache = &(CPUCacheInfo) { + /* CPUID 0x4.0x1.EAX */ + .type = INSTRUCTION_CACHE, + .level = 1, + .self_init = true, + + /* CPUID 0x4.0x1.EBX */ + .line_size = 64, + .partitions = 1, + .associativity = 16, + + /* CPUID 0x4.0x1.ECX */ + .sets = 64, + + /* CPUID 0x4.0x1.EDX */ + .no_invd_sharing = false, + .inclusive = false, + .complex_indexing = false, + + /* CPUID 0x80000005.EDX */ + .lines_per_tag = 1, + .size = 64 * KiB, + + .share_level = CPU_TOPOLOGY_LEVEL_CORE, + }, + .l2_cache = &(CPUCacheInfo) { + /* CPUID 0x4.0x2.EAX */ + .type = UNIFIED_CACHE, + .level = 2, + .self_init = true, + + /* CPUID 0x4.0x2.EBX */ + .line_size = 64, + .partitions = 1, + .associativity = 8, + + /* CPUID 0x4.0x2.ECX */ + .sets = 512, + + /* CPUID 0x4.0x2.EDX */ + .no_invd_sharing = false, + .inclusive = true, + .complex_indexing = false, + + /* CPUID 0x80000006.ECX */ + .size = 256 * KiB, + + .share_level = CPU_TOPOLOGY_LEVEL_CORE, + }, + .l3_cache = &(CPUCacheInfo) { + /* CPUID 0x4.0x3.EAX */ + .type = UNIFIED_CACHE, + .level = 3, + .self_init = true, + + /* CPUID 0x4.0x3.EBX */ + .line_size = 64, + .partitions = 1, + .associativity = 16, + + /* CPUID 0x4.0x3.ECX */ + .sets = 8192, + + /* CPUID 0x4.0x3.EDX */ + .no_invd_sharing = true, + .inclusive = true, + .complex_indexing = false, + + .size = 8 * MiB, + .share_level = CPU_TOPOLOGY_LEVEL_DIE, }, }; @@ -3019,6 +3514,7 @@ static const X86CPUDefinition builtin_x86_defs[] = { I486_FEATURES, .xlevel = 0, .model_id = "", + .cache_info = &legacy_intel_cpuid2_cache_info, }, { .name = "pentium", @@ -3031,6 +3527,7 @@ static const X86CPUDefinition builtin_x86_defs[] = { PENTIUM_FEATURES, .xlevel = 0, .model_id = "", + .cache_info = &legacy_intel_cpuid2_cache_info, }, { .name = "pentium2", @@ -3043,6 +3540,7 @@ static const X86CPUDefinition builtin_x86_defs[] = { PENTIUM2_FEATURES, .xlevel = 0, .model_id = "", + .cache_info = &legacy_intel_cpuid2_cache_info, }, { .name = "pentium3", @@ -3055,6 +3553,7 @@ static const X86CPUDefinition builtin_x86_defs[] = { PENTIUM3_FEATURES, .xlevel = 0, .model_id = "", + .cache_info = &legacy_intel_cpuid2_cache_info, }, { .name = "athlon", @@ -4587,6 +5086,15 @@ static const X86CPUDefinition builtin_x86_defs[] = { { /* end of list */ } } }, + { + .version = 4, + .note = "with spr-sp cache model and 0x1f leaf", + .cache_info = &xeon_spr_cache_info, + .props = (PropValue[]) { + { "x-force-cpuid-0x1f", "on" }, + { /* end of list */ }, + } + }, { /* end of list */ } } }, @@ -4740,6 +5248,15 @@ static const X86CPUDefinition builtin_x86_defs[] = { { /* end of list */ } } }, + { + .version = 3, + .note = "with gnr-sp cache model and 0x1f leaf", + .cache_info = &xeon_gnr_cache_info, + .props = (PropValue[]) { + { "x-force-cpuid-0x1f", "on" }, + { /* end of list */ }, + } + }, { /* end of list */ }, }, }, @@ -4885,6 +5402,15 @@ static const X86CPUDefinition builtin_x86_defs[] = { { /* end of list */ } } }, + { + .version = 3, + .note = "with srf-sp cache model and 0x1f leaf", + .cache_info = &xeon_srf_cache_info, + .props = (PropValue[]) { + { "x-force-cpuid-0x1f", "on" }, + { /* end of list */ }, + } + }, { /* end of list */ }, }, }, @@ -6027,6 +6553,15 @@ static const X86CPUDefinition builtin_x86_defs[] = { { /* end of list */ } } }, + { + .version = 3, + .note = "with the cache model and 0x1f leaf", + .cache_info = &yongfeng_cache_info, + .props = (PropValue[]) { + { "x-force-cpuid-0x1f", "on" }, + { /* end of list */ }, + } + }, { /* end of list */ } } }, @@ -6187,6 +6722,7 @@ static void max_x86_cpu_class_init(ObjectClass *oc, const void *data) xcc->ordering = 9; + xcc->max_features = true; xcc->model_description = "Enables all features supported by the accelerator in the current host"; @@ -6197,22 +6733,21 @@ static void max_x86_cpu_class_init(ObjectClass *oc, const void *data) static void max_x86_cpu_initfn(Object *obj) { X86CPU *cpu = X86_CPU(obj); - - /* We can't fill the features array here because we don't know yet if - * "migratable" is true or false. - */ - cpu->max_features = true; - object_property_set_bool(OBJECT(cpu), "pmu", true, &error_abort); + CPUX86State *env = &cpu->env; /* - * these defaults are used for TCG and all other accelerators - * besides KVM and HVF, which overwrite these values + * these defaults are used for TCG, other accelerators have overwritten + * these values */ - object_property_set_str(OBJECT(cpu), "vendor", CPUID_VENDOR_AMD, - &error_abort); - object_property_set_str(OBJECT(cpu), "model-id", - "QEMU TCG CPU version " QEMU_HW_VERSION, - &error_abort); + if (!env->cpuid_vendor1) { + object_property_set_str(OBJECT(cpu), "vendor", CPUID_VENDOR_AMD, + &error_abort); + } + if (!env->cpuid_model[0]) { + object_property_set_str(OBJECT(cpu), "model-id", + "QEMU TCG CPU version " QEMU_HW_VERSION, + &error_abort); + } } static const TypeInfo max_x86_cpu_type_info = { @@ -6324,10 +6859,7 @@ static void x86_cpuid_version_get_family(Object *obj, Visitor *v, CPUX86State *env = &cpu->env; uint64_t value; - value = (env->cpuid_version >> 8) & 0xf; - if (value == 0xf) { - value += (env->cpuid_version >> 20) & 0xff; - } + value = x86_cpu_family(env->cpuid_version); visit_type_uint64(v, name, &value, errp); } @@ -6365,8 +6897,7 @@ static void x86_cpuid_version_get_model(Object *obj, Visitor *v, CPUX86State *env = &cpu->env; uint64_t value; - value = (env->cpuid_version >> 4) & 0xf; - value |= ((env->cpuid_version >> 16) & 0xf) << 4; + value = x86_cpu_model(env->cpuid_version); visit_type_uint64(v, name, &value, errp); } @@ -6400,7 +6931,7 @@ static void x86_cpuid_version_get_stepping(Object *obj, Visitor *v, CPUX86State *env = &cpu->env; uint64_t value; - value = env->cpuid_version & 0xf; + value = x86_cpu_stepping(env->cpuid_version); visit_type_uint64(v, name, &value, errp); } @@ -6468,11 +6999,11 @@ static char *x86_cpuid_get_model_id(Object *obj, Error **errp) char *value; int i; - value = g_malloc(48 + 1); - for (i = 0; i < 48; i++) { + value = g_malloc(CPUID_MODEL_ID_SZ + 1); + for (i = 0; i < CPUID_MODEL_ID_SZ; i++) { value[i] = env->cpuid_model[i >> 2] >> (8 * (i & 3)); } - value[48] = '\0'; + value[CPUID_MODEL_ID_SZ] = '\0'; return value; } @@ -6487,7 +7018,7 @@ static void x86_cpuid_set_model_id(Object *obj, const char *model_id, model_id = ""; } len = strlen(model_id); - memset(env->cpuid_model, 0, 48); + memset(env->cpuid_model, 0, CPUID_MODEL_ID_SZ); for (i = 0; i < 48; i++) { if (i >= len) { c = '\0'; @@ -7347,11 +7878,35 @@ void cpu_x86_cpuid(CPUX86State *env, uint32_t index, uint32_t count, } *edx = env->features[FEAT_1_EDX]; if (threads_per_pkg > 1) { - *ebx |= threads_per_pkg << 16; + uint32_t num; + + /* + * For CPUID.01H.EBX[Bits 23-16], AMD requires logical processor + * count, but Intel needs maximum number of addressable IDs for + * logical processors per package. + */ + if (cpu->vendor_cpuid_only_v2 && + (IS_INTEL_CPU(env) || IS_ZHAOXIN_CPU(env))) { + num = 1 << apicid_pkg_offset(topo_info); + } else { + num = threads_per_pkg; + } + + /* Fixup overflow: max value for bits 23-16 is 255. */ + *ebx |= MIN(num, 255) << 16; } break; - case 2: - /* cache info: needed for Pentium Pro compatibility */ + case 2: { /* cache info: needed for Pentium Pro compatibility */ + const CPUCaches *caches; + + if (env->enable_legacy_cpuid2_cache) { + caches = &legacy_intel_cpuid2_cache_info; + } else if (env->enable_legacy_vendor_cache) { + caches = &legacy_intel_cache_info; + } else { + caches = &env->cache_info; + } + if (cpu->cache_info_passthrough) { x86_cpu_get_cache_cpuid(index, 0, eax, ebx, ecx, edx); break; @@ -7359,18 +7914,18 @@ void cpu_x86_cpuid(CPUX86State *env, uint32_t index, uint32_t count, *eax = *ebx = *ecx = *edx = 0; break; } - *eax = 1; /* Number of CPUID[EAX=2] calls required */ - *ebx = 0; - if (!cpu->enable_l3_cache) { - *ecx = 0; + encode_cache_cpuid2(cpu, caches, eax, ebx, ecx, edx); + break; + } + case 4: { + const CPUCaches *caches; + + if (env->enable_legacy_vendor_cache) { + caches = &legacy_intel_cache_info; } else { - *ecx = cpuid2_cache_descriptor(env->cache_info_cpuid2.l3_cache); + caches = &env->cache_info; } - *edx = (cpuid2_cache_descriptor(env->cache_info_cpuid2.l1d_cache) << 16) | - (cpuid2_cache_descriptor(env->cache_info_cpuid2.l1i_cache) << 8) | - (cpuid2_cache_descriptor(env->cache_info_cpuid2.l2_cache)); - break; - case 4: + /* cache info: needed for Core compatibility */ if (cpu->cache_info_passthrough) { x86_cpu_get_cache_cpuid(index, count, eax, ebx, ecx, edx); @@ -7382,13 +7937,13 @@ void cpu_x86_cpuid(CPUX86State *env, uint32_t index, uint32_t count, int host_vcpus_per_cache = 1 + ((*eax & 0x3FFC000) >> 14); *eax &= ~0xFC000000; - *eax |= max_core_ids_in_package(topo_info) << 26; + *eax |= MIN(max_core_ids_in_package(topo_info), 63) << 26; if (host_vcpus_per_cache > threads_per_pkg) { *eax &= ~0x3FFC000; /* Share the cache at package level. */ - *eax |= max_thread_ids_for_cache(topo_info, - CPU_TOPOLOGY_LEVEL_SOCKET) << 14; + *eax |= MIN(max_thread_ids_for_cache(topo_info, + CPU_TOPOLOGY_LEVEL_SOCKET), 4095) << 14; } } } else if (cpu->vendor_cpuid_only && IS_AMD_CPU(env)) { @@ -7398,30 +7953,26 @@ void cpu_x86_cpuid(CPUX86State *env, uint32_t index, uint32_t count, switch (count) { case 0: /* L1 dcache info */ - encode_cache_cpuid4(env->cache_info_cpuid4.l1d_cache, - topo_info, + encode_cache_cpuid4(caches->l1d_cache, topo_info, eax, ebx, ecx, edx); if (!cpu->l1_cache_per_core) { *eax &= ~MAKE_64BIT_MASK(14, 12); } break; case 1: /* L1 icache info */ - encode_cache_cpuid4(env->cache_info_cpuid4.l1i_cache, - topo_info, + encode_cache_cpuid4(caches->l1i_cache, topo_info, eax, ebx, ecx, edx); if (!cpu->l1_cache_per_core) { *eax &= ~MAKE_64BIT_MASK(14, 12); } break; case 2: /* L2 cache info */ - encode_cache_cpuid4(env->cache_info_cpuid4.l2_cache, - topo_info, + encode_cache_cpuid4(caches->l2_cache, topo_info, eax, ebx, ecx, edx); break; case 3: /* L3 cache info */ if (cpu->enable_l3_cache) { - encode_cache_cpuid4(env->cache_info_cpuid4.l3_cache, - topo_info, + encode_cache_cpuid4(caches->l3_cache, topo_info, eax, ebx, ecx, edx); break; } @@ -7432,6 +7983,7 @@ void cpu_x86_cpuid(CPUX86State *env, uint32_t index, uint32_t count, } } break; + } case 5: /* MONITOR/MWAIT Leaf */ *eax = cpu->mwait.eax; /* Smallest monitor-line size in bytes */ @@ -7522,21 +8074,6 @@ void cpu_x86_cpuid(CPUX86State *env, uint32_t index, uint32_t count, assert(!(*eax & ~0x1f)); *ebx &= 0xffff; /* The count doesn't need to be reliable. */ break; - case 0x1C: - if (cpu->enable_pmu && (env->features[FEAT_7_0_EDX] & CPUID_7_0_EDX_ARCH_LBR)) { - x86_cpu_get_supported_cpuid(0x1C, 0, eax, ebx, ecx, edx); - *edx = 0; - } - break; - case 0x1F: - /* V2 Extended Topology Enumeration Leaf */ - if (!x86_has_cpuid_0x1f(cpu)) { - *eax = *ebx = *ecx = *edx = 0; - break; - } - - encode_topo_cpuid1f(env, count, topo_info, eax, ebx, ecx, edx); - break; case 0xD: { /* Processor Extended State */ *eax = 0; @@ -7677,6 +8214,12 @@ void cpu_x86_cpuid(CPUX86State *env, uint32_t index, uint32_t count, } break; } + case 0x1C: + if (cpu->enable_pmu && (env->features[FEAT_7_0_EDX] & CPUID_7_0_EDX_ARCH_LBR)) { + x86_cpu_get_supported_cpuid(0x1C, 0, eax, ebx, ecx, edx); + *edx = 0; + } + break; case 0x1D: { /* AMX TILE, for now hardcoded for Sapphire Rapids*/ *eax = 0; @@ -7714,6 +8257,15 @@ void cpu_x86_cpuid(CPUX86State *env, uint32_t index, uint32_t count, } break; } + case 0x1F: + /* V2 Extended Topology Enumeration Leaf */ + if (!x86_has_cpuid_0x1f(cpu)) { + *eax = *ebx = *ecx = *edx = 0; + break; + } + + encode_topo_cpuid1f(env, count, topo_info, eax, ebx, ecx, edx); + break; case 0x24: { *eax = 0; *ebx = 0; @@ -7750,9 +8302,15 @@ void cpu_x86_cpuid(CPUX86State *env, uint32_t index, uint32_t count, break; case 0x80000000: *eax = env->cpuid_xlevel; - *ebx = env->cpuid_vendor1; - *edx = env->cpuid_vendor2; - *ecx = env->cpuid_vendor3; + + if (cpu->vendor_cpuid_only_v2 && + (IS_INTEL_CPU(env) || IS_ZHAOXIN_CPU(env))) { + *ebx = *ecx = *edx = 0; + } else { + *ebx = env->cpuid_vendor1; + *edx = env->cpuid_vendor2; + *ecx = env->cpuid_vendor3; + } break; case 0x80000001: *eax = env->cpuid_version; @@ -7760,7 +8318,7 @@ void cpu_x86_cpuid(CPUX86State *env, uint32_t index, uint32_t count, *ecx = env->features[FEAT_8000_0001_ECX]; *edx = env->features[FEAT_8000_0001_EDX]; - if (tcg_enabled() && env->cpuid_vendor1 == CPUID_VENDOR_INTEL_1 && + if (tcg_enabled() && IS_INTEL_CPU(env) && !(env->hflags & HF_LMA_MASK)) { *edx &= ~CPUID_EXT2_SYSCALL; } @@ -7773,41 +8331,78 @@ void cpu_x86_cpuid(CPUX86State *env, uint32_t index, uint32_t count, *ecx = env->cpuid_model[(index - 0x80000002) * 4 + 2]; *edx = env->cpuid_model[(index - 0x80000002) * 4 + 3]; break; - case 0x80000005: - /* cache info (L1 cache) */ + case 0x80000005: { + /* cache info (L1 cache/TLB Associativity Field) */ + const CPUCaches *caches; + + if (env->enable_legacy_vendor_cache) { + caches = &legacy_amd_cache_info; + } else { + caches = &env->cache_info; + } + if (cpu->cache_info_passthrough) { x86_cpu_get_cache_cpuid(index, 0, eax, ebx, ecx, edx); break; } + + if (cpu->vendor_cpuid_only_v2 && IS_INTEL_CPU(env)) { + *eax = *ebx = *ecx = *edx = 0; + break; + } + *eax = (L1_DTLB_2M_ASSOC << 24) | (L1_DTLB_2M_ENTRIES << 16) | (L1_ITLB_2M_ASSOC << 8) | (L1_ITLB_2M_ENTRIES); *ebx = (L1_DTLB_4K_ASSOC << 24) | (L1_DTLB_4K_ENTRIES << 16) | (L1_ITLB_4K_ASSOC << 8) | (L1_ITLB_4K_ENTRIES); - *ecx = encode_cache_cpuid80000005(env->cache_info_amd.l1d_cache); - *edx = encode_cache_cpuid80000005(env->cache_info_amd.l1i_cache); + *ecx = encode_cache_cpuid80000005(caches->l1d_cache); + *edx = encode_cache_cpuid80000005(caches->l1i_cache); break; - case 0x80000006: - /* cache info (L2 cache) */ + } + case 0x80000006: { /* cache info (L2 cache/TLB/L3 cache) */ + const CPUCaches *caches; + + if (env->enable_legacy_vendor_cache) { + caches = &legacy_amd_cache_info; + } else { + caches = &env->cache_info; + } + if (cpu->cache_info_passthrough) { x86_cpu_get_cache_cpuid(index, 0, eax, ebx, ecx, edx); break; } - *eax = (AMD_ENC_ASSOC(L2_DTLB_2M_ASSOC) << 28) | + + if (cpu->vendor_cpuid_only_v2 && + (IS_INTEL_CPU(env) || IS_ZHAOXIN_CPU(env))) { + *eax = *ebx = 0; + encode_cache_cpuid80000006(caches->l2_cache, + NULL, ecx, edx); + break; + } + + *eax = (X86_ENC_ASSOC(L2_DTLB_2M_ASSOC) << 28) | (L2_DTLB_2M_ENTRIES << 16) | - (AMD_ENC_ASSOC(L2_ITLB_2M_ASSOC) << 12) | + (X86_ENC_ASSOC(L2_ITLB_2M_ASSOC) << 12) | (L2_ITLB_2M_ENTRIES); - *ebx = (AMD_ENC_ASSOC(L2_DTLB_4K_ASSOC) << 28) | + *ebx = (X86_ENC_ASSOC(L2_DTLB_4K_ASSOC) << 28) | (L2_DTLB_4K_ENTRIES << 16) | - (AMD_ENC_ASSOC(L2_ITLB_4K_ASSOC) << 12) | + (X86_ENC_ASSOC(L2_ITLB_4K_ASSOC) << 12) | (L2_ITLB_4K_ENTRIES); - encode_cache_cpuid80000006(env->cache_info_amd.l2_cache, + + encode_cache_cpuid80000006(caches->l2_cache, cpu->enable_l3_cache ? - env->cache_info_amd.l3_cache : NULL, + caches->l3_cache : NULL, ecx, edx); break; + } case 0x80000007: *eax = 0; - *ebx = env->features[FEAT_8000_0007_EBX]; + if (cpu->vendor_cpuid_only_v2 && IS_INTEL_CPU(env)) { + *ebx = 0; + } else { + *ebx = env->features[FEAT_8000_0007_EBX]; + } *ecx = 0; *edx = env->features[FEAT_8000_0007_EDX]; break; @@ -7820,6 +8415,17 @@ void cpu_x86_cpuid(CPUX86State *env, uint32_t index, uint32_t count, *eax |= (cpu->guest_phys_bits << 16); } *ebx = env->features[FEAT_8000_0008_EBX]; + + /* + * Don't emulate Bits [7:0] & Bits [15:12] for Intel/Zhaoxin, since + * they're using 0x1f leaf. + */ + if (cpu->vendor_cpuid_only_v2 && + (IS_INTEL_CPU(env) || IS_ZHAOXIN_CPU(env))) { + *ecx = *edx = 0; + break; + } + if (threads_per_pkg > 1) { /* * Bits 15:12 is "The number of bits in the initial @@ -7855,19 +8461,19 @@ void cpu_x86_cpuid(CPUX86State *env, uint32_t index, uint32_t count, } switch (count) { case 0: /* L1 dcache info */ - encode_cache_cpuid8000001d(env->cache_info_amd.l1d_cache, + encode_cache_cpuid8000001d(env->cache_info.l1d_cache, topo_info, eax, ebx, ecx, edx); break; case 1: /* L1 icache info */ - encode_cache_cpuid8000001d(env->cache_info_amd.l1i_cache, + encode_cache_cpuid8000001d(env->cache_info.l1i_cache, topo_info, eax, ebx, ecx, edx); break; case 2: /* L2 cache info */ - encode_cache_cpuid8000001d(env->cache_info_amd.l2_cache, + encode_cache_cpuid8000001d(env->cache_info.l2_cache, topo_info, eax, ebx, ecx, edx); break; case 3: /* L3 cache info */ - encode_cache_cpuid8000001d(env->cache_info_amd.l3_cache, + encode_cache_cpuid8000001d(env->cache_info.l3_cache, topo_info, eax, ebx, ecx, edx); break; default: /* end of info */ @@ -7888,6 +8494,21 @@ void cpu_x86_cpuid(CPUX86State *env, uint32_t index, uint32_t count, *edx = 0; } break; + case 0x8000001F: + *eax = *ebx = *ecx = *edx = 0; + if (sev_enabled()) { + *eax = 0x2; + *eax |= sev_es_enabled() ? 0x8 : 0; + *eax |= sev_snp_enabled() ? 0x10 : 0; + *ebx = sev_get_cbit_position() & 0x3f; /* EBX[5:0] */ + *ebx |= (sev_get_reduced_phys_bits() & 0x3f) << 6; /* EBX[11:6] */ + } + break; + case 0x80000021: + *eax = *ebx = *ecx = *edx = 0; + *eax = env->features[FEAT_8000_0021_EAX]; + *ebx = env->features[FEAT_8000_0021_EBX]; + break; case 0x80000022: *eax = *ebx = *ecx = *edx = 0; /* AMD Extended Performance Monitoring and Debug */ @@ -7920,21 +8541,6 @@ void cpu_x86_cpuid(CPUX86State *env, uint32_t index, uint32_t count, *ecx = 0; *edx = 0; break; - case 0x8000001F: - *eax = *ebx = *ecx = *edx = 0; - if (sev_enabled()) { - *eax = 0x2; - *eax |= sev_es_enabled() ? 0x8 : 0; - *eax |= sev_snp_enabled() ? 0x10 : 0; - *ebx = sev_get_cbit_position() & 0x3f; /* EBX[5:0] */ - *ebx |= (sev_get_reduced_phys_bits() & 0x3f) << 6; /* EBX[11:6] */ - } - break; - case 0x80000021: - *eax = *ebx = *ecx = *edx = 0; - *eax = env->features[FEAT_8000_0021_EAX]; - *ebx = env->features[FEAT_8000_0021_EBX]; - break; default: /* reserved values: zero */ *eax = 0; @@ -8154,7 +8760,7 @@ static void mce_init(X86CPU *cpu) CPUX86State *cenv = &cpu->env; unsigned int bank; - if (((cenv->cpuid_version >> 8) & 0xf) >= 6 + if (x86_cpu_family(cenv->cpuid_version) >= 6 && (cenv->features[FEAT_1_EDX] & (CPUID_MCE | CPUID_MCA)) == (CPUID_MCE | CPUID_MCA)) { cenv->mcg_cap = MCE_CAP_DEF | MCE_BANKS_DEF | @@ -8282,6 +8888,7 @@ static void x86_cpu_enable_xsave_components(X86CPU *cpu) */ void x86_cpu_expand_features(X86CPU *cpu, Error **errp) { + X86CPUClass *xcc = X86_CPU_GET_CLASS(cpu); CPUX86State *env = &cpu->env; FeatureWord w; int i; @@ -8301,12 +8908,12 @@ void x86_cpu_expand_features(X86CPU *cpu, Error **errp) } } - /*TODO: Now cpu->max_features doesn't overwrite features + /* TODO: Now xcc->max_features doesn't overwrite features * set using QOM properties, and we can convert * plus_features & minus_features to global properties * inside x86_cpu_parse_featurestr() too. */ - if (cpu->max_features) { + if (xcc->max_features) { for (w = 0; w < FEATURE_WORDS; w++) { /* Override only features that weren't set explicitly * by the user. @@ -8338,7 +8945,8 @@ void x86_cpu_expand_features(X86CPU *cpu, Error **errp) } } - if (!cpu->enable_pmu) { + /* PDCM is fixed1 bit for TDX */ + if (!cpu->enable_pmu && !is_tdx_vm()) { mark_unavailable_features(cpu, FEAT_1_ECX, env->user_features[FEAT_1_ECX] & CPUID_EXT_PDCM, "This feature is not available due to PMU being disabled"); @@ -8574,46 +9182,34 @@ static bool x86_cpu_update_smp_cache_topo(MachineState *ms, X86CPU *cpu, level = machine_get_cache_topo_level(ms, CACHE_LEVEL_AND_TYPE_L1D); if (level != CPU_TOPOLOGY_LEVEL_DEFAULT) { - env->cache_info_cpuid4.l1d_cache->share_level = level; - env->cache_info_amd.l1d_cache->share_level = level; + env->cache_info.l1d_cache->share_level = level; } else { machine_set_cache_topo_level(ms, CACHE_LEVEL_AND_TYPE_L1D, - env->cache_info_cpuid4.l1d_cache->share_level); - machine_set_cache_topo_level(ms, CACHE_LEVEL_AND_TYPE_L1D, - env->cache_info_amd.l1d_cache->share_level); + env->cache_info.l1d_cache->share_level); } level = machine_get_cache_topo_level(ms, CACHE_LEVEL_AND_TYPE_L1I); if (level != CPU_TOPOLOGY_LEVEL_DEFAULT) { - env->cache_info_cpuid4.l1i_cache->share_level = level; - env->cache_info_amd.l1i_cache->share_level = level; + env->cache_info.l1i_cache->share_level = level; } else { machine_set_cache_topo_level(ms, CACHE_LEVEL_AND_TYPE_L1I, - env->cache_info_cpuid4.l1i_cache->share_level); - machine_set_cache_topo_level(ms, CACHE_LEVEL_AND_TYPE_L1I, - env->cache_info_amd.l1i_cache->share_level); + env->cache_info.l1i_cache->share_level); } level = machine_get_cache_topo_level(ms, CACHE_LEVEL_AND_TYPE_L2); if (level != CPU_TOPOLOGY_LEVEL_DEFAULT) { - env->cache_info_cpuid4.l2_cache->share_level = level; - env->cache_info_amd.l2_cache->share_level = level; + env->cache_info.l2_cache->share_level = level; } else { machine_set_cache_topo_level(ms, CACHE_LEVEL_AND_TYPE_L2, - env->cache_info_cpuid4.l2_cache->share_level); - machine_set_cache_topo_level(ms, CACHE_LEVEL_AND_TYPE_L2, - env->cache_info_amd.l2_cache->share_level); + env->cache_info.l2_cache->share_level); } level = machine_get_cache_topo_level(ms, CACHE_LEVEL_AND_TYPE_L3); if (level != CPU_TOPOLOGY_LEVEL_DEFAULT) { - env->cache_info_cpuid4.l3_cache->share_level = level; - env->cache_info_amd.l3_cache->share_level = level; + env->cache_info.l3_cache->share_level = level; } else { machine_set_cache_topo_level(ms, CACHE_LEVEL_AND_TYPE_L3, - env->cache_info_cpuid4.l3_cache->share_level); - machine_set_cache_topo_level(ms, CACHE_LEVEL_AND_TYPE_L3, - env->cache_info_amd.l3_cache->share_level); + env->cache_info.l3_cache->share_level); } if (!machine_check_smp_cache(ms, errp)) { @@ -8637,6 +9233,16 @@ static void x86_cpu_realizefn(DeviceState *dev, Error **errp) tcg_cflags_set(cs, CF_PCREL); #endif + /* + * x-vendor-cpuid-only and v2 should be initernal only. But + * QEMU doesn't support "internal" property. + */ + if (!cpu->vendor_cpuid_only && cpu->vendor_cpuid_only_v2) { + error_setg(errp, "x-vendor-cpuid-only-v2 property " + "depends on x-vendor-cpuid-only"); + return; + } + if (cpu->apic_id == UNASSIGNED_APIC_ID) { error_setg(errp, "apic-id property was not initialized properly"); return; @@ -8840,24 +9446,22 @@ static void x86_cpu_realizefn(DeviceState *dev, Error **errp) "CPU model '%s' doesn't support legacy-cache=off", name); return; } - env->cache_info_cpuid2 = env->cache_info_cpuid4 = env->cache_info_amd = - *cache_info; + env->cache_info = *cache_info; } else { /* Build legacy cache information */ - env->cache_info_cpuid2.l1d_cache = &legacy_l1d_cache; - env->cache_info_cpuid2.l1i_cache = &legacy_l1i_cache; - env->cache_info_cpuid2.l2_cache = &legacy_l2_cache_cpuid2; - env->cache_info_cpuid2.l3_cache = &legacy_l3_cache; + if (!cpu->consistent_cache) { + env->enable_legacy_cpuid2_cache = true; + } - env->cache_info_cpuid4.l1d_cache = &legacy_l1d_cache; - env->cache_info_cpuid4.l1i_cache = &legacy_l1i_cache; - env->cache_info_cpuid4.l2_cache = &legacy_l2_cache; - env->cache_info_cpuid4.l3_cache = &legacy_l3_cache; + if (!cpu->vendor_cpuid_only_v2) { + env->enable_legacy_vendor_cache = true; + } - env->cache_info_amd.l1d_cache = &legacy_l1d_cache_amd; - env->cache_info_amd.l1i_cache = &legacy_l1i_cache_amd; - env->cache_info_amd.l2_cache = &legacy_l2_cache_amd; - env->cache_info_amd.l3_cache = &legacy_l3_cache; + if (IS_AMD_CPU(env)) { + env->cache_info = legacy_amd_cache_info; + } else { + env->cache_info = legacy_intel_cache_info; + } } #ifndef CONFIG_USER_ONLY @@ -9036,8 +9640,6 @@ static void x86_cpu_post_initfn(Object *obj) } } - accel_cpu_instance_init(CPU(obj)); - #ifndef CONFIG_USER_ONLY if (current_machine && current_machine->cgs) { x86_confidential_guest_cpu_instance_init( @@ -9112,6 +9714,8 @@ static void x86_cpu_initfn(Object *obj) if (xcc->model) { x86_cpu_load_model(cpu, xcc->model); } + + accel_cpu_instance_init(CPU(obj)); } static int64_t x86_cpu_get_arch_id(CPUState *cs) @@ -9358,6 +9962,7 @@ static const Property x86_cpu_properties[] = { DEFINE_PROP_STRING("hv-vendor-id", X86CPU, hyperv_vendor), DEFINE_PROP_BOOL("cpuid-0xb", X86CPU, enable_cpuid_0xb, true), DEFINE_PROP_BOOL("x-vendor-cpuid-only", X86CPU, vendor_cpuid_only, true), + DEFINE_PROP_BOOL("x-vendor-cpuid-only-v2", X86CPU, vendor_cpuid_only_v2, true), DEFINE_PROP_BOOL("x-amd-topoext-features-only", X86CPU, amd_topoext_features_only, true), DEFINE_PROP_BOOL("lmce", X86CPU, enable_lmce, false), DEFINE_PROP_BOOL("l3-cache", X86CPU, enable_l3_cache, true), @@ -9372,6 +9977,7 @@ static const Property x86_cpu_properties[] = { * own cache information (see x86_cpu_load_def()). */ DEFINE_PROP_BOOL("legacy-cache", X86CPU, legacy_cache, true), + DEFINE_PROP_BOOL("x-consistent-cache", X86CPU, consistent_cache, true), DEFINE_PROP_BOOL("legacy-multi-node", X86CPU, legacy_multi_node, false), DEFINE_PROP_BOOL("xen-vapic", X86CPU, xen_vapic, false), @@ -9393,6 +9999,7 @@ static const Property x86_cpu_properties[] = { DEFINE_PROP_BOOL("x-intel-pt-auto-level", X86CPU, intel_pt_auto_level, true), DEFINE_PROP_BOOL("x-l1-cache-per-thread", X86CPU, l1_cache_per_core, true), + DEFINE_PROP_BOOL("x-force-cpuid-0x1f", X86CPU, force_cpuid_0x1f, false), }; #ifndef CONFIG_USER_ONLY diff --git a/target/i386/cpu.h b/target/i386/cpu.h index 51e1013..f977fc4 100644 --- a/target/i386/cpu.h +++ b/target/i386/cpu.h @@ -1159,7 +1159,8 @@ uint64_t x86_cpu_get_supported_feature_word(X86CPU *cpu, FeatureWord w); /* PMM enabled */ #define CPUID_C000_0001_EDX_PMM_EN (1U << 13) -#define CPUID_VENDOR_SZ 12 +#define CPUID_VENDOR_SZ 12 +#define CPUID_MODEL_ID_SZ 48 #define CPUID_VENDOR_INTEL_1 0x756e6547 /* "Genu" */ #define CPUID_VENDOR_INTEL_2 0x49656e69 /* "ineI" */ @@ -2072,11 +2073,14 @@ typedef struct CPUArchState { /* Features that were explicitly enabled/disabled */ FeatureWordArray user_features; uint32_t cpuid_model[12]; - /* Cache information for CPUID. When legacy-cache=on, the cache data + /* + * Cache information for CPUID. When legacy-cache=on, the cache data * on each CPUID leaf will be different, because we keep compatibility * with old QEMU versions. */ - CPUCaches cache_info_cpuid2, cache_info_cpuid4, cache_info_amd; + CPUCaches cache_info; + bool enable_legacy_cpuid2_cache; + bool enable_legacy_vendor_cache; /* MTRRs */ uint64_t mtrr_fixed[11]; @@ -2196,7 +2200,6 @@ struct ArchCPU { bool expose_tcg; bool migratable; bool migrate_smi_count; - bool max_features; /* Enable all supported features automatically */ uint32_t apic_id; /* Enables publishing of TSC increment and Local APIC bus frequencies to @@ -2259,6 +2262,13 @@ struct ArchCPU { */ bool legacy_cache; + /* + * Compatibility bits for old machine types. + * If true, use the same cache model in CPUID leaf 0x2 + * and 0x4. + */ + bool consistent_cache; + /* Compatibility bits for old machine types. * If true decode the CPUID Function 0x8000001E_ECX to support multiple * nodes per processor @@ -2274,9 +2284,18 @@ struct ArchCPU { /* Enable auto level-increase for all CPUID leaves */ bool full_cpuid_auto_level; - /* Only advertise CPUID leaves defined by the vendor */ + /* + * Compatibility bits for old machine types (PC machine v6.0 and older). + * Only advertise CPUID leaves defined by the vendor. + */ bool vendor_cpuid_only; + /* + * Compatibility bits for old machine types (PC machine v10.0 and older). + * Only advertise CPUID leaves defined by the vendor. + */ + bool vendor_cpuid_only_v2; + /* Only advertise TOPOEXT features that AMD defines */ bool amd_topoext_features_only; @@ -2349,6 +2368,7 @@ struct X86CPUClass { */ const X86CPUModel *model; + bool max_features; /* Enable all supported features automatically */ bool host_cpuid_required; int ordering; bool migration_safe; @@ -2417,7 +2437,14 @@ static inline void cpu_x86_load_seg_cache(CPUX86State *env, SegmentCache *sc; unsigned int new_hflags; - sc = &env->segs[seg_reg]; + if (seg_reg == R_LDTR) { + sc = &env->ldt; + } else if (seg_reg == R_TR) { + sc = &env->tr; + } else { + sc = &env->segs[seg_reg]; + } + sc->selector = selector; sc->base = base; sc->limit = limit; @@ -2670,6 +2697,36 @@ static inline int32_t x86_get_a20_mask(CPUX86State *env) } } +static inline uint32_t x86_cpu_family(uint32_t eax) +{ + uint32_t family = (eax >> 8) & 0xf; + + if (family == 0xf) { + family += (eax >> 20) & 0xff; + } + + return family; +} + +static inline uint32_t x86_cpu_model(uint32_t eax) +{ + uint32_t family, model; + + family = x86_cpu_family(eax); + model = (eax >> 4) & 0xf; + + if (family >= 0x6) { + model += ((eax >> 16) & 0xf) << 4; + } + + return model; +} + +static inline uint32_t x86_cpu_stepping(uint32_t eax) +{ + return eax & 0xf; +} + static inline bool cpu_has_vmx(CPUX86State *env) { return env->features[FEAT_1_ECX] & CPUID_EXT_VMX; diff --git a/target/i386/host-cpu.c b/target/i386/host-cpu.c index 7512567..d5e2bb5 100644 --- a/target/i386/host-cpu.c +++ b/target/i386/host-cpu.c @@ -80,7 +80,6 @@ bool host_cpu_realizefn(CPUState *cs, Error **errp) return true; } -#define CPUID_MODEL_ID_SZ 48 /** * cpu_x86_fill_model_id: * Get CPUID model ID string from host CPU. @@ -118,13 +117,13 @@ void host_cpu_vendor_fms(char *vendor, int *family, int *model, int *stepping) host_cpuid(0x1, 0, &eax, &ebx, &ecx, &edx); if (family) { - *family = ((eax >> 8) & 0x0F) + ((eax >> 20) & 0xFF); + *family = x86_cpu_family(eax); } if (model) { - *model = ((eax >> 4) & 0x0F) | ((eax & 0xF0000) >> 12); + *model = x86_cpu_model(eax); } if (stepping) { - *stepping = eax & 0x0F; + *stepping = x86_cpu_stepping(eax); } } @@ -132,27 +131,27 @@ void host_cpu_instance_init(X86CPU *cpu) { X86CPUClass *xcc = X86_CPU_GET_CLASS(cpu); - if (xcc->model) { - char vendor[CPUID_VENDOR_SZ + 1]; - - host_cpu_vendor_fms(vendor, NULL, NULL, NULL); - object_property_set_str(OBJECT(cpu), "vendor", vendor, &error_abort); - } -} - -void host_cpu_max_instance_init(X86CPU *cpu) -{ char vendor[CPUID_VENDOR_SZ + 1] = { 0 }; char model_id[CPUID_MODEL_ID_SZ + 1] = { 0 }; int family, model, stepping; - /* Use max host physical address bits if -cpu max option is applied */ - object_property_set_bool(OBJECT(cpu), "host-phys-bits", true, &error_abort); - + /* + * setting vendor applies to both max/host and builtin_x86_defs CPU. + * FIXME: this probably should warn or should be skipped if vendors do + * not match, because family numbers are incompatible between Intel and AMD. + */ host_cpu_vendor_fms(vendor, &family, &model, &stepping); + object_property_set_str(OBJECT(cpu), "vendor", vendor, &error_abort); + + if (!xcc->max_features) { + return; + } + host_cpu_fill_model_id(model_id); - object_property_set_str(OBJECT(cpu), "vendor", vendor, &error_abort); + /* Use max host physical address bits if -cpu max option is applied */ + object_property_set_bool(OBJECT(cpu), "host-phys-bits", true, &error_abort); + object_property_set_int(OBJECT(cpu), "family", family, &error_abort); object_property_set_int(OBJECT(cpu), "model", model, &error_abort); object_property_set_int(OBJECT(cpu), "stepping", stepping, @@ -161,6 +160,15 @@ void host_cpu_max_instance_init(X86CPU *cpu) &error_abort); } +bool is_host_cpu_intel(void) +{ + char vendor[CPUID_VENDOR_SZ + 1]; + + host_cpu_vendor_fms(vendor, NULL, NULL, NULL); + + return g_str_equal(vendor, CPUID_VENDOR_INTEL); +} + static void host_cpu_class_init(ObjectClass *oc, const void *data) { X86CPUClass *xcc = X86_CPU_CLASS(oc); diff --git a/target/i386/host-cpu.h b/target/i386/host-cpu.h index b97ec01..10df4b3 100644 --- a/target/i386/host-cpu.h +++ b/target/i386/host-cpu.h @@ -17,4 +17,5 @@ bool host_cpu_realizefn(CPUState *cs, Error **errp); void host_cpu_vendor_fms(char *vendor, int *family, int *model, int *stepping); +bool is_host_cpu_intel(void); #endif /* HOST_CPU_H */ diff --git a/target/i386/hvf/hvf-cpu.c b/target/i386/hvf/hvf-cpu.c index dfdda70..94ee096 100644 --- a/target/i386/hvf/hvf-cpu.c +++ b/target/i386/hvf/hvf-cpu.c @@ -21,8 +21,6 @@ static void hvf_cpu_max_instance_init(X86CPU *cpu) { CPUX86State *env = &cpu->env; - host_cpu_max_instance_init(cpu); - env->cpuid_min_level = hvf_get_supported_cpuid(0x0, 0, R_EAX); env->cpuid_min_xlevel = @@ -61,13 +59,14 @@ static void hvf_cpu_xsave_init(void) static void hvf_cpu_instance_init(CPUState *cs) { X86CPU *cpu = X86_CPU(cs); + X86CPUClass *xcc = X86_CPU_GET_CLASS(cpu); host_cpu_instance_init(cpu); /* Special cases not set in the X86CPUDefinition structs: */ /* TODO: in-kernel irqchip for hvf */ - if (cpu->max_features) { + if (xcc->max_features) { hvf_cpu_max_instance_init(cpu); } diff --git a/target/i386/kvm/kvm-cpu.c b/target/i386/kvm/kvm-cpu.c index 16bde4d..89a7953 100644 --- a/target/i386/kvm/kvm-cpu.c +++ b/target/i386/kvm/kvm-cpu.c @@ -41,6 +41,7 @@ static void kvm_set_guest_phys_bits(CPUState *cs) static bool kvm_cpu_realizefn(CPUState *cs, Error **errp) { X86CPU *cpu = X86_CPU(cs); + X86CPUClass *xcc = X86_CPU_GET_CLASS(cpu); CPUX86State *env = &cpu->env; bool ret; @@ -63,7 +64,7 @@ static bool kvm_cpu_realizefn(CPUState *cs, Error **errp) * check/update ucode_rev, phys_bits, guest_phys_bits, mwait * cpu_common_realizefn() (via xcc->parent_realize) */ - if (cpu->max_features) { + if (xcc->max_features) { if (enable_cpu_pm) { if (kvm_has_waitpkg()) { env->features[FEAT_7_0_ECX] |= CPUID_7_0_ECX_WAITPKG; @@ -72,7 +73,7 @@ static bool kvm_cpu_realizefn(CPUState *cs, Error **errp) if (env->features[FEAT_1_ECX] & CPUID_EXT_MONITOR) { host_cpuid(5, 0, &cpu->mwait.eax, &cpu->mwait.ebx, &cpu->mwait.ecx, &cpu->mwait.edx); - } + } } if (cpu->ucode_rev == 0) { cpu->ucode_rev = @@ -108,7 +109,7 @@ static void kvm_cpu_max_instance_init(X86CPU *cpu) CPUX86State *env = &cpu->env; KVMState *s = kvm_state; - host_cpu_max_instance_init(cpu); + object_property_set_bool(OBJECT(cpu), "pmu", true, &error_abort); if (lmce_supported()) { object_property_set_bool(OBJECT(cpu), "lmce", true, &error_abort); @@ -216,7 +217,7 @@ static void kvm_cpu_instance_init(CPUState *cs) x86_cpu_apply_props(cpu, kvm_default_props); } - if (cpu->max_features) { + if (xcc->max_features) { kvm_cpu_max_instance_init(cpu); } diff --git a/target/i386/kvm/kvm.c b/target/i386/kvm/kvm.c index 234878c..e8c8be0 100644 --- a/target/i386/kvm/kvm.c +++ b/target/i386/kvm/kvm.c @@ -2259,7 +2259,7 @@ int kvm_arch_init_vcpu(CPUState *cs) cpuid_i = kvm_x86_build_cpuid(env, cpuid_data.entries, cpuid_i); cpuid_data.cpuid.nent = cpuid_i; - if (((env->cpuid_version >> 8)&0xF) >= 6 + if (x86_cpu_family(env->cpuid_version) >= 6 && (env->features[FEAT_1_EDX] & (CPUID_MCE | CPUID_MCA)) == (CPUID_MCE | CPUID_MCA)) { uint64_t mcg_cap, unsupported_caps; @@ -6182,6 +6182,9 @@ int kvm_arch_handle_exit(CPUState *cs, struct kvm_run *run) case TDVMCALL_GET_TD_VM_CALL_INFO: tdx_handle_get_tdvmcall_info(cpu, run); break; + case TDVMCALL_SETUP_EVENT_NOTIFY_INTERRUPT: + tdx_handle_setup_event_notify_interrupt(cpu, run); + break; } ret = 0; break; diff --git a/target/i386/kvm/tdx-quote-generator.c b/target/i386/kvm/tdx-quote-generator.c index f59715f..dee8334 100644 --- a/target/i386/kvm/tdx-quote-generator.c +++ b/target/i386/kvm/tdx-quote-generator.c @@ -75,7 +75,9 @@ static void tdx_generate_quote_cleanup(TdxGenerateQuoteTask *task) { timer_del(&task->timer); - g_source_remove(task->watch); + if (task->watch) { + g_source_remove(task->watch); + } qio_channel_close(QIO_CHANNEL(task->sioc), NULL); object_unref(OBJECT(task->sioc)); diff --git a/target/i386/kvm/tdx-stub.c b/target/i386/kvm/tdx-stub.c index 76fee49..1f0e108 100644 --- a/target/i386/kvm/tdx-stub.c +++ b/target/i386/kvm/tdx-stub.c @@ -26,3 +26,7 @@ void tdx_handle_get_quote(X86CPU *cpu, struct kvm_run *run) void tdx_handle_get_tdvmcall_info(X86CPU *cpu, struct kvm_run *run) { } + +void tdx_handle_setup_event_notify_interrupt(X86CPU *cpu, struct kvm_run *run) +{ +} diff --git a/target/i386/kvm/tdx.c b/target/i386/kvm/tdx.c index e809e4b..7d69d6d 100644 --- a/target/i386/kvm/tdx.c +++ b/target/i386/kvm/tdx.c @@ -28,10 +28,13 @@ #include "cpu.h" #include "cpu-internal.h" #include "host-cpu.h" +#include "hw/i386/apic_internal.h" +#include "hw/i386/apic-msidef.h" #include "hw/i386/e820_memory_layout.h" #include "hw/i386/tdvf.h" #include "hw/i386/x86.h" #include "hw/i386/tdvf-hob.h" +#include "hw/pci/msi.h" #include "kvm_i386.h" #include "tdx.h" #include "tdx-quote-generator.h" @@ -1123,6 +1126,28 @@ int tdx_parse_tdvf(void *flash_ptr, int size) return tdvf_parse_metadata(&tdx_guest->tdvf, flash_ptr, size); } +static void tdx_inject_interrupt(uint32_t apicid, uint32_t vector) +{ + int ret; + + if (vector < 32 || vector > 255) { + return; + } + + MSIMessage msg = { + .address = ((apicid & 0xff) << MSI_ADDR_DEST_ID_SHIFT) | + (((uint64_t)apicid & 0xffffff00) << 32), + .data = vector | (APIC_DM_FIXED << MSI_DATA_DELIVERY_MODE_SHIFT), + }; + + ret = kvm_irqchip_send_msi(kvm_state, msg); + if (ret < 0) { + /* In this case, no better way to tell it to guest. Log it. */ + error_report("TDX: injection interrupt %d failed, interrupt lost (%s).", + vector, strerror(-ret)); + } +} + static void tdx_get_quote_completion(TdxGenerateQuoteTask *task) { TdxGuest *tdx = task->opaque; @@ -1154,6 +1179,9 @@ static void tdx_get_quote_completion(TdxGenerateQuoteTask *task) error_report("TDX: get-quote: failed to update GetQuote header."); } + tdx_inject_interrupt(tdx_guest->event_notify_apicid, + tdx_guest->event_notify_vector); + g_free(task->send_data); g_free(task->receive_buf); g_free(task); @@ -1256,20 +1284,45 @@ out_free: g_free(task); } +#define SUPPORTED_TDVMCALLINFO_1_R11 (TDG_VP_VMCALL_SUBFUNC_SET_EVENT_NOTIFY_INTERRUPT) +#define SUPPORTED_TDVMCALLINFO_1_R12 (0) + void tdx_handle_get_tdvmcall_info(X86CPU *cpu, struct kvm_run *run) { if (run->tdx.get_tdvmcall_info.leaf != 1) { - return; + return; } - run->tdx.get_tdvmcall_info.r11 = TDG_VP_VMCALL_SUBFUNC_GET_QUOTE; - run->tdx.get_tdvmcall_info.r12 = 0; + run->tdx.get_tdvmcall_info.r11 = (tdx_caps->user_tdvmcallinfo_1_r11 & + SUPPORTED_TDVMCALLINFO_1_R11) | + tdx_caps->kernel_tdvmcallinfo_1_r11; + run->tdx.get_tdvmcall_info.r12 = (tdx_caps->user_tdvmcallinfo_1_r12 & + SUPPORTED_TDVMCALLINFO_1_R12) | + tdx_caps->kernel_tdvmcallinfo_1_r12; run->tdx.get_tdvmcall_info.r13 = 0; run->tdx.get_tdvmcall_info.r14 = 0; + + run->tdx.get_tdvmcall_info.ret = TDG_VP_VMCALL_SUCCESS; +} + +void tdx_handle_setup_event_notify_interrupt(X86CPU *cpu, struct kvm_run *run) +{ + uint64_t vector = run->tdx.setup_event_notify.vector; + + if (vector >= 32 && vector < 256) { + qemu_mutex_lock(&tdx_guest->lock); + tdx_guest->event_notify_vector = vector; + tdx_guest->event_notify_apicid = cpu->apic_id; + qemu_mutex_unlock(&tdx_guest->lock); + run->tdx.setup_event_notify.ret = TDG_VP_VMCALL_SUCCESS; + } else { + run->tdx.setup_event_notify.ret = TDG_VP_VMCALL_INVALID_OPERAND; + } } static void tdx_panicked_on_fatal_error(X86CPU *cpu, uint64_t error_code, - char *message, uint64_t gpa) + char *message, bool has_gpa, + uint64_t gpa) { GuestPanicInformation *panic_info; @@ -1278,6 +1331,7 @@ static void tdx_panicked_on_fatal_error(X86CPU *cpu, uint64_t error_code, panic_info->u.tdx.error_code = (uint32_t) error_code; panic_info->u.tdx.message = message; panic_info->u.tdx.gpa = gpa; + panic_info->u.tdx.has_gpa = has_gpa; qemu_system_guest_panicked(panic_info); } @@ -1297,6 +1351,7 @@ int tdx_handle_report_fatal_error(X86CPU *cpu, struct kvm_run *run) char *message = NULL; uint64_t *tmp; uint64_t gpa = -1ull; + bool has_gpa = false; if (error_code & 0xffff) { error_report("TDX: REPORT_FATAL_ERROR: invalid error code: 0x%"PRIx64, @@ -1329,9 +1384,10 @@ int tdx_handle_report_fatal_error(X86CPU *cpu, struct kvm_run *run) if (error_code & TDX_REPORT_FATAL_ERROR_GPA_VALID) { gpa = run->system_event.data[R_R13]; + has_gpa = true; } - tdx_panicked_on_fatal_error(cpu, error_code, message, gpa); + tdx_panicked_on_fatal_error(cpu, error_code, message, has_gpa, gpa); return -1; } @@ -1468,6 +1524,9 @@ static void tdx_guest_init(Object *obj) NULL, NULL); qemu_mutex_init(&tdx->lock); + + tdx->event_notify_vector = -1; + tdx->event_notify_apicid = -1; } static void tdx_guest_finalize(Object *obj) diff --git a/target/i386/kvm/tdx.h b/target/i386/kvm/tdx.h index 35a09c1..1c38faf 100644 --- a/target/i386/kvm/tdx.h +++ b/target/i386/kvm/tdx.h @@ -25,6 +25,7 @@ typedef struct TdxGuestClass { #define TDVMCALL_GET_TD_VM_CALL_INFO 0x10000 #define TDVMCALL_GET_QUOTE 0x10002 +#define TDVMCALL_SETUP_EVENT_NOTIFY_INTERRUPT 0x10004 #define TDG_VP_VMCALL_SUCCESS 0x0000000000000000ULL #define TDG_VP_VMCALL_RETRY 0x0000000000000001ULL @@ -32,7 +33,7 @@ typedef struct TdxGuestClass { #define TDG_VP_VMCALL_GPA_INUSE 0x8000000000000001ULL #define TDG_VP_VMCALL_ALIGN_ERROR 0x8000000000000002ULL -#define TDG_VP_VMCALL_SUBFUNC_GET_QUOTE 0x0000000000000001ULL +#define TDG_VP_VMCALL_SUBFUNC_SET_EVENT_NOTIFY_INTERRUPT BIT_ULL(1) enum TdxRamType { TDX_RAM_UNACCEPTED, @@ -66,6 +67,9 @@ typedef struct TdxGuest { /* GetQuote */ SocketAddress *qg_sock_addr; int num; + + uint32_t event_notify_vector; + uint32_t event_notify_apicid; } TdxGuest; #ifdef CONFIG_TDX @@ -80,5 +84,6 @@ int tdx_parse_tdvf(void *flash_ptr, int size); int tdx_handle_report_fatal_error(X86CPU *cpu, struct kvm_run *run); void tdx_handle_get_quote(X86CPU *cpu, struct kvm_run *run); void tdx_handle_get_tdvmcall_info(X86CPU *cpu, struct kvm_run *run); +void tdx_handle_setup_event_notify_interrupt(X86CPU *cpu, struct kvm_run *run); #endif /* QEMU_I386_TDX_H */ diff --git a/target/i386/kvm/vmsr_energy.c b/target/i386/kvm/vmsr_energy.c index d6aad52..58ce3df 100644 --- a/target/i386/kvm/vmsr_energy.c +++ b/target/i386/kvm/vmsr_energy.c @@ -27,15 +27,6 @@ char *vmsr_compute_default_paths(void) return g_build_filename(state, "run", "qemu-vmsr-helper.sock", NULL); } -bool is_host_cpu_intel(void) -{ - char vendor[CPUID_VENDOR_SZ + 1]; - - host_cpu_vendor_fms(vendor, NULL, NULL, NULL); - - return g_str_equal(vendor, CPUID_VENDOR_INTEL); -} - int is_rapl_enabled(void) { const char *path = "/sys/class/powercap/intel-rapl/enabled"; diff --git a/target/i386/kvm/vmsr_energy.h b/target/i386/kvm/vmsr_energy.h index 16cc1f4..151bcbd 100644 --- a/target/i386/kvm/vmsr_energy.h +++ b/target/i386/kvm/vmsr_energy.h @@ -94,6 +94,5 @@ double vmsr_get_ratio(uint64_t e_delta, unsigned long long delta_ticks, unsigned int maxticks); void vmsr_init_topo_info(X86CPUTopoInfo *topo_info, const MachineState *ms); -bool is_host_cpu_intel(void); int is_rapl_enabled(void); #endif /* VMSR_ENERGY_H */ diff --git a/target/i386/meson.build b/target/i386/meson.build index c1aacea..092af34 100644 --- a/target/i386/meson.build +++ b/target/i386/meson.build @@ -11,6 +11,8 @@ i386_ss.add(when: 'CONFIG_SEV', if_true: files('host-cpu.c', 'confidential-guest # x86 cpu type i386_ss.add(when: 'CONFIG_KVM', if_true: files('host-cpu.c')) i386_ss.add(when: 'CONFIG_HVF', if_true: files('host-cpu.c')) +i386_ss.add(when: 'CONFIG_WHPX', if_true: files('host-cpu.c')) +i386_ss.add(when: 'CONFIG_NVMM', if_true: files('host-cpu.c')) i386_system_ss = ss.source_set() i386_system_ss.add(files( diff --git a/target/i386/nvmm/nvmm-all.c b/target/i386/nvmm/nvmm-all.c index b4a4d50..11c2630 100644 --- a/target/i386/nvmm/nvmm-all.c +++ b/target/i386/nvmm/nvmm-all.c @@ -19,6 +19,8 @@ #include "qemu/error-report.h" #include "qapi/error.h" #include "qemu/queue.h" +#include "accel/accel-cpu-target.h" +#include "host-cpu.h" #include "migration/blocker.h" #include "strings.h" @@ -1207,10 +1209,33 @@ static const TypeInfo nvmm_accel_type = { .class_init = nvmm_accel_class_init, }; +static void nvmm_cpu_instance_init(CPUState *cs) +{ + X86CPU *cpu = X86_CPU(cs); + + host_cpu_instance_init(cpu); +} + +static void nvmm_cpu_accel_class_init(ObjectClass *oc, const void *data) +{ + AccelCPUClass *acc = ACCEL_CPU_CLASS(oc); + + acc->cpu_instance_init = nvmm_cpu_instance_init; +} + +static const TypeInfo nvmm_cpu_accel_type = { + .name = ACCEL_CPU_NAME("nvmm"), + + .parent = TYPE_ACCEL_CPU, + .class_init = nvmm_cpu_accel_class_init, + .abstract = true, +}; + static void nvmm_type_init(void) { type_register_static(&nvmm_accel_type); + type_register_static(&nvmm_cpu_accel_type); } type_init(nvmm_type_init); diff --git a/target/i386/sev.c b/target/i386/sev.c index 1a12f06..1057b8a 100644 --- a/target/i386/sev.c +++ b/target/i386/sev.c @@ -41,7 +41,9 @@ #include "confidential-guest.h" #include "hw/i386/pc.h" #include "system/address-spaces.h" +#include "hw/i386/e820_memory_layout.h" #include "qemu/queue.h" +#include "qemu/cutils.h" OBJECT_DECLARE_TYPE(SevCommonState, SevCommonStateClass, SEV_COMMON) OBJECT_DECLARE_TYPE(SevGuestState, SevCommonStateClass, SEV_GUEST) @@ -50,6 +52,15 @@ OBJECT_DECLARE_TYPE(SevSnpGuestState, SevCommonStateClass, SEV_SNP_GUEST) /* hard code sha256 digest size */ #define HASH_SIZE 32 +/* Hard coded GPA that KVM uses for the VMSA */ +#define KVM_VMSA_GPA 0xFFFFFFFFF000 + +/* Convert between SEV-ES VMSA and SegmentCache flags/attributes */ +#define FLAGS_VMSA_TO_SEGCACHE(flags) \ + ((((flags) & 0xff00) << 12) | (((flags) & 0xff) << 8)) +#define FLAGS_SEGCACHE_TO_VMSA(flags) \ + ((((flags) & 0xff00) >> 8) | (((flags) & 0xf00000) >> 12)) + typedef struct QEMU_PACKED SevHashTableEntry { QemuUUID guid; uint16_t len; @@ -89,6 +100,14 @@ typedef struct QEMU_PACKED SevHashTableDescriptor { uint32_t size; } SevHashTableDescriptor; +typedef struct SevLaunchVmsa { + QTAILQ_ENTRY(SevLaunchVmsa) next; + + uint16_t cpu_index; + uint64_t gpa; + struct sev_es_save_area vmsa; +} SevLaunchVmsa; + struct SevCommonState { X86ConfidentialGuest parent_obj; @@ -99,6 +118,8 @@ struct SevCommonState { uint32_t cbitpos; uint32_t reduced_phys_bits; bool kernel_hashes; + uint64_t sev_features; + uint64_t supported_sev_features; /* runtime state */ uint8_t api_major; @@ -107,9 +128,7 @@ struct SevCommonState { int sev_fd; SevState state; - uint32_t reset_cs; - uint32_t reset_ip; - bool reset_data_valid; + QTAILQ_HEAD(, SevLaunchVmsa) launch_vmsa; }; struct SevCommonStateClass { @@ -122,7 +141,8 @@ struct SevCommonStateClass { Error **errp); int (*launch_start)(SevCommonState *sev_common); void (*launch_finish)(SevCommonState *sev_common); - int (*launch_update_data)(SevCommonState *sev_common, hwaddr gpa, uint8_t *ptr, size_t len); + int (*launch_update_data)(SevCommonState *sev_common, hwaddr gpa, + uint8_t *ptr, size_t len, Error **errp); int (*kvm_init)(ConfidentialGuestSupport *cgs, Error **errp); }; @@ -363,6 +383,288 @@ static struct RAMBlockNotifier sev_ram_notifier = { .ram_block_removed = sev_ram_block_removed, }; +static void sev_apply_cpu_context(CPUState *cpu) +{ + SevCommonState *sev_common = SEV_COMMON(MACHINE(qdev_get_machine())->cgs); + X86CPU *x86; + CPUX86State *env; + struct SevLaunchVmsa *launch_vmsa; + + /* See if an initial VMSA has been provided for this CPU */ + QTAILQ_FOREACH(launch_vmsa, &sev_common->launch_vmsa, next) + { + if (cpu->cpu_index == launch_vmsa->cpu_index) { + x86 = X86_CPU(cpu); + env = &x86->env; + + /* + * Ideally we would provide the VMSA directly to kvm which would + * ensure that the resulting initial VMSA measurement which is + * calculated during KVM_SEV_LAUNCH_UPDATE_VMSA is calculated from + * exactly what we provide here. Currently this is not possible so + * we need to copy the parts of the VMSA structure that we currently + * support into the CPU state. + */ + cpu_load_efer(env, launch_vmsa->vmsa.efer); + cpu_x86_update_cr4(env, launch_vmsa->vmsa.cr4); + cpu_x86_update_cr0(env, launch_vmsa->vmsa.cr0); + cpu_x86_update_cr3(env, launch_vmsa->vmsa.cr3); + env->xcr0 = launch_vmsa->vmsa.xcr0; + env->pat = launch_vmsa->vmsa.g_pat; + + cpu_x86_load_seg_cache( + env, R_CS, launch_vmsa->vmsa.cs.selector, + launch_vmsa->vmsa.cs.base, launch_vmsa->vmsa.cs.limit, + FLAGS_VMSA_TO_SEGCACHE(launch_vmsa->vmsa.cs.attrib)); + cpu_x86_load_seg_cache( + env, R_DS, launch_vmsa->vmsa.ds.selector, + launch_vmsa->vmsa.ds.base, launch_vmsa->vmsa.ds.limit, + FLAGS_VMSA_TO_SEGCACHE(launch_vmsa->vmsa.ds.attrib)); + cpu_x86_load_seg_cache( + env, R_ES, launch_vmsa->vmsa.es.selector, + launch_vmsa->vmsa.es.base, launch_vmsa->vmsa.es.limit, + FLAGS_VMSA_TO_SEGCACHE(launch_vmsa->vmsa.es.attrib)); + cpu_x86_load_seg_cache( + env, R_FS, launch_vmsa->vmsa.fs.selector, + launch_vmsa->vmsa.fs.base, launch_vmsa->vmsa.fs.limit, + FLAGS_VMSA_TO_SEGCACHE(launch_vmsa->vmsa.fs.attrib)); + cpu_x86_load_seg_cache( + env, R_GS, launch_vmsa->vmsa.gs.selector, + launch_vmsa->vmsa.gs.base, launch_vmsa->vmsa.gs.limit, + FLAGS_VMSA_TO_SEGCACHE(launch_vmsa->vmsa.gs.attrib)); + cpu_x86_load_seg_cache( + env, R_SS, launch_vmsa->vmsa.ss.selector, + launch_vmsa->vmsa.ss.base, launch_vmsa->vmsa.ss.limit, + FLAGS_VMSA_TO_SEGCACHE(launch_vmsa->vmsa.ss.attrib)); + + env->gdt.base = launch_vmsa->vmsa.gdtr.base; + env->gdt.limit = launch_vmsa->vmsa.gdtr.limit; + env->gdt.flags = + FLAGS_VMSA_TO_SEGCACHE(launch_vmsa->vmsa.gdtr.attrib); + env->idt.base = launch_vmsa->vmsa.idtr.base; + env->idt.limit = launch_vmsa->vmsa.idtr.limit; + env->idt.flags = + FLAGS_VMSA_TO_SEGCACHE(launch_vmsa->vmsa.idtr.attrib); + + cpu_x86_load_seg_cache( + env, R_LDTR, launch_vmsa->vmsa.ldtr.selector, + launch_vmsa->vmsa.ldtr.base, launch_vmsa->vmsa.ldtr.limit, + FLAGS_VMSA_TO_SEGCACHE(launch_vmsa->vmsa.ldtr.attrib)); + cpu_x86_load_seg_cache( + env, R_TR, launch_vmsa->vmsa.tr.selector, + launch_vmsa->vmsa.ldtr.base, launch_vmsa->vmsa.tr.limit, + FLAGS_VMSA_TO_SEGCACHE(launch_vmsa->vmsa.tr.attrib)); + + env->dr[6] = launch_vmsa->vmsa.dr6; + env->dr[7] = launch_vmsa->vmsa.dr7; + + env->regs[R_EAX] = launch_vmsa->vmsa.rax; + env->regs[R_ECX] = launch_vmsa->vmsa.rcx; + env->regs[R_EDX] = launch_vmsa->vmsa.rdx; + env->regs[R_EBX] = launch_vmsa->vmsa.rbx; + env->regs[R_ESP] = launch_vmsa->vmsa.rsp; + env->regs[R_EBP] = launch_vmsa->vmsa.rbp; + env->regs[R_ESI] = launch_vmsa->vmsa.rsi; + env->regs[R_EDI] = launch_vmsa->vmsa.rdi; +#ifdef TARGET_X86_64 + env->regs[R_R8] = launch_vmsa->vmsa.r8; + env->regs[R_R9] = launch_vmsa->vmsa.r9; + env->regs[R_R10] = launch_vmsa->vmsa.r10; + env->regs[R_R11] = launch_vmsa->vmsa.r11; + env->regs[R_R12] = launch_vmsa->vmsa.r12; + env->regs[R_R13] = launch_vmsa->vmsa.r13; + env->regs[R_R14] = launch_vmsa->vmsa.r14; + env->regs[R_R15] = launch_vmsa->vmsa.r15; +#endif + env->eip = launch_vmsa->vmsa.rip; + env->eflags = launch_vmsa->vmsa.rflags; + + cpu_set_fpuc(env, launch_vmsa->vmsa.x87_fcw); + env->mxcsr = launch_vmsa->vmsa.mxcsr; + + break; + } + } +} + +static int check_sev_features(SevCommonState *sev_common, uint64_t sev_features, + Error **errp) +{ + /* + * Ensure SEV_FEATURES is configured for correct SEV hardware and that + * the requested features are supported. If SEV-SNP is enabled then + * that feature must be enabled, otherwise it must be cleared. + */ + if (sev_snp_enabled() && !(sev_features & SVM_SEV_FEAT_SNP_ACTIVE)) { + error_setg( + errp, + "%s: SEV_SNP is enabled but is not enabled in VMSA sev_features", + __func__); + return -1; + } else if (!sev_snp_enabled() && + (sev_features & SVM_SEV_FEAT_SNP_ACTIVE)) { + error_setg( + errp, + "%s: SEV_SNP is not enabled but is enabled in VMSA sev_features", + __func__); + return -1; + } + if (sev_features & ~sev_common->supported_sev_features) { + error_setg(errp, + "%s: VMSA contains unsupported sev_features: %lX, " + "supported features: %lX", + __func__, sev_features, sev_common->supported_sev_features); + return -1; + } + return 0; +} + +static int check_vmsa_supported(SevCommonState *sev_common, hwaddr gpa, + const struct sev_es_save_area *vmsa, + Error **errp) +{ + struct sev_es_save_area vmsa_check; + + /* + * KVM always populates the VMSA at a fixed GPA which cannot be modified + * from userspace. Specifying a different GPA will not prevent the guest + * from starting but will cause the launch measurement to be different + * from expected. Therefore check that the provided GPA matches the KVM + * hardcoded value. + */ + if (gpa != KVM_VMSA_GPA) { + error_setg(errp, + "%s: The VMSA GPA must be %lX but is specified as %lX", + __func__, KVM_VMSA_GPA, gpa); + return -1; + } + + /* + * Clear all supported fields so we can then check the entire structure + * is zero. + */ + memcpy(&vmsa_check, vmsa, sizeof(struct sev_es_save_area)); + memset(&vmsa_check.es, 0, sizeof(vmsa_check.es)); + memset(&vmsa_check.cs, 0, sizeof(vmsa_check.cs)); + memset(&vmsa_check.ss, 0, sizeof(vmsa_check.ss)); + memset(&vmsa_check.ds, 0, sizeof(vmsa_check.ds)); + memset(&vmsa_check.fs, 0, sizeof(vmsa_check.fs)); + memset(&vmsa_check.gs, 0, sizeof(vmsa_check.gs)); + memset(&vmsa_check.gdtr, 0, sizeof(vmsa_check.gdtr)); + memset(&vmsa_check.idtr, 0, sizeof(vmsa_check.idtr)); + memset(&vmsa_check.ldtr, 0, sizeof(vmsa_check.ldtr)); + memset(&vmsa_check.tr, 0, sizeof(vmsa_check.tr)); + vmsa_check.efer = 0; + vmsa_check.cr0 = 0; + vmsa_check.cr3 = 0; + vmsa_check.cr4 = 0; + vmsa_check.xcr0 = 0; + vmsa_check.dr6 = 0; + vmsa_check.dr7 = 0; + vmsa_check.rax = 0; + vmsa_check.rcx = 0; + vmsa_check.rdx = 0; + vmsa_check.rbx = 0; + vmsa_check.rsp = 0; + vmsa_check.rbp = 0; + vmsa_check.rsi = 0; + vmsa_check.rdi = 0; + vmsa_check.r8 = 0; + vmsa_check.r9 = 0; + vmsa_check.r10 = 0; + vmsa_check.r11 = 0; + vmsa_check.r12 = 0; + vmsa_check.r13 = 0; + vmsa_check.r14 = 0; + vmsa_check.r15 = 0; + vmsa_check.rip = 0; + vmsa_check.rflags = 0; + + vmsa_check.g_pat = 0; + vmsa_check.xcr0 = 0; + + vmsa_check.x87_fcw = 0; + vmsa_check.mxcsr = 0; + + if (check_sev_features(sev_common, vmsa_check.sev_features, errp) < 0) { + return -1; + } + vmsa_check.sev_features = 0; + + if (!buffer_is_zero(&vmsa_check, sizeof(vmsa_check))) { + error_setg(errp, + "%s: The VMSA contains fields that are not " + "synchronized with KVM. Continuing would result in " + "either unpredictable guest behavior, or a " + "mismatched launch measurement.", + __func__); + return -1; + } + return 0; +} + +static int sev_set_cpu_context(uint16_t cpu_index, const void *ctx, + uint32_t ctx_len, hwaddr gpa, Error **errp) +{ + SevCommonState *sev_common = SEV_COMMON(MACHINE(qdev_get_machine())->cgs); + SevLaunchVmsa *launch_vmsa; + CPUState *cpu; + bool exists = false; + + /* + * Setting the CPU context is only supported for SEV-ES and SEV-SNP. The + * context buffer will contain a sev_es_save_area from the Linux kernel + * which is defined by "Table B-4. VMSA Layout, State Save Area for SEV-ES" + * in the AMD64 APM, Volume 2. + */ + + if (!sev_es_enabled()) { + error_setg(errp, "SEV: unable to set CPU context: Not supported"); + return -1; + } + + if (ctx_len < sizeof(struct sev_es_save_area)) { + error_setg(errp, "SEV: unable to set CPU context: " + "Invalid context provided"); + return -1; + } + + cpu = qemu_get_cpu(cpu_index); + if (!cpu) { + error_setg(errp, "SEV: unable to set CPU context for out of bounds " + "CPU index %d", cpu_index); + return -1; + } + + /* + * If the context of this VP has already been set then replace it with the + * new context. + */ + QTAILQ_FOREACH(launch_vmsa, &sev_common->launch_vmsa, next) + { + if (cpu_index == launch_vmsa->cpu_index) { + launch_vmsa->gpa = gpa; + memcpy(&launch_vmsa->vmsa, ctx, sizeof(launch_vmsa->vmsa)); + exists = true; + break; + } + } + + if (!exists) { + /* New VP context */ + launch_vmsa = g_new0(SevLaunchVmsa, 1); + memcpy(&launch_vmsa->vmsa, ctx, sizeof(launch_vmsa->vmsa)); + launch_vmsa->cpu_index = cpu_index; + launch_vmsa->gpa = gpa; + QTAILQ_INSERT_TAIL(&sev_common->launch_vmsa, launch_vmsa, next); + } + + /* Synchronise the VMSA with the current CPU state */ + sev_apply_cpu_context(cpu); + + return 0; +} + bool sev_enabled(void) { @@ -970,9 +1272,8 @@ sev_snp_adjust_cpuid_features(X86ConfidentialGuest *cg, uint32_t feature, uint32 return value; } -static int -sev_launch_update_data(SevCommonState *sev_common, hwaddr gpa, - uint8_t *addr, size_t len) +static int sev_launch_update_data(SevCommonState *sev_common, hwaddr gpa, + uint8_t *addr, size_t len, Error **errp) { int ret, fw_error; struct kvm_sev_launch_update_data update; @@ -987,8 +1288,8 @@ sev_launch_update_data(SevCommonState *sev_common, hwaddr gpa, ret = sev_ioctl(sev_common->sev_fd, KVM_SEV_LAUNCH_UPDATE_DATA, &update, &fw_error); if (ret) { - error_report("%s: LAUNCH_UPDATE ret=%d fw_error=%d '%s'", - __func__, ret, fw_error, fw_error_to_str(fw_error)); + error_setg(errp, "%s: LAUNCH_UPDATE ret=%d fw_error=%d '%s'", __func__, + ret, fw_error, fw_error_to_str(fw_error)); } return ret; @@ -998,6 +1299,16 @@ static int sev_launch_update_vmsa(SevGuestState *sev_guest) { int ret, fw_error; + CPUState *cpu; + + /* + * The initial CPU state is measured as part of KVM_SEV_LAUNCH_UPDATE_VMSA. + * Synchronise the CPU state to any provided launch VMSA structures. + */ + CPU_FOREACH(cpu) { + sev_apply_cpu_context(cpu); + } + ret = sev_ioctl(SEV_COMMON(sev_guest)->sev_fd, KVM_SEV_LAUNCH_UPDATE_VMSA, NULL, &fw_error); @@ -1116,8 +1427,8 @@ sev_launch_finish(SevCommonState *sev_common) migrate_add_blocker(&sev_mig_blocker, &error_fatal); } -static int -snp_launch_update_data(uint64_t gpa, void *hva, size_t len, int type) +static int snp_launch_update_data(uint64_t gpa, void *hva, size_t len, + int type, Error **errp) { SevLaunchUpdateData *data; @@ -1132,23 +1443,21 @@ snp_launch_update_data(uint64_t gpa, void *hva, size_t len, int type) return 0; } -static int -sev_snp_launch_update_data(SevCommonState *sev_common, hwaddr gpa, - uint8_t *ptr, size_t len) +static int sev_snp_launch_update_data(SevCommonState *sev_common, hwaddr gpa, + uint8_t *ptr, size_t len, Error **errp) { - int ret = snp_launch_update_data(gpa, ptr, len, - KVM_SEV_SNP_PAGE_TYPE_NORMAL); - return ret; + return snp_launch_update_data(gpa, ptr, len, + KVM_SEV_SNP_PAGE_TYPE_NORMAL, errp); } static int sev_snp_cpuid_info_fill(SnpCpuidInfo *snp_cpuid_info, - const KvmCpuidInfo *kvm_cpuid_info) + const KvmCpuidInfo *kvm_cpuid_info, Error **errp) { size_t i; if (kvm_cpuid_info->cpuid.nent > SNP_CPUID_FUNCTION_MAXCOUNT) { - error_report("SEV-SNP: CPUID entry count (%d) exceeds max (%d)", + error_setg(errp, "SEV-SNP: CPUID entry count (%d) exceeds max (%d)", kvm_cpuid_info->cpuid.nent, SNP_CPUID_FUNCTION_MAXCOUNT); return -1; } @@ -1190,8 +1499,8 @@ sev_snp_cpuid_info_fill(SnpCpuidInfo *snp_cpuid_info, return 0; } -static int -snp_launch_update_cpuid(uint32_t cpuid_addr, void *hva, size_t cpuid_len) +static int snp_launch_update_cpuid(uint32_t cpuid_addr, void *hva, + size_t cpuid_len, Error **errp) { KvmCpuidInfo kvm_cpuid_info = {0}; SnpCpuidInfo snp_cpuid_info; @@ -1208,26 +1517,25 @@ snp_launch_update_cpuid(uint32_t cpuid_addr, void *hva, size_t cpuid_len) } while (ret == -E2BIG); if (ret) { - error_report("SEV-SNP: unable to query CPUID values for CPU: '%s'", - strerror(-ret)); - return 1; + error_setg(errp, "SEV-SNP: unable to query CPUID values for CPU: '%s'", + strerror(-ret)); + return -1; } - ret = sev_snp_cpuid_info_fill(&snp_cpuid_info, &kvm_cpuid_info); - if (ret) { - error_report("SEV-SNP: failed to generate CPUID table information"); - return 1; + ret = sev_snp_cpuid_info_fill(&snp_cpuid_info, &kvm_cpuid_info, errp); + if (ret < 0) { + return -1; } memcpy(hva, &snp_cpuid_info, sizeof(snp_cpuid_info)); return snp_launch_update_data(cpuid_addr, hva, cpuid_len, - KVM_SEV_SNP_PAGE_TYPE_CPUID); + KVM_SEV_SNP_PAGE_TYPE_CPUID, errp); } -static int -snp_launch_update_kernel_hashes(SevSnpGuestState *sev_snp, uint32_t addr, - void *hva, uint32_t len) +static int snp_launch_update_kernel_hashes(SevSnpGuestState *sev_snp, + uint32_t addr, void *hva, + uint32_t len, Error **errp) { int type = KVM_SEV_SNP_PAGE_TYPE_ZERO; if (sev_snp->parent_obj.kernel_hashes) { @@ -1239,7 +1547,7 @@ snp_launch_update_kernel_hashes(SevSnpGuestState *sev_snp, uint32_t addr, sizeof(*sev_snp->kernel_hashes_data)); type = KVM_SEV_SNP_PAGE_TYPE_NORMAL; } - return snp_launch_update_data(addr, hva, len, type); + return snp_launch_update_data(addr, hva, len, type, errp); } static int @@ -1277,12 +1585,14 @@ snp_populate_metadata_pages(SevSnpGuestState *sev_snp, } if (type == KVM_SEV_SNP_PAGE_TYPE_CPUID) { - ret = snp_launch_update_cpuid(desc->base, hva, desc->len); + ret = snp_launch_update_cpuid(desc->base, hva, desc->len, + &error_fatal); } else if (desc->type == SEV_DESC_TYPE_SNP_KERNEL_HASHES) { ret = snp_launch_update_kernel_hashes(sev_snp, desc->base, hva, - desc->len); + desc->len, &error_fatal); } else { - ret = snp_launch_update_data(desc->base, hva, desc->len, type); + ret = snp_launch_update_data(desc->base, hva, desc->len, type, + &error_fatal); } if (ret) { @@ -1304,18 +1614,26 @@ sev_snp_launch_finish(SevCommonState *sev_common) struct kvm_sev_snp_launch_finish *finish = &sev_snp->kvm_finish_conf; /* - * To boot the SNP guest, the hypervisor is required to populate the CPUID - * and Secrets page before finalizing the launch flow. The location of - * the secrets and CPUID page is available through the OVMF metadata GUID. + * Populate all the metadata pages if not using an IGVM file. In the case + * where an IGVM file is provided it will be used to configure the metadata + * pages directly. */ - metadata = pc_system_get_ovmf_sev_metadata_ptr(); - if (metadata == NULL) { - error_report("%s: Failed to locate SEV metadata header", __func__); - exit(1); - } + if (!X86_MACHINE(qdev_get_machine())->igvm) { + /* + * To boot the SNP guest, the hypervisor is required to populate the + * CPUID and Secrets page before finalizing the launch flow. The + * location of the secrets and CPUID page is available through the + * OVMF metadata GUID. + */ + metadata = pc_system_get_ovmf_sev_metadata_ptr(); + if (metadata == NULL) { + error_report("%s: Failed to locate SEV metadata header", __func__); + exit(1); + } - /* Populate all the metadata pages */ - snp_populate_metadata_pages(sev_snp, metadata); + /* Populate all the metadata pages */ + snp_populate_metadata_pages(sev_snp, metadata); + } QTAILQ_FOREACH(data, &launch_update, next) { ret = sev_snp_launch_update(sev_snp, data); @@ -1425,6 +1743,39 @@ static int sev_snp_kvm_type(X86ConfidentialGuest *cg) return KVM_X86_SNP_VM; } +static int sev_init_supported_features(ConfidentialGuestSupport *cgs, + SevCommonState *sev_common, Error **errp) +{ + X86ConfidentialGuestClass *x86_klass = + X86_CONFIDENTIAL_GUEST_GET_CLASS(cgs); + /* + * Older kernels do not support query or setting of sev_features. In this + * case the set of supported features must be zero to match the settings + * in the kernel. + */ + if (x86_klass->kvm_type(X86_CONFIDENTIAL_GUEST(sev_common)) == + KVM_X86_DEFAULT_VM) { + sev_common->supported_sev_features = 0; + return 0; + } + + /* Query KVM for the supported set of sev_features */ + struct kvm_device_attr attr = { + .group = KVM_X86_GRP_SEV, + .attr = KVM_X86_SEV_VMSA_FEATURES, + .addr = (unsigned long)&sev_common->supported_sev_features, + }; + if (kvm_ioctl(kvm_state, KVM_GET_DEVICE_ATTR, &attr) < 0) { + error_setg(errp, "%s: failed to query supported sev_features", + __func__); + return -1; + } + if (sev_snp_enabled()) { + sev_common->supported_sev_features |= SVM_SEV_FEAT_SNP_ACTIVE; + } + return 0; +} + static int sev_common_kvm_init(ConfidentialGuestSupport *cgs, Error **errp) { char *devname; @@ -1505,6 +1856,10 @@ static int sev_common_kvm_init(ConfidentialGuestSupport *cgs, Error **errp) } } + if (sev_init_supported_features(cgs, sev_common, errp) < 0) { + return -1; + } + trace_kvm_sev_init(); switch (x86_klass->kvm_type(X86_CONFIDENTIAL_GUEST(sev_common))) { case KVM_X86_DEFAULT_VM: @@ -1516,6 +1871,40 @@ static int sev_common_kvm_init(ConfidentialGuestSupport *cgs, Error **errp) case KVM_X86_SEV_ES_VM: case KVM_X86_SNP_VM: { struct kvm_sev_init args = { 0 }; + MachineState *machine = MACHINE(qdev_get_machine()); + X86MachineState *x86machine = X86_MACHINE(qdev_get_machine()); + + /* + * If configuration is provided via an IGVM file then the IGVM file + * might contain configuration of the initial vcpu context. For SEV + * the vcpu context includes the sev_features which should be applied + * to the vcpu. + * + * KVM does not synchronize sev_features from CPU state. Instead it + * requires sev_features to be provided as part of this initialization + * call which is subsequently automatically applied to the VMSA of + * each vcpu. + * + * The IGVM file is normally processed after initialization. Therefore + * we need to pre-process it here to extract sev_features in order to + * provide it to KVM_SEV_INIT2. Each cgs_* function that is called by + * the IGVM processor detects this pre-process by observing the state + * as SEV_STATE_UNINIT. + */ + if (x86machine->igvm) { + if (IGVM_CFG_GET_CLASS(x86machine->igvm) + ->process(x86machine->igvm, machine->cgs, true, errp) == + -1) { + return -1; + } + /* + * KVM maintains a bitmask of allowed sev_features. This does not + * include SVM_SEV_FEAT_SNP_ACTIVE which is set accordingly by KVM + * itself. Therefore we need to clear this flag. + */ + args.vmsa_features = sev_common->sev_features & + ~SVM_SEV_FEAT_SNP_ACTIVE; + } ret = sev_ioctl(sev_common->sev_fd, KVM_SEV_INIT2, &args, &fw_error); break; @@ -1615,9 +2004,8 @@ sev_encrypt_flash(hwaddr gpa, uint8_t *ptr, uint64_t len, Error **errp) if (sev_check_state(sev_common, SEV_STATE_LAUNCH_UPDATE)) { int ret; - ret = klass->launch_update_data(sev_common, gpa, ptr, len); + ret = klass->launch_update_data(sev_common, gpa, ptr, len, errp); if (ret < 0) { - error_setg(errp, "SEV: Failed to encrypt pflash rom"); return ret; } } @@ -1782,40 +2170,109 @@ sev_es_find_reset_vector(void *flash_ptr, uint64_t flash_size, return sev_es_parse_reset_block(info, addr); } -void sev_es_set_reset_vector(CPUState *cpu) + +static void seg_to_vmsa(const SegmentCache *cpu_seg, struct vmcb_seg *vmsa_seg) { - X86CPU *x86; - CPUX86State *env; - ConfidentialGuestSupport *cgs = MACHINE(qdev_get_machine())->cgs; - SevCommonState *sev_common = SEV_COMMON( - object_dynamic_cast(OBJECT(cgs), TYPE_SEV_COMMON)); + vmsa_seg->selector = cpu_seg->selector; + vmsa_seg->base = cpu_seg->base; + vmsa_seg->limit = cpu_seg->limit; + vmsa_seg->attrib = FLAGS_SEGCACHE_TO_VMSA(cpu_seg->flags); +} - /* Only update if we have valid reset information */ - if (!sev_common || !sev_common->reset_data_valid) { - return; - } +static void initialize_vmsa(const CPUState *cpu, struct sev_es_save_area *vmsa) +{ + const X86CPU *x86 = X86_CPU(cpu); + const CPUX86State *env = &x86->env; - /* Do not update the BSP reset state */ - if (cpu->cpu_index == 0) { - return; + /* + * Initialize the SEV-ES save area from the current state of + * the CPU. The entire state does not need to be copied, only the state + * that is copied back to the CPUState in sev_apply_cpu_context. + */ + memset(vmsa, 0, sizeof(struct sev_es_save_area)); + vmsa->efer = env->efer; + vmsa->cr0 = env->cr[0]; + vmsa->cr3 = env->cr[3]; + vmsa->cr4 = env->cr[4]; + vmsa->xcr0 = env->xcr0; + vmsa->g_pat = env->pat; + + seg_to_vmsa(&env->segs[R_CS], &vmsa->cs); + seg_to_vmsa(&env->segs[R_DS], &vmsa->ds); + seg_to_vmsa(&env->segs[R_ES], &vmsa->es); + seg_to_vmsa(&env->segs[R_FS], &vmsa->fs); + seg_to_vmsa(&env->segs[R_GS], &vmsa->gs); + seg_to_vmsa(&env->segs[R_SS], &vmsa->ss); + + seg_to_vmsa(&env->gdt, &vmsa->gdtr); + seg_to_vmsa(&env->idt, &vmsa->idtr); + seg_to_vmsa(&env->ldt, &vmsa->ldtr); + seg_to_vmsa(&env->tr, &vmsa->tr); + + vmsa->dr6 = env->dr[6]; + vmsa->dr7 = env->dr[7]; + + vmsa->rax = env->regs[R_EAX]; + vmsa->rcx = env->regs[R_ECX]; + vmsa->rdx = env->regs[R_EDX]; + vmsa->rbx = env->regs[R_EBX]; + vmsa->rsp = env->regs[R_ESP]; + vmsa->rbp = env->regs[R_EBP]; + vmsa->rsi = env->regs[R_ESI]; + vmsa->rdi = env->regs[R_EDI]; + +#ifdef TARGET_X86_64 + vmsa->r8 = env->regs[R_R8]; + vmsa->r9 = env->regs[R_R9]; + vmsa->r10 = env->regs[R_R10]; + vmsa->r11 = env->regs[R_R11]; + vmsa->r12 = env->regs[R_R12]; + vmsa->r13 = env->regs[R_R13]; + vmsa->r14 = env->regs[R_R14]; + vmsa->r15 = env->regs[R_R15]; +#endif + + vmsa->rip = env->eip; + vmsa->rflags = env->eflags; +} + +static void sev_es_set_ap_context(uint32_t reset_addr) +{ + CPUState *cpu; + struct sev_es_save_area vmsa; + SegmentCache cs; + + cs.selector = 0xf000; + cs.base = reset_addr & 0xffff0000; + cs.limit = 0xffff; + cs.flags = DESC_P_MASK | DESC_S_MASK | DESC_CS_MASK | DESC_R_MASK | + DESC_A_MASK; + + CPU_FOREACH(cpu) { + if (cpu->cpu_index == 0) { + /* Do not update the BSP reset state */ + continue; + } + initialize_vmsa(cpu, &vmsa); + seg_to_vmsa(&cs, &vmsa.cs); + vmsa.rip = reset_addr & 0x0000ffff; + sev_set_cpu_context(cpu->cpu_index, &vmsa, + sizeof(struct sev_es_save_area), + 0, &error_fatal); } +} - x86 = X86_CPU(cpu); - env = &x86->env; - - cpu_x86_load_seg_cache(env, R_CS, 0xf000, sev_common->reset_cs, 0xffff, - DESC_P_MASK | DESC_S_MASK | DESC_CS_MASK | - DESC_R_MASK | DESC_A_MASK); - - env->eip = sev_common->reset_ip; +void sev_es_set_reset_vector(CPUState *cpu) +{ + if (sev_enabled()) { + sev_apply_cpu_context(cpu); + } } int sev_es_save_reset_vector(void *flash_ptr, uint64_t flash_size) { - CPUState *cpu; uint32_t addr; int ret; - SevCommonState *sev_common = SEV_COMMON(MACHINE(qdev_get_machine())->cgs); if (!sev_es_enabled()) { return 0; @@ -1828,14 +2285,12 @@ int sev_es_save_reset_vector(void *flash_ptr, uint64_t flash_size) return ret; } + /* + * The reset vector is saved into a CPU context for each AP but not for + * the BSP. This is applied during guest startup or when the CPU is reset. + */ if (addr) { - sev_common->reset_cs = addr & 0xffff0000; - sev_common->reset_ip = addr & 0x0000ffff; - sev_common->reset_data_valid = true; - - CPU_FOREACH(cpu) { - sev_es_set_reset_vector(cpu); - } + sev_es_set_ap_context(addr); } return 0; @@ -2037,6 +2492,237 @@ static void sev_common_set_kernel_hashes(Object *obj, bool value, Error **errp) SEV_COMMON(obj)->kernel_hashes = value; } +static bool cgs_check_support(ConfidentialGuestPlatformType platform, + uint16_t platform_version, uint8_t highest_vtl, + uint64_t shared_gpa_boundary) +{ + return (((platform == CGS_PLATFORM_SEV_SNP) && sev_snp_enabled()) || + ((platform == CGS_PLATFORM_SEV_ES) && sev_es_enabled()) || + ((platform == CGS_PLATFORM_SEV) && sev_enabled())); +} + +static int cgs_set_guest_state(hwaddr gpa, uint8_t *ptr, uint64_t len, + ConfidentialGuestPageType memory_type, + uint16_t cpu_index, Error **errp) +{ + SevCommonState *sev_common = SEV_COMMON(MACHINE(qdev_get_machine())->cgs); + SevCommonStateClass *klass = SEV_COMMON_GET_CLASS(sev_common); + + if (sev_common->state == SEV_STATE_UNINIT) { + /* Pre-processing of IGVM file called from sev_common_kvm_init() */ + if ((cpu_index == 0) && (memory_type == CGS_PAGE_TYPE_VMSA)) { + const struct sev_es_save_area *sa = + (const struct sev_es_save_area *)ptr; + if (len < sizeof(*sa)) { + error_setg(errp, "%s: invalid VMSA length encountered", + __func__); + return -1; + } + if (check_sev_features(sev_common, sa->sev_features, errp) < 0) { + return -1; + } + sev_common->sev_features = sa->sev_features; + } + return 0; + } + + if (!sev_enabled()) { + error_setg(errp, "%s: attempt to configure guest memory, but SEV " + "is not enabled", __func__); + return -1; + } + + switch (memory_type) { + case CGS_PAGE_TYPE_NORMAL: + case CGS_PAGE_TYPE_ZERO: + return klass->launch_update_data(sev_common, gpa, ptr, len, errp); + + case CGS_PAGE_TYPE_VMSA: + if (!sev_es_enabled()) { + error_setg(errp, + "%s: attempt to configure initial VMSA, but SEV-ES " + "is not supported", + __func__); + return -1; + } + if (check_vmsa_supported(sev_common, gpa, + (const struct sev_es_save_area *)ptr, + errp) < 0) { + return -1; + } + return sev_set_cpu_context(cpu_index, ptr, len, gpa, errp); + + case CGS_PAGE_TYPE_UNMEASURED: + if (sev_snp_enabled()) { + return snp_launch_update_data( + gpa, ptr, len, KVM_SEV_SNP_PAGE_TYPE_UNMEASURED, errp); + } + /* No action required if not SEV-SNP */ + return 0; + + case CGS_PAGE_TYPE_SECRETS: + if (!sev_snp_enabled()) { + error_setg(errp, + "%s: attempt to configure secrets page, but SEV-SNP " + "is not supported", + __func__); + return -1; + } + return snp_launch_update_data(gpa, ptr, len, + KVM_SEV_SNP_PAGE_TYPE_SECRETS, errp); + + case CGS_PAGE_TYPE_REQUIRED_MEMORY: + if (kvm_convert_memory(gpa, len, true) < 0) { + error_setg( + errp, + "%s: failed to configure required memory. gpa: %lX, type: %d", + __func__, gpa, memory_type); + return -1; + } + return 0; + + case CGS_PAGE_TYPE_CPUID: + if (!sev_snp_enabled()) { + error_setg(errp, + "%s: attempt to configure CPUID page, but SEV-SNP " + "is not supported", + __func__); + return -1; + } + return snp_launch_update_cpuid(gpa, ptr, len, errp); + } + error_setg(errp, "%s: failed to update guest. gpa: %lX, type: %d", __func__, + gpa, memory_type); + return -1; +} + +static int cgs_get_mem_map_entry(int index, + ConfidentialGuestMemoryMapEntry *entry, + Error **errp) +{ + struct e820_entry *table; + int num_entries; + + SevCommonState *sev_common = SEV_COMMON(MACHINE(qdev_get_machine())->cgs); + if (sev_common->state == SEV_STATE_UNINIT) { + /* Pre-processing of IGVM file called from sev_common_kvm_init() */ + return 1; + } + + num_entries = e820_get_table(&table); + if ((index < 0) || (index >= num_entries)) { + return 1; + } + entry->gpa = table[index].address; + entry->size = table[index].length; + switch (table[index].type) { + case E820_RAM: + entry->type = CGS_MEM_RAM; + break; + case E820_RESERVED: + entry->type = CGS_MEM_RESERVED; + break; + case E820_ACPI: + entry->type = CGS_MEM_ACPI; + break; + case E820_NVS: + entry->type = CGS_MEM_NVS; + break; + case E820_UNUSABLE: + entry->type = CGS_MEM_UNUSABLE; + break; + } + return 0; +} + +static int cgs_set_guest_policy(ConfidentialGuestPolicyType policy_type, + uint64_t policy, void *policy_data1, + uint32_t policy_data1_size, void *policy_data2, + uint32_t policy_data2_size, Error **errp) +{ + SevCommonState *sev_common = SEV_COMMON(MACHINE(qdev_get_machine())->cgs); + if (sev_common->state == SEV_STATE_UNINIT) { + /* Pre-processing of IGVM file called from sev_common_kvm_init() */ + return 0; + } + + if (policy_type != GUEST_POLICY_SEV) { + error_setg(errp, "%s: Invalid guest policy type provided for SEV: %d", + __func__, policy_type); + return -1; + } + /* + * SEV-SNP handles policy differently. The policy flags are defined in + * kvm_start_conf.policy and an ID block and ID auth can be provided. + */ + if (sev_snp_enabled()) { + SevSnpGuestState *sev_snp_guest = + SEV_SNP_GUEST(MACHINE(qdev_get_machine())->cgs); + struct kvm_sev_snp_launch_finish *finish = + &sev_snp_guest->kvm_finish_conf; + + /* + * The policy consists of flags in 'policy' and optionally an ID block + * and ID auth in policy_data1 and policy_data2 respectively. The ID + * block and auth are optional so clear any previous ID block and auth + * and set them if provided, but always set the policy flags. + */ + g_free(sev_snp_guest->id_block); + g_free((guchar *)finish->id_block_uaddr); + g_free(sev_snp_guest->id_auth); + g_free((guchar *)finish->id_auth_uaddr); + sev_snp_guest->id_block = NULL; + finish->id_block_uaddr = 0; + sev_snp_guest->id_auth = NULL; + finish->id_auth_uaddr = 0; + + if (policy_data1_size > 0) { + struct sev_snp_id_authentication *id_auth = + (struct sev_snp_id_authentication *)policy_data2; + + if (policy_data1_size != KVM_SEV_SNP_ID_BLOCK_SIZE) { + error_setg(errp, "%s: Invalid SEV-SNP ID block: incorrect size", + __func__); + return -1; + } + if (policy_data2_size != KVM_SEV_SNP_ID_AUTH_SIZE) { + error_setg(errp, + "%s: Invalid SEV-SNP ID auth block: incorrect size", + __func__); + return -1; + } + assert(policy_data1 != NULL); + assert(policy_data2 != NULL); + + finish->id_block_uaddr = + (__u64)g_memdup2(policy_data1, KVM_SEV_SNP_ID_BLOCK_SIZE); + finish->id_auth_uaddr = + (__u64)g_memdup2(policy_data2, KVM_SEV_SNP_ID_AUTH_SIZE); + + /* + * Check if an author key has been provided and use that to flag + * whether the author key is enabled. The first of the author key + * must be non-zero to indicate the key type, which will currently + * always be 2. + */ + sev_snp_guest->kvm_finish_conf.auth_key_en = + id_auth->author_key[0] ? 1 : 0; + finish->id_block_en = 1; + } + sev_snp_guest->kvm_start_conf.policy = policy; + } else { + SevGuestState *sev_guest = SEV_GUEST(MACHINE(qdev_get_machine())->cgs); + /* Only the policy flags are supported for SEV and SEV-ES */ + if ((policy_data1_size > 0) || (policy_data2_size > 0) || !sev_guest) { + error_setg(errp, "%s: An ID block/ID auth block has been provided " + "but SEV-SNP is not enabled", __func__); + return -1; + } + sev_guest->policy = policy; + } + return 0; +} + static void sev_common_class_init(ObjectClass *oc, const void *data) { @@ -2060,6 +2746,8 @@ static void sev_common_instance_init(Object *obj) { SevCommonState *sev_common = SEV_COMMON(obj); + ConfidentialGuestSupportClass *cgs = + CONFIDENTIAL_GUEST_SUPPORT_GET_CLASS(obj); sev_common->kvm_type = -1; @@ -2070,6 +2758,12 @@ sev_common_instance_init(Object *obj) object_property_add_uint32_ptr(obj, "reduced-phys-bits", &sev_common->reduced_phys_bits, OBJ_PROP_FLAG_READWRITE); + cgs->check_support = cgs_check_support; + cgs->set_guest_state = cgs_set_guest_state; + cgs->get_mem_map_entry = cgs_get_mem_map_entry; + cgs->set_guest_policy = cgs_set_guest_policy; + + QTAILQ_INIT(&sev_common->launch_vmsa); } /* sev guest info common to sev/sev-es/sev-snp */ diff --git a/target/i386/sev.h b/target/i386/sev.h index 373669e..9db1a80 100644 --- a/target/i386/sev.h +++ b/target/i386/sev.h @@ -44,6 +44,8 @@ bool sev_snp_enabled(void); #define SEV_SNP_POLICY_SMT 0x10000 #define SEV_SNP_POLICY_DBG 0x80000 +#define SVM_SEV_FEAT_SNP_ACTIVE 1 + typedef struct SevKernelLoaderContext { char *setup_data; size_t setup_size; @@ -55,6 +57,128 @@ typedef struct SevKernelLoaderContext { size_t cmdline_size; } SevKernelLoaderContext; +/* Save area definition for SEV-ES and SEV-SNP guests */ +struct QEMU_PACKED sev_es_save_area { + struct vmcb_seg es; + struct vmcb_seg cs; + struct vmcb_seg ss; + struct vmcb_seg ds; + struct vmcb_seg fs; + struct vmcb_seg gs; + struct vmcb_seg gdtr; + struct vmcb_seg ldtr; + struct vmcb_seg idtr; + struct vmcb_seg tr; + uint64_t vmpl0_ssp; + uint64_t vmpl1_ssp; + uint64_t vmpl2_ssp; + uint64_t vmpl3_ssp; + uint64_t u_cet; + uint8_t reserved_0xc8[2]; + uint8_t vmpl; + uint8_t cpl; + uint8_t reserved_0xcc[4]; + uint64_t efer; + uint8_t reserved_0xd8[104]; + uint64_t xss; + uint64_t cr4; + uint64_t cr3; + uint64_t cr0; + uint64_t dr7; + uint64_t dr6; + uint64_t rflags; + uint64_t rip; + uint64_t dr0; + uint64_t dr1; + uint64_t dr2; + uint64_t dr3; + uint64_t dr0_addr_mask; + uint64_t dr1_addr_mask; + uint64_t dr2_addr_mask; + uint64_t dr3_addr_mask; + uint8_t reserved_0x1c0[24]; + uint64_t rsp; + uint64_t s_cet; + uint64_t ssp; + uint64_t isst_addr; + uint64_t rax; + uint64_t star; + uint64_t lstar; + uint64_t cstar; + uint64_t sfmask; + uint64_t kernel_gs_base; + uint64_t sysenter_cs; + uint64_t sysenter_esp; + uint64_t sysenter_eip; + uint64_t cr2; + uint8_t reserved_0x248[32]; + uint64_t g_pat; + uint64_t dbgctl; + uint64_t br_from; + uint64_t br_to; + uint64_t last_excp_from; + uint64_t last_excp_to; + uint8_t reserved_0x298[80]; + uint32_t pkru; + uint32_t tsc_aux; + uint8_t reserved_0x2f0[24]; + uint64_t rcx; + uint64_t rdx; + uint64_t rbx; + uint64_t reserved_0x320; /* rsp already available at 0x01d8 */ + uint64_t rbp; + uint64_t rsi; + uint64_t rdi; + uint64_t r8; + uint64_t r9; + uint64_t r10; + uint64_t r11; + uint64_t r12; + uint64_t r13; + uint64_t r14; + uint64_t r15; + uint8_t reserved_0x380[16]; + uint64_t guest_exit_info_1; + uint64_t guest_exit_info_2; + uint64_t guest_exit_int_info; + uint64_t guest_nrip; + uint64_t sev_features; + uint64_t vintr_ctrl; + uint64_t guest_exit_code; + uint64_t virtual_tom; + uint64_t tlb_id; + uint64_t pcpu_id; + uint64_t event_inj; + uint64_t xcr0; + uint8_t reserved_0x3f0[16]; + + /* Floating point area */ + uint64_t x87_dp; + uint32_t mxcsr; + uint16_t x87_ftw; + uint16_t x87_fsw; + uint16_t x87_fcw; + uint16_t x87_fop; + uint16_t x87_ds; + uint16_t x87_cs; + uint64_t x87_rip; + uint8_t fpreg_x87[80]; + uint8_t fpreg_xmm[256]; + uint8_t fpreg_ymm[256]; +}; + +struct QEMU_PACKED sev_snp_id_authentication { + uint32_t id_key_alg; + uint32_t auth_key_algo; + uint8_t reserved[56]; + uint8_t id_block_sig[512]; + uint8_t id_key[1028]; + uint8_t reserved2[60]; + uint8_t id_key_sig[512]; + uint8_t author_key[1028]; + uint8_t reserved3[892]; +}; + bool sev_add_kernel_loader_hashes(SevKernelLoaderContext *ctx, Error **errp); int sev_encrypt_flash(hwaddr gpa, uint8_t *ptr, uint64_t len, Error **errp); diff --git a/target/i386/tcg/decode-new.c.inc b/target/i386/tcg/decode-new.c.inc index 55216e0..853b1c8 100644 --- a/target/i386/tcg/decode-new.c.inc +++ b/target/i386/tcg/decode-new.c.inc @@ -2722,14 +2722,14 @@ static void disas_insn(DisasContext *s, CPUState *cpu) if (decode.e.check & X86_CHECK_i64) { goto illegal_op; } - if ((decode.e.check & X86_CHECK_i64_amd) && env->cpuid_vendor1 != CPUID_VENDOR_INTEL_1) { + if ((decode.e.check & X86_CHECK_i64_amd) && !IS_INTEL_CPU(env)) { goto illegal_op; } } else { if (decode.e.check & X86_CHECK_o64) { goto illegal_op; } - if ((decode.e.check & X86_CHECK_o64_intel) && env->cpuid_vendor1 == CPUID_VENDOR_INTEL_1) { + if ((decode.e.check & X86_CHECK_o64_intel) && IS_INTEL_CPU(env)) { goto illegal_op; } } diff --git a/target/i386/tcg/system/excp_helper.c b/target/i386/tcg/system/excp_helper.c index c162621..50040f6 100644 --- a/target/i386/tcg/system/excp_helper.c +++ b/target/i386/tcg/system/excp_helper.c @@ -25,7 +25,6 @@ #include "exec/page-protection.h" #include "exec/target_page.h" #include "exec/tlb-flags.h" -#include "exec/tswap.h" #include "tcg/helper-tcg.h" typedef struct TranslateParams { diff --git a/target/i386/whpx/whpx-all.c b/target/i386/whpx/whpx-all.c index faf56e1..22ac609 100644 --- a/target/i386/whpx/whpx-all.c +++ b/target/i386/whpx/whpx-all.c @@ -26,6 +26,8 @@ #include "qapi/qapi-types-common.h" #include "qapi/qapi-visit-common.h" #include "migration/blocker.h" +#include "host-cpu.h" +#include "accel/accel-cpu-target.h" #include <winerror.h> #include "whpx-internal.h" @@ -2500,6 +2502,28 @@ static void whpx_set_kernel_irqchip(Object *obj, Visitor *v, } } +static void whpx_cpu_instance_init(CPUState *cs) +{ + X86CPU *cpu = X86_CPU(cs); + + host_cpu_instance_init(cpu); +} + +static void whpx_cpu_accel_class_init(ObjectClass *oc, const void *data) +{ + AccelCPUClass *acc = ACCEL_CPU_CLASS(oc); + + acc->cpu_instance_init = whpx_cpu_instance_init; +} + +static const TypeInfo whpx_cpu_accel_type = { + .name = ACCEL_CPU_NAME("whpx"), + + .parent = TYPE_ACCEL_CPU, + .class_init = whpx_cpu_accel_class_init, + .abstract = true, +}; + /* * Partition support */ @@ -2726,6 +2750,7 @@ static const TypeInfo whpx_accel_type = { static void whpx_type_init(void) { type_register_static(&whpx_accel_type); + type_register_static(&whpx_cpu_accel_type); } bool init_whp_dispatch(void) diff --git a/target/i386/xsave_helper.c b/target/i386/xsave_helper.c index 24ab7be..996e9f3 100644 --- a/target/i386/xsave_helper.c +++ b/target/i386/xsave_helper.c @@ -5,7 +5,6 @@ #include "qemu/osdep.h" #include "cpu.h" -#include "exec/tswap.h" void x86_cpu_xsave_all_areas(X86CPU *cpu, void *buf, uint32_t buflen) { diff --git a/target/loongarch/loongarch-qmp-cmds.c b/target/loongarch/loongarch-qmp-cmds.c index f5f1cd0..1d8cd32 100644 --- a/target/loongarch/loongarch-qmp-cmds.c +++ b/target/loongarch/loongarch-qmp-cmds.c @@ -7,6 +7,7 @@ */ #include "qemu/osdep.h" +#include "qemu/target-info.h" #include "qapi/error.h" #include "qapi/qapi-commands-machine.h" #include "cpu.h" @@ -32,7 +33,7 @@ CpuDefinitionInfoList *qmp_query_cpu_definitions(Error **errp) CpuDefinitionInfoList *cpu_list = NULL; GSList *list; - list = object_class_get_list(TYPE_LOONGARCH_CPU, false); + list = object_class_get_list(target_cpu_type(), false); g_slist_foreach(list, loongarch_cpu_add_definition, &cpu_list); g_slist_free(list); diff --git a/target/mips/cpu-defs.c.inc b/target/mips/cpu-defs.c.inc index 922fc39..d93b9d3 100644 --- a/target/mips/cpu-defs.c.inc +++ b/target/mips/cpu-defs.c.inc @@ -756,8 +756,9 @@ const mips_def_t mips_defs[] = (1 << CP0C3_RXI) | (1 << CP0C3_LPA) | (1 << CP0C3_VInt), .CP0_Config4 = MIPS_CONFIG4 | (1U << CP0C4_M) | (3 << CP0C4_IE) | (1 << CP0C4_AE) | (0xfc << CP0C4_KScrExist), - .CP0_Config5 = MIPS_CONFIG5 | (1 << CP0C5_XNP) | (1 << CP0C5_VP) | - (1 << CP0C5_LLB) | (1 << CP0C5_MRP) | (3 << CP0C5_GI), + .CP0_Config5 = MIPS_CONFIG5 | (1 << CP0C5_CRCP) | (1 << CP0C5_XNP) | + (1 << CP0C5_VP) | (1 << CP0C5_LLB) | (1 << CP0C5_MRP) | + (3 << CP0C5_GI), .CP0_Config5_rw_bitmask = (1 << CP0C5_MSAEn) | (1 << CP0C5_SBRI) | (1 << CP0C5_FRE) | (1 << CP0C5_UFE), .CP0_LLAddr_rw_bitmask = 0, @@ -796,8 +797,9 @@ const mips_def_t mips_defs[] = (1 << CP0C3_RXI) | (1 << CP0C3_LPA) | (1 << CP0C3_VInt), .CP0_Config4 = MIPS_CONFIG4 | (1U << CP0C4_M) | (3 << CP0C4_IE) | (1 << CP0C4_AE) | (0xfc << CP0C4_KScrExist), - .CP0_Config5 = MIPS_CONFIG5 | (1 << CP0C5_XNP) | (1 << CP0C5_VP) | - (1 << CP0C5_LLB) | (1 << CP0C5_MRP) | (3 << CP0C5_GI), + .CP0_Config5 = MIPS_CONFIG5 | (1 << CP0C5_CRCP) | (1 << CP0C5_XNP) | + (1 << CP0C5_VP) | (1 << CP0C5_LLB) | (1 << CP0C5_MRP) | + (3 << CP0C5_GI), .CP0_Config5_rw_bitmask = (1 << CP0C5_MSAEn) | (1 << CP0C5_SBRI) | (1 << CP0C5_FRE) | (1 << CP0C5_UFE), .CP0_LLAddr_rw_bitmask = 0, diff --git a/target/mips/helper.h b/target/mips/helper.h index 7e40041..b6cd53c 100644 --- a/target/mips/helper.h +++ b/target/mips/helper.h @@ -21,6 +21,8 @@ DEF_HELPER_FLAGS_1(bitswap, TCG_CALL_NO_RWG_SE, tl, tl) DEF_HELPER_FLAGS_1(dbitswap, TCG_CALL_NO_RWG_SE, tl, tl) #endif +DEF_HELPER_3(crc32, tl, tl, tl, i32) +DEF_HELPER_3(crc32c, tl, tl, tl, i32) DEF_HELPER_FLAGS_4(rotx, TCG_CALL_NO_RWG_SE, tl, tl, i32, i32, i32) /* microMIPS functions */ diff --git a/target/mips/meson.build b/target/mips/meson.build index 247979a..abf0ce3 100644 --- a/target/mips/meson.build +++ b/target/mips/meson.build @@ -7,6 +7,7 @@ mips_ss.add(files( 'gdbstub.c', 'msa.c', )) +mips_ss.add(zlib) if have_system subdir('system') diff --git a/target/mips/system/mips-qmp-cmds.c b/target/mips/system/mips-qmp-cmds.c index d98d662..b6a2874 100644 --- a/target/mips/system/mips-qmp-cmds.c +++ b/target/mips/system/mips-qmp-cmds.c @@ -7,6 +7,7 @@ */ #include "qemu/osdep.h" +#include "qemu/target-info.h" #include "qapi/error.h" #include "qapi/qapi-commands-machine.h" #include "cpu.h" @@ -40,7 +41,7 @@ CpuDefinitionInfoList *qmp_query_cpu_definitions(Error **errp) CpuDefinitionInfoList *cpu_list = NULL; GSList *list; - list = object_class_get_list(TYPE_MIPS_CPU, false); + list = object_class_get_list(target_cpu_type(), false); g_slist_foreach(list, mips_cpu_add_definition, &cpu_list); g_slist_free(list); diff --git a/target/mips/tcg/micromips_translate.c.inc b/target/mips/tcg/micromips_translate.c.inc index c479bec..8fda7c8 100644 --- a/target/mips/tcg/micromips_translate.c.inc +++ b/target/mips/tcg/micromips_translate.c.inc @@ -1795,7 +1795,7 @@ static void decode_micromips32_opc(CPUMIPSState *env, DisasContext *ctx) return; case LSA: check_insn(ctx, ISA_MIPS_R6); - gen_lsa(ctx, rd, rt, rs, extract32(ctx->opcode, 9, 2)); + gen_lsa(ctx, rd, rt, rs, extract32(ctx->opcode, 9, 2) + 1); break; case ALIGN: check_insn(ctx, ISA_MIPS_R6); diff --git a/target/mips/tcg/msa_translate.c b/target/mips/tcg/msa_translate.c index 75cf80a..82b1499 100644 --- a/target/mips/tcg/msa_translate.c +++ b/target/mips/tcg/msa_translate.c @@ -780,7 +780,7 @@ TRANS_DF_iv(ST, trans_msa_ldst, gen_helper_msa_st); static bool trans_LSA(DisasContext *ctx, arg_r *a) { - return gen_lsa(ctx, a->rd, a->rt, a->rs, a->sa); + return gen_lsa(ctx, a->rd, a->rt, a->rs, a->sa + 1); } static bool trans_DLSA(DisasContext *ctx, arg_r *a) @@ -788,5 +788,5 @@ static bool trans_DLSA(DisasContext *ctx, arg_r *a) if (TARGET_LONG_BITS != 64) { return false; } - return gen_dlsa(ctx, a->rd, a->rt, a->rs, a->sa); + return gen_dlsa(ctx, a->rd, a->rt, a->rs, a->sa + 1); } diff --git a/target/mips/tcg/nanomips_translate.c.inc b/target/mips/tcg/nanomips_translate.c.inc index 1e27414..9d4e0be 100644 --- a/target/mips/tcg/nanomips_translate.c.inc +++ b/target/mips/tcg/nanomips_translate.c.inc @@ -3626,12 +3626,7 @@ static int decode_nanomips_32_48_opc(CPUMIPSState *env, DisasContext *ctx) gen_p_lsx(ctx, rd, rs, rt); break; case NM_LSA: - /* - * In nanoMIPS, the shift field directly encodes the shift - * amount, meaning that the supported shift values are in - * the range 0 to 3 (instead of 1 to 4 in MIPSR6). - */ - gen_lsa(ctx, rd, rt, rs, extract32(ctx->opcode, 9, 2) - 1); + gen_lsa(ctx, rd, rt, rs, extract32(ctx->opcode, 9, 2)); break; case NM_EXTW: gen_ext(ctx, 32, rd, rs, rt, extract32(ctx->opcode, 6, 5)); diff --git a/target/mips/tcg/octeon.decode b/target/mips/tcg/octeon.decode index 0c787cb..102a058 100644 --- a/target/mips/tcg/octeon.decode +++ b/target/mips/tcg/octeon.decode @@ -1,6 +1,7 @@ # Octeon Architecture Module instruction set # # Copyright (C) 2022 Pavel Dovgalyuk +# Copyright (C) 2024 Philippe Mathieu-Daudé # # SPDX-License-Identifier: LGPL-2.1-or-later # @@ -39,3 +40,10 @@ CINS 011100 ..... ..... ..... ..... 11001 . @bitfield POP 011100 rs:5 00000 rd:5 00000 10110 dw:1 SEQNE 011100 rs:5 rt:5 rd:5 00000 10101 ne:1 SEQNEI 011100 rs:5 rt:5 imm:s10 10111 ne:1 + +&lx base index rd +@lx ...... base:5 index:5 rd:5 ...... ..... &lx +LWX 011111 ..... ..... ..... 00000 001010 @lx +LHX 011111 ..... ..... ..... 00100 001010 @lx +LBUX 011111 ..... ..... ..... 00110 001010 @lx +LDX 011111 ..... ..... ..... 01000 001010 @lx diff --git a/target/mips/tcg/octeon_translate.c b/target/mips/tcg/octeon_translate.c index d9eb437..b2eca29 100644 --- a/target/mips/tcg/octeon_translate.c +++ b/target/mips/tcg/octeon_translate.c @@ -174,3 +174,15 @@ static bool trans_SEQNEI(DisasContext *ctx, arg_SEQNEI *a) } return true; } + +static bool trans_lx(DisasContext *ctx, arg_lx *a, MemOp mop) +{ + gen_lx(ctx, a->rd, a->base, a->index, mop); + + return true; +} + +TRANS(LBUX, trans_lx, MO_UB); +TRANS(LHX, trans_lx, MO_SW); +TRANS(LWX, trans_lx, MO_SL); +TRANS(LDX, trans_lx, MO_UQ); diff --git a/target/mips/tcg/op_helper.c b/target/mips/tcg/op_helper.c index b906d10..4502ae2 100644 --- a/target/mips/tcg/op_helper.c +++ b/target/mips/tcg/op_helper.c @@ -24,6 +24,8 @@ #include "exec/helper-proto.h" #include "exec/memop.h" #include "fpu_helper.h" +#include "qemu/crc32c.h" +#include <zlib.h> static inline target_ulong bitswap(target_ulong v) { @@ -142,6 +144,30 @@ target_ulong helper_rotx(target_ulong rs, uint32_t shift, uint32_t shiftx, return (int64_t)(int32_t)(uint32_t)tmp5; } +/* these crc32 functions are based on target/loongarch/tcg/op_helper.c */ +target_ulong helper_crc32(target_ulong val, target_ulong m, uint32_t sz) +{ + uint8_t buf[8]; + target_ulong mask = ((sz * 8) == 64) ? + (target_ulong) -1ULL : + ((1ULL << (sz * 8)) - 1); + + m &= mask; + stq_le_p(buf, m); + return (int32_t) (crc32(val ^ 0xffffffff, buf, sz) ^ 0xffffffff); +} + +target_ulong helper_crc32c(target_ulong val, target_ulong m, uint32_t sz) +{ + uint8_t buf[8]; + target_ulong mask = ((sz * 8) == 64) ? + (target_ulong) -1ULL : + ((1ULL << (sz * 8)) - 1); + m &= mask; + stq_le_p(buf, m); + return (int32_t) (crc32c(val, buf, sz) ^ 0xffffffff); +} + void helper_fork(target_ulong arg1, target_ulong arg2) { /* diff --git a/target/mips/tcg/rel6.decode b/target/mips/tcg/rel6.decode index d6989cf..7fbcb10 100644 --- a/target/mips/tcg/rel6.decode +++ b/target/mips/tcg/rel6.decode @@ -16,11 +16,16 @@ &r rs rt rd sa +&special3_crc rs rt c sz + @lsa ...... rs:5 rt:5 rd:5 ... sa:2 ...... &r +@crc32 ...... rs:5 rt:5 ..... c:3 sz:2 ...... &special3_crc LSA 000000 ..... ..... ..... 000 .. 000101 @lsa DLSA 000000 ..... ..... ..... 000 .. 010101 @lsa +CRC32 011111 ..... ..... 00000 ... .. 001111 @crc32 + REMOVED 010011 ----- ----- ----- ----- ------ # COP1X (COP3) REMOVED 011100 ----- ----- ----- ----- ------ # SPECIAL2 diff --git a/target/mips/tcg/rel6_translate.c b/target/mips/tcg/rel6_translate.c index 59f237b..4c05662 100644 --- a/target/mips/tcg/rel6_translate.c +++ b/target/mips/tcg/rel6_translate.c @@ -23,7 +23,7 @@ bool trans_REMOVED(DisasContext *ctx, arg_REMOVED *a) static bool trans_LSA(DisasContext *ctx, arg_r *a) { - return gen_lsa(ctx, a->rd, a->rt, a->rs, a->sa); + return gen_lsa(ctx, a->rd, a->rt, a->rs, a->sa + 1); } static bool trans_DLSA(DisasContext *ctx, arg_r *a) @@ -31,5 +31,17 @@ static bool trans_DLSA(DisasContext *ctx, arg_r *a) if (TARGET_LONG_BITS != 64) { return false; } - return gen_dlsa(ctx, a->rd, a->rt, a->rs, a->sa); + return gen_dlsa(ctx, a->rd, a->rt, a->rs, a->sa + 1); +} + +static bool trans_CRC32(DisasContext *ctx, arg_special3_crc *a) +{ + if (unlikely(!ctx->crcp) + || unlikely((a->sz == 3) && (!(ctx->hflags & MIPS_HFLAG_64))) + || unlikely((a->c >= 2))) { + gen_reserved_instruction(ctx); + return true; + } + gen_crc32(ctx, a->rt, a->rs, a->rt, a->sz, a->c); + return true; } diff --git a/target/mips/tcg/translate.c b/target/mips/tcg/translate.c index 8658315..d91d6ef 100644 --- a/target/mips/tcg/translate.c +++ b/target/mips/tcg/translate.c @@ -1957,6 +1957,17 @@ void gen_base_offset_addr(DisasContext *ctx, TCGv addr, int base, int offset) } } +void gen_base_index_addr(DisasContext *ctx, TCGv addr, int base, int index) +{ + if (base == 0) { + gen_load_gpr(addr, index); + } else if (index == 0) { + gen_load_gpr(addr, base); + } else { + gen_op_addr_add(ctx, addr, cpu_gpr[base], cpu_gpr[index]); + } +} + static target_ulong pc_relative_pc(DisasContext *ctx) { target_ulong pc = ctx->base.pc_next; @@ -2025,6 +2036,15 @@ static void gen_lxr(DisasContext *ctx, TCGv reg, TCGv addr, tcg_gen_or_tl(reg, t0, t1); } +void gen_lx(DisasContext *ctx, int rd, int base, int index, MemOp mop) +{ + TCGv t0 = tcg_temp_new(); + + gen_base_index_addr(ctx, t0, base, index); + tcg_gen_qemu_ld_tl(t0, t0, ctx->mem_idx, mo_endian(ctx) | mop); + gen_store_gpr(t0, rd); +} + /* Load */ static void gen_ld(DisasContext *ctx, uint32_t opc, int rt, int base, int offset) @@ -10546,13 +10566,7 @@ static void gen_flt3_ldst(DisasContext *ctx, uint32_t opc, { TCGv t0 = tcg_temp_new(); - if (base == 0) { - gen_load_gpr(t0, index); - } else if (index == 0) { - gen_load_gpr(t0, base); - } else { - gen_op_addr_add(ctx, t0, cpu_gpr[base], cpu_gpr[index]); - } + gen_base_index_addr(ctx, t0, base, index); /* * Don't do NOP if destination is zero: we must perform the actual * memory access. @@ -11323,47 +11337,6 @@ enum { /* MIPSDSP functions. */ -/* Indexed load is not for DSP only */ -static void gen_mips_lx(DisasContext *ctx, uint32_t opc, - int rd, int base, int offset) -{ - TCGv t0; - - if (!(ctx->insn_flags & INSN_OCTEON)) { - check_dsp(ctx); - } - t0 = tcg_temp_new(); - - if (base == 0) { - gen_load_gpr(t0, offset); - } else if (offset == 0) { - gen_load_gpr(t0, base); - } else { - gen_op_addr_add(ctx, t0, cpu_gpr[base], cpu_gpr[offset]); - } - - switch (opc) { - case OPC_LBUX: - tcg_gen_qemu_ld_tl(t0, t0, ctx->mem_idx, MO_UB); - gen_store_gpr(t0, rd); - break; - case OPC_LHX: - tcg_gen_qemu_ld_tl(t0, t0, ctx->mem_idx, mo_endian(ctx) | MO_SW); - gen_store_gpr(t0, rd); - break; - case OPC_LWX: - tcg_gen_qemu_ld_tl(t0, t0, ctx->mem_idx, mo_endian(ctx) | MO_SL); - gen_store_gpr(t0, rd); - break; -#if defined(TARGET_MIPS64) - case OPC_LDX: - tcg_gen_qemu_ld_tl(t0, t0, ctx->mem_idx, mo_endian(ctx) | MO_UQ); - gen_store_gpr(t0, rd); - break; -#endif - } -} - static void gen_mipsdsp_arith(DisasContext *ctx, uint32_t op1, uint32_t op2, int ret, int v1, int v2) { @@ -13449,6 +13422,29 @@ static void decode_opc_special2_legacy(CPUMIPSState *env, DisasContext *ctx) } } +void gen_crc32(DisasContext *ctx, int rd, int rs, int rt, int sz, + int crc32c) +{ + TCGv t0; + TCGv t1; + TCGv_i32 tsz = tcg_constant_i32(1 << sz); + if (rd == 0) { + /* Treat as NOP. */ + return; + } + t0 = tcg_temp_new(); + t1 = tcg_temp_new(); + + gen_load_gpr(t0, rt); + gen_load_gpr(t1, rs); + + if (crc32c) { + gen_helper_crc32c(cpu_gpr[rd], t0, t1, tsz); + } else { + gen_helper_crc32(cpu_gpr[rd], t0, t1, tsz); + } +} + static void decode_opc_special3_r6(CPUMIPSState *env, DisasContext *ctx) { int rs, rt, rd, sa; @@ -13611,15 +13607,22 @@ static void decode_opc_special3_legacy(CPUMIPSState *env, DisasContext *ctx) } break; case OPC_LX_DSP: + check_dsp(ctx); op2 = MASK_LX(ctx->opcode); switch (op2) { #if defined(TARGET_MIPS64) case OPC_LDX: + gen_lx(ctx, rd, rs, rt, MO_UQ); + break; #endif case OPC_LBUX: + gen_lx(ctx, rd, rs, rt, MO_UB); + break; case OPC_LHX: + gen_lx(ctx, rd, rs, rt, MO_SW); + break; case OPC_LWX: - gen_mips_lx(ctx, op2, rd, rs, rt); + gen_lx(ctx, rd, rs, rt, MO_SL); break; default: /* Invalid */ MIPS_INVAL("MASK LX"); @@ -15095,6 +15098,7 @@ static void mips_tr_init_disas_context(DisasContextBase *dcbase, CPUState *cs) ctx->abs2008 = (env->active_fpu.fcr31 >> FCR31_ABS2008) & 1; ctx->mi = (env->CP0_Config5 >> CP0C5_MI) & 1; ctx->gi = (env->CP0_Config5 >> CP0C5_GI) & 3; + ctx->crcp = (env->CP0_Config5 >> CP0C5_CRCP) & 1; restore_cpu_state(env, ctx); #ifdef CONFIG_USER_ONLY ctx->mem_idx = MIPS_HFLAG_UM; diff --git a/target/mips/tcg/translate.h b/target/mips/tcg/translate.h index 1bf153d..89dde1e 100644 --- a/target/mips/tcg/translate.h +++ b/target/mips/tcg/translate.h @@ -51,6 +51,7 @@ typedef struct DisasContext { bool abs2008; bool mi; int gi; + bool crcp; } DisasContext; #define DISAS_STOP DISAS_TARGET_0 @@ -153,6 +154,7 @@ void check_cp1_registers(DisasContext *ctx, int regs); void check_cop1x(DisasContext *ctx); void gen_base_offset_addr(DisasContext *ctx, TCGv addr, int base, int offset); +void gen_base_index_addr(DisasContext *ctx, TCGv addr, int base, int index); void gen_move_low32(TCGv ret, TCGv_i64 arg); void gen_move_high32(TCGv ret, TCGv_i64 arg); void gen_load_gpr(TCGv t, int reg); @@ -167,6 +169,7 @@ void gen_store_fpr32(DisasContext *ctx, TCGv_i32 t, int reg); void gen_store_fpr64(DisasContext *ctx, TCGv_i64 t, int reg); int get_fp_bit(int cc); +void gen_lx(DisasContext *ctx, int rd, int base, int index, MemOp mop); void gen_ldxs(DisasContext *ctx, int base, int index, int rd); void gen_align(DisasContext *ctx, int wordsz, int rd, int rs, int rt, int bp); void gen_addiupc(DisasContext *ctx, int rx, int imm, @@ -181,6 +184,7 @@ bool gen_lsa(DisasContext *ctx, int rd, int rt, int rs, int sa); bool gen_dlsa(DisasContext *ctx, int rd, int rt, int rs, int sa); void gen_rdhwr(DisasContext *ctx, int rt, int rd, int sel); +void gen_crc32(DisasContext *ctx, int rd, int rs, int rt, int sz, int crc32c); extern TCGv cpu_gpr[32], cpu_PC; #if defined(TARGET_MIPS64) diff --git a/target/mips/tcg/translate_addr_const.c b/target/mips/tcg/translate_addr_const.c index 6f4b39f..1d140e9 100644 --- a/target/mips/tcg/translate_addr_const.c +++ b/target/mips/tcg/translate_addr_const.c @@ -26,7 +26,7 @@ bool gen_lsa(DisasContext *ctx, int rd, int rt, int rs, int sa) t1 = tcg_temp_new(); gen_load_gpr(t0, rs); gen_load_gpr(t1, rt); - tcg_gen_shli_tl(t0, t0, sa + 1); + tcg_gen_shli_tl(t0, t0, sa); tcg_gen_add_tl(cpu_gpr[rd], t0, t1); tcg_gen_ext32s_tl(cpu_gpr[rd], cpu_gpr[rd]); return true; @@ -47,7 +47,7 @@ bool gen_dlsa(DisasContext *ctx, int rd, int rt, int rs, int sa) t1 = tcg_temp_new(); gen_load_gpr(t0, rs); gen_load_gpr(t1, rt); - tcg_gen_shli_tl(t0, t0, sa + 1); + tcg_gen_shli_tl(t0, t0, sa); tcg_gen_add_tl(cpu_gpr[rd], t0, t1); return true; } diff --git a/target/ppc/mmu-hash64.h b/target/ppc/mmu-hash64.h index b8fb12a..ae8d4b3 100644 --- a/target/ppc/mmu-hash64.h +++ b/target/ppc/mmu-hash64.h @@ -1,8 +1,6 @@ #ifndef MMU_HASH64_H #define MMU_HASH64_H -#include "exec/tswap.h" - #ifndef CONFIG_USER_ONLY #ifdef TARGET_PPC64 diff --git a/target/riscv/vector_helper.c b/target/riscv/vector_helper.c index b41c29d..7c67d67 100644 --- a/target/riscv/vector_helper.c +++ b/target/riscv/vector_helper.c @@ -27,7 +27,6 @@ #include "exec/helper-proto.h" #include "exec/tlb-flags.h" #include "exec/target_page.h" -#include "exec/tswap.h" #include "fpu/softfloat.h" #include "tcg/tcg-gvec-desc.h" #include "internals.h" diff --git a/target/sparc/cpu.c b/target/sparc/cpu.c index ed7701b..245caf2 100644 --- a/target/sparc/cpu.c +++ b/target/sparc/cpu.c @@ -1090,6 +1090,7 @@ static void sparc_cpu_class_init(ObjectClass *oc, const void *data) cc->disas_set_info = cpu_sparc_disas_set_info; #if defined(TARGET_SPARC64) && !defined(TARGET_ABI32) + cc->gdb_core_xml_file = "sparc64-core.xml"; cc->gdb_num_core_regs = 86; #else cc->gdb_num_core_regs = 72; diff --git a/tests/data/acpi/aarch64/virt/DSDT b/tests/data/acpi/aarch64/virt/DSDT Binary files differindex 36d3e5d..18d97e8 100644 --- a/tests/data/acpi/aarch64/virt/DSDT +++ b/tests/data/acpi/aarch64/virt/DSDT diff --git a/tests/data/acpi/aarch64/virt/DSDT.acpihmatvirt b/tests/data/acpi/aarch64/virt/DSDT.acpihmatvirt Binary files differindex e6154d0..2cef095 100644 --- a/tests/data/acpi/aarch64/virt/DSDT.acpihmatvirt +++ b/tests/data/acpi/aarch64/virt/DSDT.acpihmatvirt diff --git a/tests/data/acpi/aarch64/virt/DSDT.acpipcihp b/tests/data/acpi/aarch64/virt/DSDT.acpipcihp Binary files differnew file mode 100644 index 0000000..8d55a87 --- /dev/null +++ b/tests/data/acpi/aarch64/virt/DSDT.acpipcihp diff --git a/tests/data/acpi/aarch64/virt/DSDT.hpoffacpiindex b/tests/data/acpi/aarch64/virt/DSDT.hpoffacpiindex Binary files differnew file mode 100644 index 0000000..970d43f --- /dev/null +++ b/tests/data/acpi/aarch64/virt/DSDT.hpoffacpiindex diff --git a/tests/data/acpi/aarch64/virt/DSDT.memhp b/tests/data/acpi/aarch64/virt/DSDT.memhp Binary files differindex 33f011d..372ca3d 100644 --- a/tests/data/acpi/aarch64/virt/DSDT.memhp +++ b/tests/data/acpi/aarch64/virt/DSDT.memhp diff --git a/tests/data/acpi/aarch64/virt/DSDT.pxb b/tests/data/acpi/aarch64/virt/DSDT.pxb Binary files differindex c0fdc6e..c277988 100644 --- a/tests/data/acpi/aarch64/virt/DSDT.pxb +++ b/tests/data/acpi/aarch64/virt/DSDT.pxb diff --git a/tests/data/acpi/aarch64/virt/DSDT.topology b/tests/data/acpi/aarch64/virt/DSDT.topology Binary files differindex 029d03e..ebbeedc 100644 --- a/tests/data/acpi/aarch64/virt/DSDT.topology +++ b/tests/data/acpi/aarch64/virt/DSDT.topology diff --git a/tests/data/acpi/aarch64/virt/DSDT.viot b/tests/data/acpi/aarch64/virt/DSDT.viot Binary files differnew file mode 100644 index 0000000..b897d66 --- /dev/null +++ b/tests/data/acpi/aarch64/virt/DSDT.viot diff --git a/tests/data/acpi/aarch64/virt/PPTT b/tests/data/acpi/aarch64/virt/PPTT Binary files differindex 7a1258e..15598a9 100644 --- a/tests/data/acpi/aarch64/virt/PPTT +++ b/tests/data/acpi/aarch64/virt/PPTT diff --git a/tests/data/acpi/aarch64/virt/PPTT.acpihmatvirt b/tests/data/acpi/aarch64/virt/PPTT.acpihmatvirt Binary files differindex 4eef303..7b613dd 100644 --- a/tests/data/acpi/aarch64/virt/PPTT.acpihmatvirt +++ b/tests/data/acpi/aarch64/virt/PPTT.acpihmatvirt diff --git a/tests/data/acpi/aarch64/virt/PPTT.topology b/tests/data/acpi/aarch64/virt/PPTT.topology Binary files differindex 3fbcae5..6b864f0 100644 --- a/tests/data/acpi/aarch64/virt/PPTT.topology +++ b/tests/data/acpi/aarch64/virt/PPTT.topology diff --git a/tests/data/acpi/loongarch64/virt/APIC b/tests/data/acpi/loongarch64/virt/APIC Binary files differnew file mode 100644 index 0000000..3477789 --- /dev/null +++ b/tests/data/acpi/loongarch64/virt/APIC diff --git a/tests/data/acpi/loongarch64/virt/APIC.topology b/tests/data/acpi/loongarch64/virt/APIC.topology Binary files differnew file mode 100644 index 0000000..da0089d --- /dev/null +++ b/tests/data/acpi/loongarch64/virt/APIC.topology diff --git a/tests/data/acpi/loongarch64/virt/DSDT b/tests/data/acpi/loongarch64/virt/DSDT Binary files differnew file mode 100644 index 0000000..b31841a --- /dev/null +++ b/tests/data/acpi/loongarch64/virt/DSDT diff --git a/tests/data/acpi/loongarch64/virt/DSDT.memhp b/tests/data/acpi/loongarch64/virt/DSDT.memhp Binary files differnew file mode 100644 index 0000000..e291200 --- /dev/null +++ b/tests/data/acpi/loongarch64/virt/DSDT.memhp diff --git a/tests/data/acpi/loongarch64/virt/DSDT.numamem b/tests/data/acpi/loongarch64/virt/DSDT.numamem Binary files differnew file mode 100644 index 0000000..07923ac --- /dev/null +++ b/tests/data/acpi/loongarch64/virt/DSDT.numamem diff --git a/tests/data/acpi/loongarch64/virt/DSDT.topology b/tests/data/acpi/loongarch64/virt/DSDT.topology Binary files differnew file mode 100644 index 0000000..6dfbb49 --- /dev/null +++ b/tests/data/acpi/loongarch64/virt/DSDT.topology diff --git a/tests/data/acpi/loongarch64/virt/FACP b/tests/data/acpi/loongarch64/virt/FACP Binary files differnew file mode 100644 index 0000000..04d8d4c --- /dev/null +++ b/tests/data/acpi/loongarch64/virt/FACP diff --git a/tests/data/acpi/loongarch64/virt/MCFG b/tests/data/acpi/loongarch64/virt/MCFG Binary files differnew file mode 100644 index 0000000..5f93b05 --- /dev/null +++ b/tests/data/acpi/loongarch64/virt/MCFG diff --git a/tests/data/acpi/loongarch64/virt/PPTT b/tests/data/acpi/loongarch64/virt/PPTT Binary files differnew file mode 100644 index 0000000..15598a9 --- /dev/null +++ b/tests/data/acpi/loongarch64/virt/PPTT diff --git a/tests/data/acpi/loongarch64/virt/PPTT.topology b/tests/data/acpi/loongarch64/virt/PPTT.topology Binary files differnew file mode 100644 index 0000000..7fc9298 --- /dev/null +++ b/tests/data/acpi/loongarch64/virt/PPTT.topology diff --git a/tests/data/acpi/loongarch64/virt/SLIT b/tests/data/acpi/loongarch64/virt/SLIT new file mode 100644 index 0000000..e69de29 --- /dev/null +++ b/tests/data/acpi/loongarch64/virt/SLIT diff --git a/tests/data/acpi/loongarch64/virt/SLIT.numamem b/tests/data/acpi/loongarch64/virt/SLIT.numamem Binary files differnew file mode 100644 index 0000000..67f0081 --- /dev/null +++ b/tests/data/acpi/loongarch64/virt/SLIT.numamem diff --git a/tests/data/acpi/loongarch64/virt/SPCR b/tests/data/acpi/loongarch64/virt/SPCR Binary files differnew file mode 100644 index 0000000..3cc9bbc --- /dev/null +++ b/tests/data/acpi/loongarch64/virt/SPCR diff --git a/tests/data/acpi/loongarch64/virt/SRAT b/tests/data/acpi/loongarch64/virt/SRAT Binary files differnew file mode 100644 index 0000000..ff234ce --- /dev/null +++ b/tests/data/acpi/loongarch64/virt/SRAT diff --git a/tests/data/acpi/loongarch64/virt/SRAT.memhp b/tests/data/acpi/loongarch64/virt/SRAT.memhp Binary files differnew file mode 100644 index 0000000..5253218 --- /dev/null +++ b/tests/data/acpi/loongarch64/virt/SRAT.memhp diff --git a/tests/data/acpi/loongarch64/virt/SRAT.numamem b/tests/data/acpi/loongarch64/virt/SRAT.numamem Binary files differnew file mode 100644 index 0000000..2972a9a --- /dev/null +++ b/tests/data/acpi/loongarch64/virt/SRAT.numamem diff --git a/tests/data/acpi/loongarch64/virt/SRAT.topology b/tests/data/acpi/loongarch64/virt/SRAT.topology Binary files differnew file mode 100644 index 0000000..4a44831 --- /dev/null +++ b/tests/data/acpi/loongarch64/virt/SRAT.topology diff --git a/tests/data/acpi/rebuild-expected-aml.sh b/tests/data/acpi/rebuild-expected-aml.sh index c1092fb..cbf9ffe 100755 --- a/tests/data/acpi/rebuild-expected-aml.sh +++ b/tests/data/acpi/rebuild-expected-aml.sh @@ -12,7 +12,7 @@ # This work is licensed under the terms of the GNU GPLv2. # See the COPYING.LIB file in the top-level directory. -qemu_arches="x86_64 aarch64 riscv64" +qemu_arches="x86_64 aarch64 riscv64 loongarch64" if [ ! -e "tests/qtest/bios-tables-test" ]; then echo "Test: bios-tables-test is required! Run make check before this script." @@ -37,7 +37,7 @@ if [ -z "$qemu_bins" ]; then echo "Only the following architectures are currently supported: $qemu_arches" echo "None of these configured!" echo "To fix, run configure \ - --target-list=x86_64-softmmu,aarch64-softmmu,riscv64-softmmu" + --target-list=x86_64-softmmu,aarch64-softmmu,riscv64-softmmu,loongarch64-softmmu" exit 1; fi diff --git a/tests/data/acpi/riscv64/virt/DSDT b/tests/data/acpi/riscv64/virt/DSDT Binary files differindex 6a33f56..527f239 100644 --- a/tests/data/acpi/riscv64/virt/DSDT +++ b/tests/data/acpi/riscv64/virt/DSDT diff --git a/tests/data/acpi/x86/microvm/DSDT.pcie b/tests/data/acpi/x86/microvm/DSDT.pcie Binary files differindex 8eacd21..ba258f4 100644 --- a/tests/data/acpi/x86/microvm/DSDT.pcie +++ b/tests/data/acpi/x86/microvm/DSDT.pcie diff --git a/tests/functional/test_x86_cpu_model_versions.py b/tests/functional/test_x86_cpu_model_versions.py index bd18acd..36c968f 100755 --- a/tests/functional/test_x86_cpu_model_versions.py +++ b/tests/functional/test_x86_cpu_model_versions.py @@ -72,44 +72,11 @@ class X86CPUModelAliases(QemuSystemTest): self.assertNotIn("EPYC-IBPB-v1", cpus, "EPYC-IBPB shouldn't be versioned") - def test_4_0_alias_compatibility(self): - """ - Check if pc-*-4.0 unversioned CPU model won't be reported as aliases - """ - self.set_machine('pc-i440fx-4.0') - # pc-*-4.0 won't expose non-versioned CPU models as aliases - # We do this to help management software to keep compatibility - # with older QEMU versions that didn't have the versioned CPU model - self.vm.add_args('-S') - self.vm.launch() - cpus = dict((m['name'], m) for m in - self.vm.cmd('query-cpu-definitions')) - - self.assertFalse(cpus['Cascadelake-Server']['static'], - 'unversioned Cascadelake-Server CPU model must not be static') - self.assertNotIn('alias-of', cpus['Cascadelake-Server'], - 'Cascadelake-Server must not be an alias') - self.assertNotIn('alias-of', cpus['Cascadelake-Server-v1'], - 'Cascadelake-Server-v1 must not be an alias') - - self.assertFalse(cpus['qemu64']['static'], - 'unversioned qemu64 CPU model must not be static') - self.assertNotIn('alias-of', cpus['qemu64'], - 'qemu64 must not be an alias') - self.assertNotIn('alias-of', cpus['qemu64-v1'], - 'qemu64-v1 must not be an alias') - - self.validate_variant_aliases(cpus) - - # On pc-*-4.0, no CPU model should be reported as an alias: - for name,c in cpus.items(): - self.assertNotIn('alias-of', c, "%s shouldn't be an alias" % (name)) - - def test_4_1_alias(self): + def test_unversioned_alias(self): """ Check if unversioned CPU model is an alias pointing to right version """ - self.set_machine('pc-i440fx-4.1') + self.set_machine('pc') self.vm.add_args('-S') self.vm.launch() @@ -133,7 +100,7 @@ class X86CPUModelAliases(QemuSystemTest): self.validate_variant_aliases(cpus) - # On pc-*-4.1, -noTSX and -IBRS models should be aliases: + # On recent PC machines, -noTSX and -IBRS models should be aliases: self.assertEqual(cpus["Haswell"].get('alias-of'), "Haswell-v1", "Haswell must be an alias") @@ -247,8 +214,8 @@ class CascadelakeArchCapabilities(QemuSystemTest): cpu_path = self.vm.cmd('query-cpus-fast')[0].get('qom-path') return self.vm.cmd('qom-get', path=cpu_path, property=prop) - def test_4_1(self): - self.set_machine('pc-i440fx-4.1') + def test(self): + self.set_machine('pc') # machine-type only: self.vm.add_args('-S') self.set_vm_arg('-cpu', @@ -256,80 +223,27 @@ class CascadelakeArchCapabilities(QemuSystemTest): 'enforce=off') self.vm.launch() self.assertFalse(self.get_cpu_prop('arch-capabilities'), - 'pc-i440fx-4.1 + Cascadelake-Server should not have arch-capabilities') - - def test_4_0(self): - self.set_machine('pc-i440fx-4.0') - self.vm.add_args('-S') - self.set_vm_arg('-cpu', - 'Cascadelake-Server,x-force-features=on,check=off,' - 'enforce=off') - self.vm.launch() - self.assertFalse(self.get_cpu_prop('arch-capabilities'), - 'pc-i440fx-4.0 + Cascadelake-Server should not have arch-capabilities') - - def test_set_4_0(self): - self.set_machine('pc-i440fx-4.0') - # command line must override machine-type if CPU model is not versioned: - self.vm.add_args('-S') - self.set_vm_arg('-cpu', - 'Cascadelake-Server,x-force-features=on,check=off,' - 'enforce=off,+arch-capabilities') - self.vm.launch() - self.assertTrue(self.get_cpu_prop('arch-capabilities'), - 'pc-i440fx-4.0 + Cascadelake-Server,+arch-capabilities should have arch-capabilities') + 'pc + Cascadelake-Server should not have arch-capabilities') - def test_unset_4_1(self): - self.set_machine('pc-i440fx-4.1') + def test_unset(self): + self.set_machine('pc') self.vm.add_args('-S') self.set_vm_arg('-cpu', 'Cascadelake-Server,x-force-features=on,check=off,' 'enforce=off,-arch-capabilities') self.vm.launch() self.assertFalse(self.get_cpu_prop('arch-capabilities'), - 'pc-i440fx-4.1 + Cascadelake-Server,-arch-capabilities should not have arch-capabilities') - - def test_v1_4_0(self): - self.set_machine('pc-i440fx-4.0') - # versioned CPU model overrides machine-type: - self.vm.add_args('-S') - self.set_vm_arg('-cpu', - 'Cascadelake-Server-v1,x-force-features=on,check=off,' - 'enforce=off') - self.vm.launch() - self.assertFalse(self.get_cpu_prop('arch-capabilities'), - 'pc-i440fx-4.0 + Cascadelake-Server-v1 should not have arch-capabilities') - - def test_v2_4_0(self): - self.set_machine('pc-i440fx-4.0') - self.vm.add_args('-S') - self.set_vm_arg('-cpu', - 'Cascadelake-Server-v2,x-force-features=on,check=off,' - 'enforce=off') - self.vm.launch() - self.assertTrue(self.get_cpu_prop('arch-capabilities'), - 'pc-i440fx-4.0 + Cascadelake-Server-v2 should have arch-capabilities') - - def test_v1_set_4_0(self): - self.set_machine('pc-i440fx-4.0') - # command line must override machine-type and versioned CPU model: - self.vm.add_args('-S') - self.set_vm_arg('-cpu', - 'Cascadelake-Server-v1,x-force-features=on,check=off,' - 'enforce=off,+arch-capabilities') - self.vm.launch() - self.assertTrue(self.get_cpu_prop('arch-capabilities'), - 'pc-i440fx-4.0 + Cascadelake-Server-v1,+arch-capabilities should have arch-capabilities') + 'pc + Cascadelake-Server,-arch-capabilities should not have arch-capabilities') - def test_v2_unset_4_1(self): - self.set_machine('pc-i440fx-4.1') + def test_v2_unset(self): + self.set_machine('pc') self.vm.add_args('-S') self.set_vm_arg('-cpu', 'Cascadelake-Server-v2,x-force-features=on,check=off,' 'enforce=off,-arch-capabilities') self.vm.launch() self.assertFalse(self.get_cpu_prop('arch-capabilities'), - 'pc-i440fx-4.1 + Cascadelake-Server-v2,-arch-capabilities should not have arch-capabilities') + 'pc + Cascadelake-Server-v2,-arch-capabilities should not have arch-capabilities') if __name__ == '__main__': QemuSystemTest.main() diff --git a/tests/qtest/bios-tables-test.c b/tests/qtest/bios-tables-test.c index 4dbc07e..6aec68d 100644 --- a/tests/qtest/bios-tables-test.c +++ b/tests/qtest/bios-tables-test.c @@ -1643,6 +1643,54 @@ static void test_acpi_aarch64_virt_tcg_memhp(void) } +static void test_acpi_aarch64_virt_acpi_pci_hotplug(void) +{ + test_data data = { + .machine = "virt", + .arch = "aarch64", + .tcg_only = true, + .uefi_fl1 = "pc-bios/edk2-aarch64-code.fd", + .uefi_fl2 = "pc-bios/edk2-arm-vars.fd", + .cd = "tests/data/uefi-boot-images/bios-tables-test.aarch64.iso.qcow2", + .ram_start = 0x40000000ULL, + .scan_len = 256ULL * MiB, + .variant = ".acpipcihp", + }; + + /* Use ACPI PCI Hotplug */ + test_acpi_one(" -global acpi-ged.acpi-pci-hotplug-with-bridge-support=on" + " -cpu cortex-a57" + " -device pcie-root-port,id=pcie.1,bus=pcie.0,chassis=0,slot=1,addr=7.0" + " -device pci-testdev,bus=pcie.1", + &data); + + free_test_data(&data); +} + +static void test_acpi_aarch64_virt_pcie_root_port_hpoff(void) +{ + test_data data = { + .machine = "virt", + .arch = "aarch64", + .tcg_only = true, + .uefi_fl1 = "pc-bios/edk2-aarch64-code.fd", + .uefi_fl2 = "pc-bios/edk2-arm-vars.fd", + .cd = "tests/data/uefi-boot-images/bios-tables-test.aarch64.iso.qcow2", + .ram_start = 0x40000000ULL, + .scan_len = 256ULL * MiB, + .variant = ".hpoffacpiindex", + }; + + /* turn hotplug off on the pcie-root-port and use static acpi-index*/ + test_acpi_one(" -device pcie-root-port,id=pcie.1,chassis=0," + "slot=1,hotplug=off,addr=7.0" + " -device pci-testdev,bus=pcie.1,acpi-index=12" + " -cpu cortex-a57", + &data); + + free_test_data(&data); +} + static void test_acpi_microvm_prepare(test_data *data) { data->machine = "microvm"; @@ -1789,6 +1837,44 @@ static void test_acpi_aarch64_virt_tcg_pxb(void) free_test_data(&data); } +static void test_acpi_aarch64_virt_tcg_acpi_spcr(void) +{ + test_data data = { + .machine = "virt", + .arch = "aarch64", + .tcg_only = true, + .uefi_fl1 = "pc-bios/edk2-aarch64-code.fd", + .uefi_fl2 = "pc-bios/edk2-arm-vars.fd", + .cd = "tests/data/uefi-boot-images/bios-tables-test.aarch64.iso.qcow2", + .ram_start = 0x40000000ULL, + .scan_len = 128ULL * 1024 * 1024, + .variant = ".acpispcr", + }; + + test_acpi_one("-cpu cortex-a57 " + " -machine spcr=off", &data); + free_test_data(&data); +} + +static void test_acpi_riscv64_virt_tcg_acpi_spcr(void) +{ + test_data data = { + .machine = "virt", + .arch = "riscv64", + .tcg_only = true, + .uefi_fl1 = "pc-bios/edk2-riscv-code.fd", + .uefi_fl2 = "pc-bios/edk2-riscv-vars.fd", + .cd = "tests/data/uefi-boot-images/bios-tables-test.riscv64.iso.qcow2", + .ram_start = 0x80000000ULL, + .scan_len = 128ULL * 1024 * 1024, + .variant = ".acpispcr", + }; + + test_acpi_one("-cpu rva22s64 " + "-machine spcr=off", &data); + free_test_data(&data); +} + static void test_acpi_tcg_acpi_hmat(const char *machine, const char *arch) { test_data data = {}; @@ -2237,6 +2323,7 @@ static void test_acpi_aarch64_virt_viot(void) test_data data = { .machine = "virt", .arch = "aarch64", + .variant = ".viot", .tcg_only = true, .uefi_fl1 = "pc-bios/edk2-aarch64-code.fd", .uefi_fl2 = "pc-bios/edk2-arm-vars.fd", @@ -2439,6 +2526,74 @@ static void test_acpi_aarch64_virt_oem_fields(void) g_free(args); } +#define LOONGARCH64_INIT_TEST_DATA(data) \ + test_data data = { \ + .machine = "virt", \ + .arch = "loongarch64", \ + .tcg_only = true, \ + .uefi_fl1 = "pc-bios/edk2-loongarch64-code.fd", \ + .uefi_fl2 = "pc-bios/edk2-loongarch64-vars.fd", \ + .cd = "tests/data/uefi-boot-images/" \ + "bios-tables-test.loongarch64.iso.qcow2", \ + .ram_start = 0, \ + .scan_len = 128ULL * MiB, \ + } + +static void test_acpi_loongarch64_virt(void) +{ + LOONGARCH64_INIT_TEST_DATA(data); + + test_acpi_one("-cpu la464 ", &data); + free_test_data(&data); +} + +static void test_acpi_loongarch64_virt_topology(void) +{ + LOONGARCH64_INIT_TEST_DATA(data); + + data.variant = ".topology"; + test_acpi_one("-cpu la464 -smp sockets=1,cores=2,threads=2", &data); + free_test_data(&data); +} + +static void test_acpi_loongarch64_virt_numamem(void) +{ + LOONGARCH64_INIT_TEST_DATA(data); + + data.variant = ".numamem"; + test_acpi_one(" -cpu la464 -m 128" + " -object memory-backend-ram,id=ram0,size=64M" + " -object memory-backend-ram,id=ram1,size=64M" + " -numa node,memdev=ram0 -numa node,memdev=ram1" + " -numa dist,src=0,dst=1,val=21", + &data); + free_test_data(&data); +} + +static void test_acpi_loongarch64_virt_memhp(void) +{ + LOONGARCH64_INIT_TEST_DATA(data); + + data.variant = ".memhp"; + test_acpi_one(" -cpu la464 -m 128,slots=2,maxmem=256M" + " -object memory-backend-ram,id=ram0,size=128M", + &data); + free_test_data(&data); +} + +static void test_acpi_loongarch64_virt_oem_fields(void) +{ + LOONGARCH64_INIT_TEST_DATA(data); + char *args; + + args = test_acpi_create_args(&data, "-cpu la464 "OEM_TEST_ARGS); + data.qts = qtest_init(args); + test_acpi_load_tables(&data); + test_oem_fields(&data); + qtest_quit(data.qts); + free_test_data(&data); + g_free(args); +} int main(int argc, char *argv[]) { @@ -2601,9 +2756,15 @@ int main(int argc, char *argv[]) qtest_add_func("acpi/virt/numamem", test_acpi_aarch64_virt_tcg_numamem); qtest_add_func("acpi/virt/memhp", test_acpi_aarch64_virt_tcg_memhp); + qtest_add_func("acpi/virt/acpipcihp", + test_acpi_aarch64_virt_acpi_pci_hotplug); + qtest_add_func("acpi/virt/hpoffacpiindex", + test_acpi_aarch64_virt_pcie_root_port_hpoff); qtest_add_func("acpi/virt/pxb", test_acpi_aarch64_virt_tcg_pxb); qtest_add_func("acpi/virt/oem-fields", test_acpi_aarch64_virt_oem_fields); + qtest_add_func("acpi/virt/acpispcr", + test_acpi_aarch64_virt_tcg_acpi_spcr); if (qtest_has_device("virtio-iommu-pci")) { qtest_add_func("acpi/virt/viot", test_acpi_aarch64_virt_viot); } @@ -2613,6 +2774,19 @@ int main(int argc, char *argv[]) qtest_add_func("acpi/virt", test_acpi_riscv64_virt_tcg); qtest_add_func("acpi/virt/numamem", test_acpi_riscv64_virt_tcg_numamem); + qtest_add_func("acpi/virt/acpispcr", + test_acpi_riscv64_virt_tcg_acpi_spcr); + } + } else if (strcmp(arch, "loongarch64") == 0) { + if (has_tcg) { + qtest_add_func("acpi/virt", test_acpi_loongarch64_virt); + qtest_add_func("acpi/virt/topology", + test_acpi_loongarch64_virt_topology); + qtest_add_func("acpi/virt/numamem", + test_acpi_loongarch64_virt_numamem); + qtest_add_func("acpi/virt/memhp", test_acpi_loongarch64_virt_memhp); + qtest_add_func("acpi/virt/oem-fields", + test_acpi_loongarch64_virt_oem_fields); } } ret = g_test_run(); diff --git a/tests/qtest/meson.build b/tests/qtest/meson.build index 5ad969f..669d07c 100644 --- a/tests/qtest/meson.build +++ b/tests/qtest/meson.build @@ -148,6 +148,7 @@ qtests_hppa = \ qtests_loongarch64 = qtests_filter + \ (config_all_devices.has_key('CONFIG_LOONGARCH_VIRT') ? ['numa-test'] : []) + \ + (unpack_edk2_blobs ? ['bios-tables-test'] : []) + \ ['boot-serial-test', 'cpu-plug-test'] diff --git a/tests/tcg/mips/include/wrappers_mips64r6.h b/tests/tcg/mips/include/wrappers_mips64r6.h index d1e5edb..33d03de 100644 --- a/tests/tcg/mips/include/wrappers_mips64r6.h +++ b/tests/tcg/mips/include/wrappers_mips64r6.h @@ -23,6 +23,7 @@ #ifndef WRAPPERS_MIPS64R6_H #define WRAPPERS_MIPS64R6_H +#include <string.h> #define DO_MIPS64R6__RD__RS(suffix, mnemonic) \ static inline void do_mips64r6_##suffix(const void *input, \ @@ -80,4 +81,35 @@ DO_MIPS64R6__RD__RS_RT(DMULU, dmulu) DO_MIPS64R6__RD__RS_RT(DMUHU, dmuhu) +#define DO_MIPS64R6__RT__RS_RT(suffix, mnemonic) \ +static inline void do_mips64r6_##suffix(const void *input1, \ + const void *input2, \ + void *output) \ +{ \ + if (strncmp(#mnemonic, "crc32", 5) == 0) \ + __asm__ volatile ( \ + ".set crc\n\t" \ + ); \ + \ + __asm__ volatile ( \ + "ld $t1, 0(%0)\n\t" \ + "ld $t2, 0(%1)\n\t" \ + #mnemonic " $t2, $t1, $t2\n\t" \ + "sd $t2, 0(%2)\n\t" \ + : \ + : "r" (input1), "r" (input2), "r" (output) \ + : "t0", "t1", "t2", "memory" \ + ); \ +} + +DO_MIPS64R6__RT__RS_RT(CRC32B, crc32b) +DO_MIPS64R6__RT__RS_RT(CRC32H, crc32h) +DO_MIPS64R6__RT__RS_RT(CRC32W, crc32w) +DO_MIPS64R6__RT__RS_RT(CRC32D, crc32d) + +DO_MIPS64R6__RT__RS_RT(CRC32CB, crc32cb) +DO_MIPS64R6__RT__RS_RT(CRC32CH, crc32ch) +DO_MIPS64R6__RT__RS_RT(CRC32CW, crc32cw) +DO_MIPS64R6__RT__RS_RT(CRC32CD, crc32cd) + #endif diff --git a/tests/tcg/mips/user/isa/mips64r6/crc/Makefile b/tests/tcg/mips/user/isa/mips64r6/crc/Makefile new file mode 100644 index 0000000..b7f5811 --- /dev/null +++ b/tests/tcg/mips/user/isa/mips64r6/crc/Makefile @@ -0,0 +1,40 @@ +# +# Test program for MIPS64R6 CRC32 instructions +# +# Copyright (C) 2025 Aleksandar Rakic <aleksandar.rakic@htecgroup.com> +# +# SPDX-License-Identifier: GPL-2.0-or-later +# + +ifndef PREFIX + $(error "PREFIX not set, please export GNU Toolchain install directory.") +endif + +ifndef SYSROOT + $(error "SYSROOT not set, please export GNU Toolchain system root directory.") +endif + +SIM = ../../../../../../../build/qemu-mips64 +SIM_FLAGS = -L $(SYSROOT) + +CC = $(PREFIX)/bin/mips64-r6-linux-gnu-gcc + +TESTCASES = test_mips64r6_crc32b.tst +TESTCASES += test_mips64r6_crc32h.tst +TESTCASES += test_mips64r6_crc32w.tst +TESTCASES += test_mips64r6_crc32d.tst +TESTCASES += test_mips64r6_crc32cb.tst +TESTCASES += test_mips64r6_crc32ch.tst +TESTCASES += test_mips64r6_crc32cw.tst +TESTCASES += test_mips64r6_crc32cd.tst + +all: $(TESTCASES) + @for case in $(TESTCASES); do \ + echo $(SIM) $(SIM_FLAGS) ./$$case; \ + $(SIM) $(SIM_FLAGS) ./$$case; \ + echo $(RM) -rf ./$$case; \ + $(RM) -rf ./$$case; \ + done + +%.tst: %.c + $(CC) $< -o $@ diff --git a/tests/tcg/mips/user/isa/mips64r6/crc/test_mips64r6_crc32b.c b/tests/tcg/mips/user/isa/mips64r6/crc/test_mips64r6_crc32b.c new file mode 100644 index 0000000..bb1f3f6 --- /dev/null +++ b/tests/tcg/mips/user/isa/mips64r6/crc/test_mips64r6_crc32b.c @@ -0,0 +1,142 @@ +/* + * Test program for MIPS64R6 instruction CRC32B + * + * Copyright (C) 2019 Wave Computing, Inc. + * Copyright (C) 2019 Aleksandar Markovic <amarkovic@wavecomp.com> + * Copyright (C) 2025 Aleksandar Rakic <aleksandar.rakic@htecgroup.com> + * + * SPDX-License-Identifier: GPL-2.0-or-later + */ + +#include <sys/time.h> +#include <stdint.h> + +#include "../../../../include/wrappers_mips64r6.h" +#include "../../../../include/test_inputs_64.h" +#include "../../../../include/test_utils_64.h" + +#define TEST_COUNT_TOTAL (PATTERN_INPUTS_64_COUNT + RANDOM_INPUTS_64_COUNT) + +int32_t main(void) +{ + char *isa_ase_name = "mips64r6"; + char *group_name = "CRC with reversed polynomial 0xEDB88320"; + char *instruction_name = "CRC32B"; + int32_t ret; + uint32_t i, j; + struct timeval start, end; + double elapsed_time; + + uint64_t b64_result[TEST_COUNT_TOTAL]; + uint64_t b64_expect[TEST_COUNT_TOTAL] = { + 0x0000000000ffffffULL, /* 0 */ + 0x000000002d02ef8dULL, + 0x000000001bab0fd1ULL, + 0x0000000036561fa3ULL, + 0xffffffffbf1caddaULL, + 0xffffffff92e1bda8ULL, + 0x00000000278c7949ULL, + 0x000000000a71693bULL, + 0x000000002dfd1072ULL, /* 8 */ + 0x0000000000000000ULL, + 0x0000000036a9e05cULL, + 0x000000001b54f02eULL, + 0xffffffff921e4257ULL, + 0xffffffffbfe35225ULL, + 0x000000000a8e96c4ULL, + 0x00000000277386b6ULL, + 0x000000001bfe5a84ULL, /* 16 */ + 0x0000000036034af6ULL, + 0x0000000000aaaaaaULL, + 0x000000002d57bad8ULL, + 0xffffffffa41d08a1ULL, + 0xffffffff89e018d3ULL, + 0x000000003c8ddc32ULL, + 0x000000001170cc40ULL, + 0x0000000036fcb509ULL, /* 24 */ + 0x000000001b01a57bULL, + 0x000000002da84527ULL, + 0x0000000000555555ULL, + 0xffffffff891fe72cULL, + 0xffffffffa4e2f75eULL, + 0x00000000118f33bfULL, + 0x000000003c7223cdULL, + 0xffffffffbf2f9ee9ULL, /* 32 */ + 0xffffffff92d28e9bULL, + 0xffffffffa47b6ec7ULL, + 0xffffffff89867eb5ULL, + 0x0000000000ccccccULL, + 0x000000002d31dcbeULL, + 0xffffffff985c185fULL, + 0xffffffffb5a1082dULL, + 0xffffffff922d7164ULL, /* 40 */ + 0xffffffffbfd06116ULL, + 0xffffffff8979814aULL, + 0xffffffffa4849138ULL, + 0x000000002dce2341ULL, + 0x0000000000333333ULL, + 0xffffffffb55ef7d2ULL, + 0xffffffff98a3e7a0ULL, + 0x0000000027fdbe55ULL, /* 48 */ + 0x000000000a00ae27ULL, + 0x000000003ca94e7bULL, + 0x0000000011545e09ULL, + 0xffffffff981eec70ULL, + 0xffffffffb5e3fc02ULL, + 0x00000000008e38e3ULL, + 0x000000002d732891ULL, + 0x000000000aff51d8ULL, /* 56 */ + 0x00000000270241aaULL, + 0x0000000011aba1f6ULL, + 0x000000003c56b184ULL, + 0xffffffffb51c03fdULL, + 0xffffffff98e1138fULL, + 0x000000002d8cd76eULL, + 0x000000000071c71cULL, + 0x0000000000286255ULL, /* 64 */ + 0x00000000784a5a65ULL, + 0xffffffff9bdd0d3bULL, + 0xffffffffe7e61ce5ULL, + 0x00000000782fabf7ULL, + 0x00000000004d93c7ULL, + 0xffffffffe3dac499ULL, + 0xffffffff9fe1d547ULL, + 0xffffffff9b4ca0e5ULL, /* 72 */ + 0xffffffffe32e98d5ULL, + 0x0000000000b9cf8bULL, + 0x000000007c82de55ULL, + 0xffffffffe7904f52ULL, + 0xffffffff9ff27762ULL, + 0x000000007c65203cULL, + 0x00000000005e31e2ULL, + }; + + gettimeofday(&start, NULL); + + for (i = 0; i < PATTERN_INPUTS_64_SHORT_COUNT; i++) { + for (j = 0; j < PATTERN_INPUTS_64_SHORT_COUNT; j++) { + do_mips64r6_CRC32B(b64_pattern + i, b64_pattern + j, + b64_result + (PATTERN_INPUTS_64_SHORT_COUNT * i + j)); + } + } + + for (i = 0; i < RANDOM_INPUTS_64_SHORT_COUNT; i++) { + for (j = 0; j < RANDOM_INPUTS_64_SHORT_COUNT; j++) { + do_mips64r6_CRC32B(b64_random + i, b64_random + j, + b64_result + (((PATTERN_INPUTS_64_SHORT_COUNT) * + (PATTERN_INPUTS_64_SHORT_COUNT)) + + RANDOM_INPUTS_64_SHORT_COUNT * i + j)); + } + } + + gettimeofday(&end, NULL); + + elapsed_time = (end.tv_sec - start.tv_sec) * 1000.0; + elapsed_time += (end.tv_usec - start.tv_usec) / 1000.0; + + ret = check_results_64(isa_ase_name, group_name, instruction_name, + TEST_COUNT_TOTAL, elapsed_time, b64_result, + b64_expect); + + return ret; +} diff --git a/tests/tcg/mips/user/isa/mips64r6/crc/test_mips64r6_crc32cb.c b/tests/tcg/mips/user/isa/mips64r6/crc/test_mips64r6_crc32cb.c new file mode 100644 index 0000000..1439d44 --- /dev/null +++ b/tests/tcg/mips/user/isa/mips64r6/crc/test_mips64r6_crc32cb.c @@ -0,0 +1,142 @@ +/* + * Test program for MIPS64R6 instruction CRC32CB + * + * Copyright (C) 2019 Wave Computing, Inc. + * Copyright (C) 2019 Aleksandar Markovic <amarkovic@wavecomp.com> + * Copyright (C) 2025 Aleksandar Rakic <aleksandar.rakic@htecgroup.com> + * + * SPDX-License-Identifier: GPL-2.0-or-later + */ + +#include <sys/time.h> +#include <stdint.h> + +#include "../../../../include/wrappers_mips64r6.h" +#include "../../../../include/test_inputs_64.h" +#include "../../../../include/test_utils_64.h" + +#define TEST_COUNT_TOTAL (PATTERN_INPUTS_64_COUNT + RANDOM_INPUTS_64_COUNT) + +int32_t main(void) +{ + char *isa_ase_name = "mips64r6"; + char *group_name = "CRC with reversed polynomial 0x82F63B78"; + char *instruction_name = "CRC32CB"; + int32_t ret; + uint32_t i, j; + struct timeval start, end; + double elapsed_time; + + uint64_t b64_result[TEST_COUNT_TOTAL]; + uint64_t b64_expect[TEST_COUNT_TOTAL] = { + 0x0000000000ffffffULL, /* 0 */ + 0xffffffffad7d5351ULL, + 0x00000000647e6465ULL, + 0xffffffffc9fcc8cbULL, + 0x00000000237f7689ULL, + 0xffffffff8efdda27ULL, + 0xffffffff837defedULL, + 0x000000002eff4343ULL, + 0xffffffffad82acaeULL, /* 8 */ + 0x0000000000000000ULL, + 0xffffffffc9033734ULL, + 0x0000000064819b9aULL, + 0xffffffff8e0225d8ULL, + 0x0000000023808976ULL, + 0x000000002e00bcbcULL, + 0xffffffff83821012ULL, + 0x00000000642b3130ULL, /* 16 */ + 0xffffffffc9a99d9eULL, + 0x0000000000aaaaaaULL, + 0xffffffffad280604ULL, + 0x0000000047abb846ULL, + 0xffffffffea2914e8ULL, + 0xffffffffe7a92122ULL, + 0x000000004a2b8d8cULL, + 0xffffffffc9566261ULL, /* 24 */ + 0x0000000064d4cecfULL, + 0xffffffffadd7f9fbULL, + 0x0000000000555555ULL, + 0xffffffffead6eb17ULL, + 0x00000000475447b9ULL, + 0x000000004ad47273ULL, + 0xffffffffe756deddULL, + 0x00000000234c45baULL, /* 32 */ + 0xffffffff8ecee914ULL, + 0x0000000047cdde20ULL, + 0xffffffffea4f728eULL, + 0x0000000000ccccccULL, + 0xffffffffad4e6062ULL, + 0xffffffffa0ce55a8ULL, + 0x000000000d4cf906ULL, + 0xffffffff8e3116ebULL, /* 40 */ + 0x0000000023b3ba45ULL, + 0xffffffffeab08d71ULL, + 0x00000000473221dfULL, + 0xffffffffadb19f9dULL, + 0x0000000000333333ULL, + 0x000000000db306f9ULL, + 0xffffffffa031aa57ULL, + 0xffffffff830c28f1ULL, /* 48 */ + 0x000000002e8e845fULL, + 0xffffffffe78db36bULL, + 0x000000004a0f1fc5ULL, + 0xffffffffa08ca187ULL, + 0x000000000d0e0d29ULL, + 0x00000000008e38e3ULL, + 0xffffffffad0c944dULL, + 0x000000002e717ba0ULL, /* 56 */ + 0xffffffff83f3d70eULL, + 0x000000004af0e03aULL, + 0xffffffffe7724c94ULL, + 0x000000000df1f2d6ULL, + 0xffffffffa0735e78ULL, + 0xffffffffadf36bb2ULL, + 0x000000000071c71cULL, + 0x0000000000286255ULL, /* 64 */ + 0xffffffffcbefd6b4ULL, + 0xffffffffc334e94fULL, + 0xffffffffac268ec5ULL, + 0xffffffffcb8a2726ULL, + 0x00000000004d93c7ULL, + 0x000000000896ac3cULL, + 0x000000006784cbb6ULL, + 0xffffffffc3a54491ULL, /* 72 */ + 0x000000000862f070ULL, + 0x0000000000b9cf8bULL, + 0x000000006faba801ULL, + 0xffffffffac50dd72ULL, + 0x0000000067976993ULL, + 0x000000006f4c5668ULL, + 0x00000000005e31e2ULL, + }; + + gettimeofday(&start, NULL); + + for (i = 0; i < PATTERN_INPUTS_64_SHORT_COUNT; i++) { + for (j = 0; j < PATTERN_INPUTS_64_SHORT_COUNT; j++) { + do_mips64r6_CRC32CB(b64_pattern + i, b64_pattern + j, + b64_result + (PATTERN_INPUTS_64_SHORT_COUNT * i + j)); + } + } + + for (i = 0; i < RANDOM_INPUTS_64_SHORT_COUNT; i++) { + for (j = 0; j < RANDOM_INPUTS_64_SHORT_COUNT; j++) { + do_mips64r6_CRC32CB(b64_random + i, b64_random + j, + b64_result + (((PATTERN_INPUTS_64_SHORT_COUNT) * + (PATTERN_INPUTS_64_SHORT_COUNT)) + + RANDOM_INPUTS_64_SHORT_COUNT * i + j)); + } + } + + gettimeofday(&end, NULL); + + elapsed_time = (end.tv_sec - start.tv_sec) * 1000.0; + elapsed_time += (end.tv_usec - start.tv_usec) / 1000.0; + + ret = check_results_64(isa_ase_name, group_name, instruction_name, + TEST_COUNT_TOTAL, elapsed_time, b64_result, + b64_expect); + + return ret; +} diff --git a/tests/tcg/mips/user/isa/mips64r6/crc/test_mips64r6_crc32cd.c b/tests/tcg/mips/user/isa/mips64r6/crc/test_mips64r6_crc32cd.c new file mode 100644 index 0000000..bf258e0 --- /dev/null +++ b/tests/tcg/mips/user/isa/mips64r6/crc/test_mips64r6_crc32cd.c @@ -0,0 +1,142 @@ +/* + * Test program for MIPS64R6 instruction CRC32CD + * + * Copyright (C) 2019 Wave Computing, Inc. + * Copyright (C) 2019 Aleksandar Markovic <amarkovic@wavecomp.com> + * Copyright (C) 2025 Aleksandar Rakic <aleksandar.rakic@htecgroup.com> + * + * SPDX-License-Identifier: GPL-2.0-or-later + */ + +#include <sys/time.h> +#include <stdint.h> + +#include "../../../../include/wrappers_mips64r6.h" +#include "../../../../include/test_inputs_64.h" +#include "../../../../include/test_utils_64.h" + +#define TEST_COUNT_TOTAL (PATTERN_INPUTS_64_COUNT + RANDOM_INPUTS_64_COUNT) + +int32_t main(void) +{ + char *isa_ase_name = "mips64r6"; + char *group_name = "CRC with reversed polynomial 0x82F63B78"; + char *instruction_name = "CRC32CD"; + int32_t ret; + uint32_t i, j; + struct timeval start, end; + double elapsed_time; + + uint64_t b64_result[TEST_COUNT_TOTAL]; + uint64_t b64_expect[TEST_COUNT_TOTAL] = { + 0xffffffffb798b438ULL, /* 0 */ + 0xffffffffc44ff94dULL, + 0xffffffff992a70ebULL, + 0xffffffffeafd3d9eULL, + 0x000000005152da26ULL, + 0x0000000022859753ULL, + 0x0000000015cb6d32ULL, + 0x00000000661c2047ULL, + 0x0000000073d74d75ULL, /* 8 */ + 0x0000000000000000ULL, + 0x000000005d6589a6ULL, + 0x000000002eb2c4d3ULL, + 0xffffffff951d236bULL, + 0xffffffffe6ca6e1eULL, + 0xffffffffd184947fULL, + 0xffffffffa253d90aULL, + 0x0000000008f9ceacULL, /* 16 */ + 0x000000007b2e83d9ULL, + 0x00000000264b0a7fULL, + 0x00000000559c470aULL, + 0xffffffffee33a0b2ULL, + 0xffffffff9de4edc7ULL, + 0xffffffffaaaa17a6ULL, + 0xffffffffd97d5ad3ULL, + 0xffffffffccb637e1ULL, /* 24 */ + 0xffffffffbf617a94ULL, + 0xffffffffe204f332ULL, + 0xffffffff91d3be47ULL, + 0x000000002a7c59ffULL, + 0x0000000059ab148aULL, + 0x000000006ee5eeebULL, + 0x000000001d32a39eULL, + 0x0000000021e3b01bULL, /* 32 */ + 0x000000005234fd6eULL, + 0x000000000f5174c8ULL, + 0x000000007c8639bdULL, + 0xffffffffc729de05ULL, + 0xffffffffb4fe9370ULL, + 0xffffffff83b06911ULL, + 0xfffffffff0672464ULL, + 0xffffffffe5ac4956ULL, /* 40 */ + 0xffffffff967b0423ULL, + 0xffffffffcb1e8d85ULL, + 0xffffffffb8c9c0f0ULL, + 0x0000000003662748ULL, + 0x0000000070b16a3dULL, + 0x0000000047ff905cULL, + 0x000000003428dd29ULL, + 0xffffffffb89d59a6ULL, /* 48 */ + 0xffffffffcb4a14d3ULL, + 0xffffffff962f9d75ULL, + 0xffffffffe5f8d000ULL, + 0x000000005e5737b8ULL, + 0x000000002d807acdULL, + 0x000000001ace80acULL, + 0x000000006919cdd9ULL, + 0x000000007cd2a0ebULL, /* 56 */ + 0x000000000f05ed9eULL, + 0x0000000052606438ULL, + 0x0000000021b7294dULL, + 0xffffffff9a18cef5ULL, + 0xffffffffe9cf8380ULL, + 0xffffffffde8179e1ULL, + 0xffffffffad563494ULL, + 0x000000003a358bb3ULL, /* 64 */ + 0xffffffff975446ebULL, + 0x0000000041d37ad6ULL, + 0x000000004be84fe1ULL, + 0xffffffff9671b1b3ULL, + 0x000000003b107cebULL, + 0xffffffffed9740d6ULL, + 0xffffffffe7ac75e1ULL, + 0xffffffffa1489696ULL, /* 72 */ + 0x000000000c295bceULL, + 0xffffffffdaae67f3ULL, + 0xffffffffd09552c4ULL, + 0x0000000042bd7071ULL, + 0xffffffffefdcbd29ULL, + 0x00000000395b8114ULL, + 0x000000003360b423ULL, + }; + + gettimeofday(&start, NULL); + + for (i = 0; i < PATTERN_INPUTS_64_SHORT_COUNT; i++) { + for (j = 0; j < PATTERN_INPUTS_64_SHORT_COUNT; j++) { + do_mips64r6_CRC32CD(b64_pattern + i, b64_pattern + j, + b64_result + (PATTERN_INPUTS_64_SHORT_COUNT * i + j)); + } + } + + for (i = 0; i < RANDOM_INPUTS_64_SHORT_COUNT; i++) { + for (j = 0; j < RANDOM_INPUTS_64_SHORT_COUNT; j++) { + do_mips64r6_CRC32CD(b64_random + i, b64_random + j, + b64_result + (((PATTERN_INPUTS_64_SHORT_COUNT) * + (PATTERN_INPUTS_64_SHORT_COUNT)) + + RANDOM_INPUTS_64_SHORT_COUNT * i + j)); + } + } + + gettimeofday(&end, NULL); + + elapsed_time = (end.tv_sec - start.tv_sec) * 1000.0; + elapsed_time += (end.tv_usec - start.tv_usec) / 1000.0; + + ret = check_results_64(isa_ase_name, group_name, instruction_name, + TEST_COUNT_TOTAL, elapsed_time, b64_result, + b64_expect); + + return ret; +} diff --git a/tests/tcg/mips/user/isa/mips64r6/crc/test_mips64r6_crc32ch.c b/tests/tcg/mips/user/isa/mips64r6/crc/test_mips64r6_crc32ch.c new file mode 100644 index 0000000..0e7b677 --- /dev/null +++ b/tests/tcg/mips/user/isa/mips64r6/crc/test_mips64r6_crc32ch.c @@ -0,0 +1,142 @@ +/* + * Test program for MIPS64R6 instruction CRC32CH + * + * Copyright (C) 2019 Wave Computing, Inc. + * Copyright (C) 2019 Aleksandar Markovic <amarkovic@wavecomp.com> + * Copyright (C) 2025 Aleksandar Rakic <aleksandar.rakic@htecgroup.com> + * + * SPDX-License-Identifier: GPL-2.0-or-later + */ + +#include <sys/time.h> +#include <stdint.h> + +#include "../../../../include/wrappers_mips64r6.h" +#include "../../../../include/test_inputs_64.h" +#include "../../../../include/test_utils_64.h" + +#define TEST_COUNT_TOTAL (PATTERN_INPUTS_64_COUNT + RANDOM_INPUTS_64_COUNT) + +int32_t main(void) +{ + char *isa_ase_name = "mips64r6"; + char *group_name = "CRC with reversed polynomial 0x82F63B78"; + char *instruction_name = "CRC32CH"; + int32_t ret; + uint32_t i, j; + struct timeval start, end; + double elapsed_time; + + uint64_t b64_result[TEST_COUNT_TOTAL]; + uint64_t b64_expect[TEST_COUNT_TOTAL] = { + 0x000000000000ffffULL, /* 0 */ + 0x000000000e9e77d2ULL, + 0xfffffffff92eaa4bULL, + 0xfffffffff7b02266ULL, + 0x00000000571acc93ULL, + 0x00000000598444beULL, + 0xfffffffff1e6ca77ULL, + 0xffffffffff78425aULL, + 0x000000000e9e882dULL, /* 8 */ + 0x0000000000000000ULL, + 0xfffffffff7b0dd99ULL, + 0xfffffffff92e55b4ULL, + 0x000000005984bb41ULL, + 0x00000000571a336cULL, + 0xffffffffff78bda5ULL, + 0xfffffffff1e63588ULL, + 0xfffffffff92eff1eULL, /* 16 */ + 0xfffffffff7b07733ULL, + 0x000000000000aaaaULL, + 0x000000000e9e2287ULL, + 0xffffffffae34cc72ULL, + 0xffffffffa0aa445fULL, + 0x0000000008c8ca96ULL, + 0x00000000065642bbULL, + 0xfffffffff7b088ccULL, /* 24 */ + 0xfffffffff92e00e1ULL, + 0x000000000e9edd78ULL, + 0x0000000000005555ULL, + 0xffffffffa0aabba0ULL, + 0xffffffffae34338dULL, + 0x000000000656bd44ULL, + 0x0000000008c83569ULL, + 0x00000000571affa0ULL, /* 32 */ + 0x000000005984778dULL, + 0xffffffffae34aa14ULL, + 0xffffffffa0aa2239ULL, + 0x000000000000ccccULL, + 0x000000000e9e44e1ULL, + 0xffffffffa6fcca28ULL, + 0xffffffffa8624205ULL, + 0x0000000059848872ULL, /* 40 */ + 0x00000000571a005fULL, + 0xffffffffa0aaddc6ULL, + 0xffffffffae3455ebULL, + 0x000000000e9ebb1eULL, + 0x0000000000003333ULL, + 0xffffffffa862bdfaULL, + 0xffffffffa6fc35d7ULL, + 0xfffffffff1e6bbb0ULL, /* 48 */ + 0xffffffffff78339dULL, + 0x0000000008c8ee04ULL, + 0x0000000006566629ULL, + 0xffffffffa6fc88dcULL, + 0xffffffffa86200f1ULL, + 0x0000000000008e38ULL, + 0x000000000e9e0615ULL, + 0xffffffffff78cc62ULL, /* 56 */ + 0xfffffffff1e6444fULL, + 0x00000000065699d6ULL, + 0x0000000008c811fbULL, + 0xffffffffa862ff0eULL, + 0xffffffffa6fc7723ULL, + 0x000000000e9ef9eaULL, + 0x00000000000071c7ULL, + 0x0000000000002862ULL, /* 64 */ + 0x000000001190c4cfULL, + 0x000000007b7fdbbeULL, + 0xffffffff9204da99ULL, + 0x000000001190a13eULL, + 0x0000000000004d93ULL, + 0x000000006aef52e2ULL, + 0xffffffff839453c5ULL, + 0x000000007b7f4a13ULL, /* 72 */ + 0x000000006aefa6beULL, + 0x000000000000b9cfULL, + 0xffffffffe97bb8e8ULL, + 0xffffffff9204accaULL, + 0xffffffff83944067ULL, + 0xffffffffe97b5f16ULL, + 0x0000000000005e31ULL, + }; + + gettimeofday(&start, NULL); + + for (i = 0; i < PATTERN_INPUTS_64_SHORT_COUNT; i++) { + for (j = 0; j < PATTERN_INPUTS_64_SHORT_COUNT; j++) { + do_mips64r6_CRC32CH(b64_pattern + i, b64_pattern + j, + b64_result + (PATTERN_INPUTS_64_SHORT_COUNT * i + j)); + } + } + + for (i = 0; i < RANDOM_INPUTS_64_SHORT_COUNT; i++) { + for (j = 0; j < RANDOM_INPUTS_64_SHORT_COUNT; j++) { + do_mips64r6_CRC32CH(b64_random + i, b64_random + j, + b64_result + (((PATTERN_INPUTS_64_SHORT_COUNT) * + (PATTERN_INPUTS_64_SHORT_COUNT)) + + RANDOM_INPUTS_64_SHORT_COUNT * i + j)); + } + } + + gettimeofday(&end, NULL); + + elapsed_time = (end.tv_sec - start.tv_sec) * 1000.0; + elapsed_time += (end.tv_usec - start.tv_usec) / 1000.0; + + ret = check_results_64(isa_ase_name, group_name, instruction_name, + TEST_COUNT_TOTAL, elapsed_time, b64_result, + b64_expect); + + return ret; +} diff --git a/tests/tcg/mips/user/isa/mips64r6/crc/test_mips64r6_crc32cw.c b/tests/tcg/mips/user/isa/mips64r6/crc/test_mips64r6_crc32cw.c new file mode 100644 index 0000000..f7110b3 --- /dev/null +++ b/tests/tcg/mips/user/isa/mips64r6/crc/test_mips64r6_crc32cw.c @@ -0,0 +1,142 @@ +/* + * Test program for MIPS64R6 instruction CRC32CW + * + * Copyright (C) 2019 Wave Computing, Inc. + * Copyright (C) 2019 Aleksandar Markovic <amarkovic@wavecomp.com> + * Copyright (C) 2025 Aleksandar Rakic <aleksandar.rakic@htecgroup.com> + * + * SPDX-License-Identifier: GPL-2.0-or-later + */ + +#include <sys/time.h> +#include <stdint.h> + +#include "../../../../include/wrappers_mips64r6.h" +#include "../../../../include/test_inputs_64.h" +#include "../../../../include/test_utils_64.h" + +#define TEST_COUNT_TOTAL (PATTERN_INPUTS_64_COUNT + RANDOM_INPUTS_64_COUNT) + +int32_t main(void) +{ + char *isa_ase_name = "mips64r6"; + char *group_name = "CRC with reversed polynomial 0x82F63B78"; + char *instruction_name = "CRC32CW"; + int32_t ret; + uint32_t i, j; + struct timeval start, end; + double elapsed_time; + + uint64_t b64_result[TEST_COUNT_TOTAL]; + uint64_t b64_expect[TEST_COUNT_TOTAL] = { + 0x0000000000000000ULL, /* 0 */ + 0xffffffffb798b438ULL, + 0xffffffff91d3be47ULL, + 0x00000000264b0a7fULL, + 0x0000000070b16a3dULL, + 0xffffffffc729de05ULL, + 0x0000000063c5950aULL, + 0xffffffffd45d2132ULL, + 0xffffffffb798b438ULL, /* 8 */ + 0x0000000000000000ULL, + 0x00000000264b0a7fULL, + 0xffffffff91d3be47ULL, + 0xffffffffc729de05ULL, + 0x0000000070b16a3dULL, + 0xffffffffd45d2132ULL, + 0x0000000063c5950aULL, + 0xffffffff91d3be47ULL, /* 16 */ + 0x00000000264b0a7fULL, + 0x0000000000000000ULL, + 0xffffffffb798b438ULL, + 0xffffffffe162d47aULL, + 0x0000000056fa6042ULL, + 0xfffffffff2162b4dULL, + 0x00000000458e9f75ULL, + 0x00000000264b0a7fULL, /* 24 */ + 0xffffffff91d3be47ULL, + 0xffffffffb798b438ULL, + 0x0000000000000000ULL, + 0x0000000056fa6042ULL, + 0xffffffffe162d47aULL, + 0x00000000458e9f75ULL, + 0xfffffffff2162b4dULL, + 0x0000000070b16a3dULL, /* 32 */ + 0xffffffffc729de05ULL, + 0xffffffffe162d47aULL, + 0x0000000056fa6042ULL, + 0x0000000000000000ULL, + 0xffffffffb798b438ULL, + 0x000000001374ff37ULL, + 0xffffffffa4ec4b0fULL, + 0xffffffffc729de05ULL, /* 40 */ + 0x0000000070b16a3dULL, + 0x0000000056fa6042ULL, + 0xffffffffe162d47aULL, + 0xffffffffb798b438ULL, + 0x0000000000000000ULL, + 0xffffffffa4ec4b0fULL, + 0x000000001374ff37ULL, + 0x0000000063c5950aULL, /* 48 */ + 0xffffffffd45d2132ULL, + 0xfffffffff2162b4dULL, + 0x00000000458e9f75ULL, + 0x000000001374ff37ULL, + 0xffffffffa4ec4b0fULL, + 0x0000000000000000ULL, + 0xffffffffb798b438ULL, + 0xffffffffd45d2132ULL, /* 56 */ + 0x0000000063c5950aULL, + 0x00000000458e9f75ULL, + 0xfffffffff2162b4dULL, + 0xffffffffa4ec4b0fULL, + 0x000000001374ff37ULL, + 0xffffffffb798b438ULL, + 0x0000000000000000ULL, + 0x0000000000000000ULL, /* 64 */ + 0xffffffffea0755b2ULL, + 0x0000000008b188e6ULL, + 0xffffffffff3cc8d9ULL, + 0xffffffffea0755b2ULL, + 0x0000000000000000ULL, + 0xffffffffe2b6dd54ULL, + 0x00000000153b9d6bULL, + 0x0000000008b188e6ULL, /* 72 */ + 0xffffffffe2b6dd54ULL, + 0x0000000000000000ULL, + 0xfffffffff78d403fULL, + 0xffffffffff3cc8d9ULL, + 0x00000000153b9d6bULL, + 0xfffffffff78d403fULL, + 0x0000000000000000ULL, + }; + + gettimeofday(&start, NULL); + + for (i = 0; i < PATTERN_INPUTS_64_SHORT_COUNT; i++) { + for (j = 0; j < PATTERN_INPUTS_64_SHORT_COUNT; j++) { + do_mips64r6_CRC32CW(b64_pattern + i, b64_pattern + j, + b64_result + (PATTERN_INPUTS_64_SHORT_COUNT * i + j)); + } + } + + for (i = 0; i < RANDOM_INPUTS_64_SHORT_COUNT; i++) { + for (j = 0; j < RANDOM_INPUTS_64_SHORT_COUNT; j++) { + do_mips64r6_CRC32CW(b64_random + i, b64_random + j, + b64_result + (((PATTERN_INPUTS_64_SHORT_COUNT) * + (PATTERN_INPUTS_64_SHORT_COUNT)) + + RANDOM_INPUTS_64_SHORT_COUNT * i + j)); + } + } + + gettimeofday(&end, NULL); + + elapsed_time = (end.tv_sec - start.tv_sec) * 1000.0; + elapsed_time += (end.tv_usec - start.tv_usec) / 1000.0; + + ret = check_results_64(isa_ase_name, group_name, instruction_name, + TEST_COUNT_TOTAL, elapsed_time, b64_result, + b64_expect); + + return ret; +} diff --git a/tests/tcg/mips/user/isa/mips64r6/crc/test_mips64r6_crc32d.c b/tests/tcg/mips/user/isa/mips64r6/crc/test_mips64r6_crc32d.c new file mode 100644 index 0000000..e391be8 --- /dev/null +++ b/tests/tcg/mips/user/isa/mips64r6/crc/test_mips64r6_crc32d.c @@ -0,0 +1,142 @@ +/* + * Test program for MIPS64R6 instruction CRC32D + * + * Copyright (C) 2019 Wave Computing, Inc. + * Copyright (C) 2019 Aleksandar Markovic <amarkovic@wavecomp.com> + * Copyright (C) 2025 Aleksandar Rakic <aleksandar.rakic@htecgroup.com> + * + * SPDX-License-Identifier: GPL-2.0-or-later + */ + +#include <sys/time.h> +#include <stdint.h> + +#include "../../../../include/wrappers_mips64r6.h" +#include "../../../../include/test_inputs_64.h" +#include "../../../../include/test_utils_64.h" + +#define TEST_COUNT_TOTAL (PATTERN_INPUTS_64_COUNT + RANDOM_INPUTS_64_COUNT) + +int32_t main(void) +{ + char *isa_ase_name = "mips64r6"; + char *group_name = "CRC with reversed polynomial 0xEDB88320"; + char *instruction_name = "CRC32D"; + int32_t ret; + uint32_t i, j; + struct timeval start, end; + double elapsed_time; + + uint64_t b64_result[TEST_COUNT_TOTAL]; + uint64_t b64_expect[TEST_COUNT_TOTAL] = { + 0xffffffffdebb20e3ULL, /* 0 */ + 0x0000000044660075ULL, + 0x000000001e20c2aeULL, + 0xffffffff84fde238ULL, + 0x00000000281d7ce7ULL, + 0xffffffffb2c05c71ULL, + 0xffffffffd660a024ULL, + 0x000000004cbd80b2ULL, + 0xffffffff9add2096ULL, /* 8 */ + 0x0000000000000000ULL, + 0x000000005a46c2dbULL, + 0xffffffffc09be24dULL, + 0x000000006c7b7c92ULL, + 0xfffffffff6a65c04ULL, + 0xffffffff9206a051ULL, + 0x0000000008db80c7ULL, + 0x000000005449dd0fULL, /* 16 */ + 0xffffffffce94fd99ULL, + 0xffffffff94d23f42ULL, + 0x000000000e0f1fd4ULL, + 0xffffffffa2ef810bULL, + 0x000000003832a19dULL, + 0x000000005c925dc8ULL, + 0xffffffffc64f7d5eULL, + 0x00000000102fdd7aULL, /* 24 */ + 0xffffffff8af2fdecULL, + 0xffffffffd0b43f37ULL, + 0x000000004a691fa1ULL, + 0xffffffffe689817eULL, + 0x000000007c54a1e8ULL, + 0x0000000018f45dbdULL, + 0xffffffff82297d2bULL, + 0xffffffffa7157447ULL, /* 32 */ + 0x000000003dc854d1ULL, + 0x00000000678e960aULL, + 0xfffffffffd53b69cULL, + 0x0000000051b32843ULL, + 0xffffffffcb6e08d5ULL, + 0xffffffffafcef480ULL, + 0x000000003513d416ULL, + 0xffffffffe3737432ULL, /* 40 */ + 0x0000000079ae54a4ULL, + 0x0000000023e8967fULL, + 0xffffffffb935b6e9ULL, + 0x0000000015d52836ULL, + 0xffffffff8f0808a0ULL, + 0xffffffffeba8f4f5ULL, + 0x000000007175d463ULL, + 0x000000007a6adc3eULL, /* 48 */ + 0xffffffffe0b7fca8ULL, + 0xffffffffbaf13e73ULL, + 0x00000000202c1ee5ULL, + 0xffffffff8ccc803aULL, + 0x000000001611a0acULL, + 0x0000000072b15cf9ULL, + 0xffffffffe86c7c6fULL, + 0x000000003e0cdc4bULL, /* 56 */ + 0xffffffffa4d1fcddULL, + 0xfffffffffe973e06ULL, + 0x00000000644a1e90ULL, + 0xffffffffc8aa804fULL, + 0x000000005277a0d9ULL, + 0x0000000036d75c8cULL, + 0xffffffffac0a7c1aULL, + 0xffffffffed857593ULL, /* 64 */ + 0xffffffffe0b6f95fULL, + 0x00000000253b462cULL, + 0xffffffffe15579b9ULL, + 0x0000000074897c83ULL, + 0x0000000079baf04fULL, + 0xffffffffbc374f3cULL, + 0x00000000785970a9ULL, + 0xffffffffa6bae0a9ULL, /* 72 */ + 0xffffffffab896c65ULL, + 0x000000006e04d316ULL, + 0xffffffffaa6aec83ULL, + 0x000000005ae171feULL, + 0x0000000057d2fd32ULL, + 0xffffffff925f4241ULL, + 0x0000000056317dd4ULL, + }; + + gettimeofday(&start, NULL); + + for (i = 0; i < PATTERN_INPUTS_64_SHORT_COUNT; i++) { + for (j = 0; j < PATTERN_INPUTS_64_SHORT_COUNT; j++) { + do_mips64r6_CRC32D(b64_pattern + i, b64_pattern + j, + b64_result + (PATTERN_INPUTS_64_SHORT_COUNT * i + j)); + } + } + + for (i = 0; i < RANDOM_INPUTS_64_SHORT_COUNT; i++) { + for (j = 0; j < RANDOM_INPUTS_64_SHORT_COUNT; j++) { + do_mips64r6_CRC32D(b64_random + i, b64_random + j, + b64_result + (((PATTERN_INPUTS_64_SHORT_COUNT) * + (PATTERN_INPUTS_64_SHORT_COUNT)) + + RANDOM_INPUTS_64_SHORT_COUNT * i + j)); + } + } + + gettimeofday(&end, NULL); + + elapsed_time = (end.tv_sec - start.tv_sec) * 1000.0; + elapsed_time += (end.tv_usec - start.tv_usec) / 1000.0; + + ret = check_results_64(isa_ase_name, group_name, instruction_name, + TEST_COUNT_TOTAL, elapsed_time, b64_result, + b64_expect); + + return ret; +} diff --git a/tests/tcg/mips/user/isa/mips64r6/crc/test_mips64r6_crc32h.c b/tests/tcg/mips/user/isa/mips64r6/crc/test_mips64r6_crc32h.c new file mode 100644 index 0000000..100f02c --- /dev/null +++ b/tests/tcg/mips/user/isa/mips64r6/crc/test_mips64r6_crc32h.c @@ -0,0 +1,142 @@ +/* + * Test program for MIPS64R6 instruction CRC32H + * + * Copyright (C) 2019 Wave Computing, Inc. + * Copyright (C) 2019 Aleksandar Markovic <amarkovic@wavecomp.com> + * Copyright (C) 2025 Aleksandar Rakic <aleksandar.rakic@htecgroup.com> + * + * SPDX-License-Identifier: GPL-2.0-or-later + */ + +#include <sys/time.h> +#include <stdint.h> + +#include "../../../../include/wrappers_mips64r6.h" +#include "../../../../include/test_inputs_64.h" +#include "../../../../include/test_utils_64.h" + +#define TEST_COUNT_TOTAL (PATTERN_INPUTS_64_COUNT + RANDOM_INPUTS_64_COUNT) + +int32_t main(void) +{ + char *isa_ase_name = "mips64r6"; + char *group_name = "CRC with reversed polynomial 0xEDB88320"; + char *instruction_name = "CRC32H"; + int32_t ret; + uint32_t i, j; + struct timeval start, end; + double elapsed_time; + + uint64_t b64_result[TEST_COUNT_TOTAL]; + uint64_t b64_expect[TEST_COUNT_TOTAL] = { + 0x000000000000ffffULL, /* 0 */ + 0xffffffffbe2612ffULL, + 0xffffffffdccda6c0ULL, + 0x0000000062eb4bc0ULL, + 0x000000004bbbc8eaULL, + 0xfffffffff59d25eaULL, + 0x0000000022259ac0ULL, + 0xffffffff9c0377c0ULL, + 0xffffffffbe26ed00ULL, /* 8 */ + 0x0000000000000000ULL, + 0x0000000062ebb43fULL, + 0xffffffffdccd593fULL, + 0xfffffffff59dda15ULL, + 0x000000004bbb3715ULL, + 0xffffffff9c03883fULL, + 0x000000002225653fULL, + 0xffffffffdccdf395ULL, /* 16 */ + 0x0000000062eb1e95ULL, + 0x000000000000aaaaULL, + 0xffffffffbe2647aaULL, + 0xffffffff9776c480ULL, + 0x0000000029502980ULL, + 0xfffffffffee896aaULL, + 0x0000000040ce7baaULL, + 0x0000000062ebe16aULL, /* 24 */ + 0xffffffffdccd0c6aULL, + 0xffffffffbe26b855ULL, + 0x0000000000005555ULL, + 0x000000002950d67fULL, + 0xffffffff97763b7fULL, + 0x0000000040ce8455ULL, + 0xfffffffffee86955ULL, + 0x000000004bbbfbd9ULL, /* 32 */ + 0xfffffffff59d16d9ULL, + 0xffffffff9776a2e6ULL, + 0x0000000029504fe6ULL, + 0x000000000000ccccULL, + 0xffffffffbe2621ccULL, + 0x00000000699e9ee6ULL, + 0xffffffffd7b873e6ULL, + 0xfffffffff59de926ULL, /* 40 */ + 0x000000004bbb0426ULL, + 0x000000002950b019ULL, + 0xffffffff97765d19ULL, + 0xffffffffbe26de33ULL, + 0x0000000000003333ULL, + 0xffffffffd7b88c19ULL, + 0x00000000699e6119ULL, + 0x000000002225eb07ULL, /* 48 */ + 0xffffffff9c030607ULL, + 0xfffffffffee8b238ULL, + 0x0000000040ce5f38ULL, + 0x00000000699edc12ULL, + 0xffffffffd7b83112ULL, + 0x0000000000008e38ULL, + 0xffffffffbe266338ULL, + 0xffffffff9c03f9f8ULL, /* 56 */ + 0x00000000222514f8ULL, + 0x0000000040cea0c7ULL, + 0xfffffffffee84dc7ULL, + 0xffffffffd7b8ceedULL, + 0x00000000699e23edULL, + 0xffffffffbe269cc7ULL, + 0x00000000000071c7ULL, + 0x0000000000002862ULL, /* 64 */ + 0x0000000026a17af6ULL, + 0xffffffffaa919152ULL, + 0xffffffffcb865590ULL, + 0x0000000026a11f07ULL, + 0x0000000000004d93ULL, + 0xffffffff8c30a637ULL, + 0xffffffffed2762f5ULL, + 0xffffffffaa9100ffULL, /* 72 */ + 0xffffffff8c30526bULL, + 0x000000000000b9cfULL, + 0x0000000061177d0dULL, + 0xffffffffcb8623c3ULL, + 0xffffffffed277157ULL, + 0x0000000061179af3ULL, + 0x0000000000005e31ULL + }; + + gettimeofday(&start, NULL); + + for (i = 0; i < PATTERN_INPUTS_64_SHORT_COUNT; i++) { + for (j = 0; j < PATTERN_INPUTS_64_SHORT_COUNT; j++) { + do_mips64r6_CRC32H(b64_pattern + i, b64_pattern + j, + b64_result + (PATTERN_INPUTS_64_SHORT_COUNT * i + j)); + } + } + + for (i = 0; i < RANDOM_INPUTS_64_SHORT_COUNT; i++) { + for (j = 0; j < RANDOM_INPUTS_64_SHORT_COUNT; j++) { + do_mips64r6_CRC32H(b64_random + i, b64_random + j, + b64_result + (((PATTERN_INPUTS_64_SHORT_COUNT) * + (PATTERN_INPUTS_64_SHORT_COUNT)) + + RANDOM_INPUTS_64_SHORT_COUNT * i + j)); + } + } + + gettimeofday(&end, NULL); + + elapsed_time = (end.tv_sec - start.tv_sec) * 1000.0; + elapsed_time += (end.tv_usec - start.tv_usec) / 1000.0; + + ret = check_results_64(isa_ase_name, group_name, instruction_name, + TEST_COUNT_TOTAL, elapsed_time, b64_result, + b64_expect); + + return ret; +} diff --git a/tests/tcg/mips/user/isa/mips64r6/crc/test_mips64r6_crc32w.c b/tests/tcg/mips/user/isa/mips64r6/crc/test_mips64r6_crc32w.c new file mode 100644 index 0000000..b4f5f4b --- /dev/null +++ b/tests/tcg/mips/user/isa/mips64r6/crc/test_mips64r6_crc32w.c @@ -0,0 +1,142 @@ +/* + * Test program for MIPS64R6 instruction CRC32W + * + * Copyright (C) 2019 Wave Computing, Inc. + * Copyright (C) 2019 Aleksandar Markovic <amarkovic@wavecomp.com> + * Copyright (C) 2025 Aleksandar Rakic <aleksandar.rakic@htecgroup.com> + * + * SPDX-License-Identifier: GPL-2.0-or-later + */ + +#include <sys/time.h> +#include <stdint.h> + +#include "../../../../include/wrappers_mips64r6.h" +#include "../../../../include/test_inputs_64.h" +#include "../../../../include/test_utils_64.h" + +#define TEST_COUNT_TOTAL (PATTERN_INPUTS_64_COUNT + RANDOM_INPUTS_64_COUNT) + +int32_t main(void) +{ + char *isa_ase_name = "mips64r6"; + char *group_name = "CRC with reversed polynomial 0xEDB88320"; + char *instruction_name = "CRC32W"; + int32_t ret; + uint32_t i, j; + struct timeval start, end; + double elapsed_time; + + uint64_t b64_result[TEST_COUNT_TOTAL]; + uint64_t b64_expect[TEST_COUNT_TOTAL] = { + 0x0000000000000000ULL, /* 0 */ + 0xffffffffdebb20e3ULL, + 0x000000004a691fa1ULL, + 0xffffffff94d23f42ULL, + 0xffffffff8f0808a0ULL, + 0x0000000051b32843ULL, + 0x0000000065069dceULL, + 0xffffffffbbbdbd2dULL, + 0xffffffffdebb20e3ULL, /* 8 */ + 0x0000000000000000ULL, + 0xffffffff94d23f42ULL, + 0x000000004a691fa1ULL, + 0x0000000051b32843ULL, + 0xffffffff8f0808a0ULL, + 0xffffffffbbbdbd2dULL, + 0x0000000065069dceULL, + 0x000000004a691fa1ULL, /* 16 */ + 0xffffffff94d23f42ULL, + 0x0000000000000000ULL, + 0xffffffffdebb20e3ULL, + 0xffffffffc5611701ULL, + 0x000000001bda37e2ULL, + 0x000000002f6f826fULL, + 0xfffffffff1d4a28cULL, + 0xffffffff94d23f42ULL, /* 24 */ + 0x000000004a691fa1ULL, + 0xffffffffdebb20e3ULL, + 0x0000000000000000ULL, + 0x000000001bda37e2ULL, + 0xffffffffc5611701ULL, + 0xfffffffff1d4a28cULL, + 0x000000002f6f826fULL, + 0xffffffff8f0808a0ULL, /* 32 */ + 0x0000000051b32843ULL, + 0xffffffffc5611701ULL, + 0x000000001bda37e2ULL, + 0x0000000000000000ULL, + 0xffffffffdebb20e3ULL, + 0xffffffffea0e956eULL, + 0x0000000034b5b58dULL, + 0x0000000051b32843ULL, /* 40 */ + 0xffffffff8f0808a0ULL, + 0x000000001bda37e2ULL, + 0xffffffffc5611701ULL, + 0xffffffffdebb20e3ULL, + 0x0000000000000000ULL, + 0x0000000034b5b58dULL, + 0xffffffffea0e956eULL, + 0x0000000065069dceULL, /* 48 */ + 0xffffffffbbbdbd2dULL, + 0x000000002f6f826fULL, + 0xfffffffff1d4a28cULL, + 0xffffffffea0e956eULL, + 0x0000000034b5b58dULL, + 0x0000000000000000ULL, + 0xffffffffdebb20e3ULL, + 0xffffffffbbbdbd2dULL, /* 56 */ + 0x0000000065069dceULL, + 0xfffffffff1d4a28cULL, + 0x000000002f6f826fULL, + 0x0000000034b5b58dULL, + 0xffffffffea0e956eULL, + 0xffffffffdebb20e3ULL, + 0x0000000000000000ULL, + 0x0000000000000000ULL, /* 64 */ + 0xffffffff90485967ULL, + 0x000000006dfb974aULL, + 0x00000000083e4538ULL, + 0xffffffff90485967ULL, + 0x0000000000000000ULL, + 0xfffffffffdb3ce2dULL, + 0xffffffff98761c5fULL, + 0x000000006dfb974aULL, /* 72 */ + 0xfffffffffdb3ce2dULL, + 0x0000000000000000ULL, + 0x0000000065c5d272ULL, + 0x00000000083e4538ULL, + 0xffffffff98761c5fULL, + 0x0000000065c5d272ULL, + 0x0000000000000000ULL, + }; + + gettimeofday(&start, NULL); + + for (i = 0; i < PATTERN_INPUTS_64_SHORT_COUNT; i++) { + for (j = 0; j < PATTERN_INPUTS_64_SHORT_COUNT; j++) { + do_mips64r6_CRC32W(b64_pattern + i, b64_pattern + j, + b64_result + (PATTERN_INPUTS_64_SHORT_COUNT * i + j)); + } + } + + for (i = 0; i < RANDOM_INPUTS_64_SHORT_COUNT; i++) { + for (j = 0; j < RANDOM_INPUTS_64_SHORT_COUNT; j++) { + do_mips64r6_CRC32W(b64_random + i, b64_random + j, + b64_result + (((PATTERN_INPUTS_64_SHORT_COUNT) * + (PATTERN_INPUTS_64_SHORT_COUNT)) + + RANDOM_INPUTS_64_SHORT_COUNT * i + j)); + } + } + + gettimeofday(&end, NULL); + + elapsed_time = (end.tv_sec - start.tv_sec) * 1000.0; + elapsed_time += (end.tv_usec - start.tv_usec) / 1000.0; + + ret = check_results_64(isa_ase_name, group_name, instruction_name, + TEST_COUNT_TOTAL, elapsed_time, b64_result, + b64_expect); + + return ret; +} diff --git a/tests/tcg/plugins/mem.c b/tests/tcg/plugins/mem.c index ca4e888..9649bce 100644 --- a/tests/tcg/plugins/mem.c +++ b/tests/tcg/plugins/mem.c @@ -20,6 +20,7 @@ * few things provided by compiler.h. */ #include <compiler.h> +#include <stdbool.h> #include <bswap.h> #include <qemu-plugin.h> diff --git a/tests/vm/freebsd b/tests/vm/freebsd index 74b3b1e..2e96c9e 100755 --- a/tests/vm/freebsd +++ b/tests/vm/freebsd @@ -28,8 +28,8 @@ class FreeBSDVM(basevm.BaseVM): name = "freebsd" arch = "x86_64" - link = "https://download.freebsd.org/releases/CI-IMAGES/14.1-RELEASE/amd64/Latest/FreeBSD-14.1-RELEASE-amd64-BASIC-CI.raw.xz" - csum = "202fe27a05427f0a86d3ebb97712745186f2776ccc4f70d95466dd99a0238ba5" + link = "https://download.freebsd.org/releases/CI-IMAGES/14.3-RELEASE/amd64/Latest/FreeBSD-14.3-RELEASE-amd64-BASIC-CI.raw.xz" + csum = "ec0f5a4bbe63aa50a725d9fee0f1931f850e9a21cbebdadb991df00f168d6805" size = "20G" BUILD_SCRIPT = """ @@ -558,3 +558,15 @@ void qemu_print_log_usage(FILE *f) fprintf(f, "\nUse \"-d trace:help\" to get a list of trace events.\n\n"); #endif } + +#ifdef CONFIG_HAVE_RUST +ssize_t rust_fwrite(const void *ptr, size_t size, size_t nmemb, FILE *stream) +{ + /* + * Same as fwrite, but return -errno because Rust libc does not provide + * portable access to errno. :( + */ + int ret = fwrite(ptr, size, nmemb, stream); + return ret < 0 ? -errno : 0; +} +#endif |