diff --git a/devicemodel/Makefile b/devicemodel/Makefile index 18dfab3d2..d4157ba69 100644 --- a/devicemodel/Makefile +++ b/devicemodel/Makefile @@ -96,6 +96,7 @@ SRCS += hw/platform/ioc.c SRCS += hw/platform/ioc_cbc.c SRCS += hw/platform/pty_vuart.c SRCS += hw/platform/acpi/acpi.c +SRCS += hw/platform/acpi/rtct.c SRCS += hw/platform/acpi/acpi_pm.c SRCS += hw/platform/rpmb/rpmb_sim.c SRCS += hw/platform/rpmb/rpmb_backend.c diff --git a/devicemodel/core/vmmapi.c b/devicemodel/core/vmmapi.c index 1509de3b7..c40bab9f4 100644 --- a/devicemodel/core/vmmapi.c +++ b/devicemodel/core/vmmapi.c @@ -179,6 +179,11 @@ int acrn_parse_cpu_affinity(char *opt) return 0; } +uint64_t vm_get_cpu_affinity_dm(void) +{ + return cpu_affinity_bitmap; +} + struct vmctx * vm_create(const char *name, uint64_t req_buf, int *vcpu_num) { diff --git a/devicemodel/hw/platform/acpi/acpi.c b/devicemodel/hw/platform/acpi/acpi.c index 67038a837..8a8f813f4 100644 --- a/devicemodel/hw/platform/acpi/acpi.c +++ b/devicemodel/hw/platform/acpi/acpi.c @@ -1092,14 +1092,13 @@ int create_and_inject_vrtct(struct vmctx *ctx) size_t native_rtct_len; size_t vrtct_len; uint8_t buf[RTCT_BUF_LEN] = {0}; + uint8_t *vrtct; struct vm_memmap memmap = { .type = VM_MMIO, .gpa = SOFTWARE_SRAM_BASE_GPA, - .hpa = SOFTWARE_SRAM_BASE_HPA, - /* TODO: .len should be software ram_size+32kb (32kb is for CRL binary). - *We also need to modify guest E820 to adapt to real config - */ - .len = SOFTWARE_SRAM_MAX_SIZE, + /* HPA base and size of Software SRAM shall be parsed from vRTCT. */ + .hpa = 0, + .len = 0, .prot = PROT_ALL }; @@ -1122,10 +1121,19 @@ int create_and_inject_vrtct(struct vmctx *ctx) } close(native_rtct_fd); - vrtct_len = native_rtct_len; + vrtct = build_vrtct(ctx, (void *)buf); + if (vrtct == NULL) + return -1; - memcpy(vm_map_gpa(ctx, ACPI_BASE + RTCT_OFFSET, vrtct_len), buf, vrtct_len); + vrtct_len = ((struct acpi_table_hdr *)vrtct)->length; + if (vrtct_len > RTCT_BUF_LEN) { + pr_err("Warning: Size of vRTCT (%d bytes) overflows, pls update DSDT_OFFSET.\n", vrtct_len); + } + memcpy(vm_map_gpa(ctx, ACPI_BASE + RTCT_OFFSET, vrtct_len), vrtct, vrtct_len); + free(vrtct); + memmap.hpa = get_software_sram_base_hpa(); + memmap.len = get_software_sram_size(); ioctl(ctx->fd, IC_UNSET_MEMSEG, &memmap); return ioctl(ctx->fd, IC_SET_MEMSEG, &memmap); }; diff --git a/devicemodel/hw/platform/acpi/rtct.c b/devicemodel/hw/platform/acpi/rtct.c new file mode 100644 index 000000000..99b88fb39 --- /dev/null +++ b/devicemodel/hw/platform/acpi/rtct.c @@ -0,0 +1,363 @@ +/* + * Copyright (C) 2021 Intel Corporation. All rights reserved. + * + * SPDX-License-Identifier: BSD-3-Clause + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "pci_core.h" +#include "vmmapi.h" +#include "acpi.h" +#include "log.h" +#include "rtct.h" + +#define RTCT_ENTRY_HEADER_SIZE 8 +#define RTCT_PSRAM_HEADER_SIZE (RTCT_ENTRY_HEADER_SIZE + 20) +#define RTCT_MEM_HI_HEADER_SIZE (RTCT_ENTRY_HEADER_SIZE + 8) + +#define BITMASK(nr) (1U << nr) + +#define foreach_rtct_entry(rtct, e) \ + for (e = (void *)rtct + sizeof(struct acpi_table_hdr); \ + ((uint64_t)e - (uint64_t)rtct) < rtct->length; \ + e = (struct rtct_entry *)((uint64_t)e + e->size)) + +static uint64_t software_sram_base_hpa; +static uint64_t software_sram_size; + +static uint8_t vrtct_checksum(uint8_t *vrtct, uint32_t length) +{ + uint8_t sum = 0; + uint32_t i; + + for (i = 0; i < length; i++) { + sum += vrtct[i]; + } + return -sum; +} + +static inline uint32_t lapicid_to_cpuid(uint32_t lapicid) +{ + /* hardcode for TGL & EHL platform */ + return (lapicid >> 1); +} + +static inline uint32_t vcpuid_to_vlapicid(uint32_t vcpuid) +{ + /* keep vlapic same with vcpuid */ + return vcpuid; +} + +/** + * @brief Build a vLAPIC ID table base on the pCPU bitmask of guest + * and pCPU bitmask of Software SRAM. + * + * @param sw_sram_pcpu_bitmask pCPU bitmask of Software SRAM region. + * @param guest_pcpu_bitmask pCUP bitmask of guest. + * @param vlapicid_tbl Pointer to buffer of vLAPIC ID table, caller shall + * guarantee it will not overflow. + * + * @return number of vlapic IDs filled to buffer. + */ +static int map_software_sram_vlapic_ids(uint64_t sw_sram_pcpu_bitmask, uint64_t guest_pcpu_bitmask, + uint32_t *vlapicid_tbl) +{ + uint32_t vcpuid = 0; + int i, vlapicid_num = 0; + int guest_pcpu_index_start = ffsl(guest_pcpu_bitmask) - 1; + int guest_pcpu_index_end = flsl(guest_pcpu_bitmask) - 1; + + for (i = guest_pcpu_index_start; i <= guest_pcpu_index_end; i++) { + if ((guest_pcpu_bitmask & BITMASK(i)) != 0) { + if ((sw_sram_pcpu_bitmask & BITMASK(i)) != 0) { + vlapicid_tbl[vlapicid_num++] = vcpuid_to_vlapicid(vcpuid); + } + vcpuid++; + } + } + return vlapicid_num; +} + +static inline struct rtct_entry *get_free_rtct_entry(struct acpi_table_hdr *rtct) +{ + return (struct rtct_entry *)((uint8_t *)rtct + rtct->length); +} + +static inline void add_rtct_entry(struct acpi_table_hdr *rtct, struct rtct_entry *e) +{ + rtct->length += e->size; +} + +/** + * @brief Add a new Software SRAM region entry to virtual RTCT. + * + * @param vrtct Pointer to virtual RTCT. + * @param cache_level Cache level of Software SRAM region. + * @param base Base address of Software SRAM region. + * @param ways Cache ways of Software SRAM region. + * @param size Size of Software SRAM region. + * @param sw_sram_pcpu_bitmask pCPU bitmask of Software SRAM region. + * @param guest_pcpu_bitmask pCUP bitmask of guest. + * + * @return 0 on success and non-zero on fail. + */ +static int vrtct_add_psram_entry(struct acpi_table_hdr *vrtct, uint32_t cache_level, uint64_t base, uint32_t ways, + uint32_t size, uint64_t sw_sram_pcpu_bitmask, uint64_t guest_pcpu_bitmask) +{ + int vlapicid_num; + struct rtct_entry *rtct_entry; + struct rtct_entry_data_psram *sw_sram; + + rtct_entry = get_free_rtct_entry(vrtct); + rtct_entry->format = 1; + rtct_entry->type = RTCT_ENTRY_TYPE_PSRAM; + + sw_sram = (struct rtct_entry_data_psram *)rtct_entry->data; + sw_sram->cache_level = cache_level; + sw_sram->base = base; + sw_sram->ways = ways; + sw_sram->size = size; + + vlapicid_num = map_software_sram_vlapic_ids(sw_sram_pcpu_bitmask, guest_pcpu_bitmask, sw_sram->apic_id_tbl); + if (vlapicid_num <= 0) + return -1; + + rtct_entry->size = RTCT_PSRAM_HEADER_SIZE + (vlapicid_num * sizeof(uint32_t)); + add_rtct_entry(vrtct, rtct_entry); + return 0; +} + +/** + * @brief Add a memory hierarchy entry to virtual RTCT. + * + * @param vrtct Pointer to virtual RTCT. + * @param hierarchy Memory hierarchy(2: cache level-2, 3:cache level-3, 256: the last level) + * @param clock_cycles Latency value of memory 'hierarchy'. + * @param vcpu_num Number of guest vCPU. + * + * @return 0 on success and non-zero on fail. + */ +static int vrtct_add_mem_hierarchy_entry(struct acpi_table_hdr *vrtct, uint32_t hierarchy, + uint32_t clock_cycles, uint64_t vcpu_num) +{ + int vcpuid; + struct rtct_entry *rtct_entry; + struct rtct_entry_data_mem_hi_latency *mem_hi; + + rtct_entry = get_free_rtct_entry(vrtct); + rtct_entry->format = 1; + rtct_entry->type = RTCT_ENTRY_TYPE_MEM_HIERARCHY_LATENCY; + + mem_hi = (struct rtct_entry_data_mem_hi_latency *)rtct_entry->data; + mem_hi->hierarchy = hierarchy; + mem_hi->clock_cycles = clock_cycles; + + for (vcpuid = 0; vcpuid < vcpu_num; vcpuid++) { + mem_hi->apic_id_tbl[vcpuid] = vcpuid_to_vlapicid(vcpuid); + } + rtct_entry->size = RTCT_MEM_HI_HEADER_SIZE + (vcpu_num * sizeof(uint32_t)); + + add_rtct_entry(vrtct, rtct_entry); + return 0; +} + +/** + * @brief Update the base address of Software SRAM regions in vRTCT from + * host physical address(HPA) to guest physical address(GPA). + * + * @param vrtct Pointer to virtual RTCT. + * + * @return void + */ +static void remap_software_sram_regions(struct acpi_table_hdr *vrtct) +{ + struct rtct_entry *entry; + struct rtct_entry_data_psram *sw_sram_region; + uint64_t hpa_bottom, hpa_top; + + hpa_bottom = (uint64_t)-1; + hpa_top = 0; + + foreach_rtct_entry(vrtct, entry) { + if (entry->type == RTCT_ENTRY_TYPE_PSRAM) { + sw_sram_region = (struct rtct_entry_data_psram *)entry->data; + if (hpa_bottom > sw_sram_region->base) { + hpa_bottom = sw_sram_region->base; + } + + if (hpa_top < sw_sram_region->base + sw_sram_region->size) { + hpa_top = sw_sram_region->base + sw_sram_region->size; + } + } + } + pr_info("%s, hpa_bottom:%lx, hpa_top:%lx.\n", __func__, hpa_bottom, hpa_top); + + software_sram_base_hpa = hpa_bottom; + software_sram_size = hpa_top - hpa_bottom; + + foreach_rtct_entry(vrtct, entry) { + if (entry->type == RTCT_ENTRY_TYPE_PSRAM) { + sw_sram_region = (struct rtct_entry_data_psram *)entry->data; + sw_sram_region->base = SOFTWARE_SRAM_BASE_GPA + (sw_sram_region->base - hpa_bottom); + } + } +} + +/** + * @brief Initialize Software SRAM and memory hierarchy entries in virtual RTCT, + * configurations of these entries are from native RTCT. + * + * @param vrtct Pointer to virtual RTCT. + * @param native_rtct Pointer to native RTCT. + * @param guest_pcpu_bitmask pCUP bitmask of guest. + * + * @return 0 on success and non-zero on fail. + */ +static int passthru_rtct_to_guest(struct acpi_table_hdr *vrtct, struct acpi_table_hdr *native_rtct, + uint64_t guest_pcpu_bitmask) +{ + int i, cpu_num, rc = 0; + uint64_t sw_sram_pcpu_bitmask; + struct rtct_entry *entry; + struct rtct_entry_data_psram *sw_sram; + struct rtct_entry_data_mem_hi_latency *mem_hi; + + foreach_rtct_entry(native_rtct, entry) { + switch (entry->type) { + case RTCT_ENTRY_TYPE_PSRAM: + { + /* Get native CPUs of Software SRAM region */ + cpu_num = (entry->size - RTCT_PSRAM_HEADER_SIZE) / sizeof(uint32_t); + sw_sram = (struct rtct_entry_data_psram *)entry->data; + sw_sram_pcpu_bitmask = 0; + for (i = 0; i < cpu_num; i++) { + sw_sram_pcpu_bitmask |= (1U << lapicid_to_cpuid(sw_sram->apic_id_tbl[i])); + } + + if ((sw_sram_pcpu_bitmask & guest_pcpu_bitmask) != 0) { + /* + * argument 'base' is set to HPA(sw_sram->base) in passthru RTCT + * soluation as it is required to calculate Software SRAM regions range + * in host physical address space, this 'base' will be updated to + * GPA when mapping all Software SRAM regions from HPA to GPA. + */ + rc = vrtct_add_psram_entry(vrtct, sw_sram->cache_level, sw_sram->base, + sw_sram->ways, sw_sram->size, sw_sram_pcpu_bitmask, guest_pcpu_bitmask); + } + } + break; + + case RTCT_ENTRY_TYPE_MEM_HIERARCHY_LATENCY: + { + mem_hi = (struct rtct_entry_data_mem_hi_latency *)entry->data; + rc = vrtct_add_mem_hierarchy_entry(vrtct, mem_hi->hierarchy, mem_hi->clock_cycles, + bitmap_weight(guest_pcpu_bitmask)); + } + break; + + default: + break; + } + + if (rc) + break; + } + + if (rc) + return -1; + + remap_software_sram_regions(vrtct); + vrtct->checksum = vrtct_checksum((uint8_t *)vrtct, vrtct->length); + return rc; +} + +uint64_t get_software_sram_base_hpa(void) +{ + return software_sram_base_hpa; +} + +uint64_t get_software_sram_size(void) +{ + return software_sram_size; +} + +/** + * @brief Initialize virtual RTCT based on configurations from native RTCT in Service VM. + * + * @param ctx Pointer to context of guest. + * @param cfg Pointer to configuration data, it pointers to native RTCT in passthru RTCT solution. + * + * @return Pointer to virtual RTCT data on success and NULL on fail. + */ +uint8_t *build_vrtct(struct vmctx *ctx, void *cfg) +{ +#define PTCT_BUF_SIZE 4096 + struct acrn_vm_config vm_cfg; + struct acpi_table_hdr *rtct_cfg, *vrtct = NULL; + uint64_t dm_cpu_bitmask, hv_cpu_bitmask, guest_pcpu_bitmask; + + if ((cfg == NULL) || (ctx == NULL)) + return NULL; + + rtct_cfg = (struct acpi_table_hdr *)cfg; + vrtct = malloc(PTCT_BUF_SIZE); + if (vrtct == NULL) { + pr_err("%s, Failed to allocate vRTCT buffer.\n", __func__); + return NULL; + } + + memcpy((void *)vrtct, (void *)rtct_cfg, sizeof(struct acpi_table_hdr)); + vrtct->length = sizeof(struct acpi_table_hdr); + vrtct->checksum = 0; + + if (vm_get_config(ctx, &vm_cfg)) { + pr_err("%s, get VM configuration fail.\n", __func__); + goto error; + } + + /* + * pCPU bitmask of VM is configured in hypervisor by default but can be + * overwritten by '--cpu_affinity' argument of DM if this bitmask is + * the subset of bitmask configured in hypervisor. + * + * FIXME: The cpu_affinity does not only mean the vcpu's pcpu affinity but + * also indicates the maximum vCPU number of guest. Its name should be renamed + * to pu_bitmask to avoid confusing. + */ + hv_cpu_bitmask = vm_cfg.cpu_affinity; + dm_cpu_bitmask = vm_get_cpu_affinity_dm(); + if ((dm_cpu_bitmask != 0) && ((dm_cpu_bitmask & ~hv_cpu_bitmask) == 0)) { + guest_pcpu_bitmask = dm_cpu_bitmask; + } else { + guest_pcpu_bitmask = hv_cpu_bitmask; + } + + if (guest_pcpu_bitmask == 0) { + pr_err("%s,Err: Invalid guest_pcpu_bitmask.\n", __func__); + goto error; + } + + pr_info("%s, dm_cpu_bitmask:0x%x, hv_cpu_bitmask:0x%x, guest_cpu_bitmask: 0x%x\n", + __func__, dm_cpu_bitmask, hv_cpu_bitmask, guest_pcpu_bitmask); + + if (passthru_rtct_to_guest(vrtct, rtct_cfg, guest_pcpu_bitmask)) { + pr_err("%s, initialize vRTCT fail.", __func__); + goto error; + } + + return (uint8_t *)vrtct; +error: + free(vrtct); + return NULL; +} diff --git a/devicemodel/include/acpi.h b/devicemodel/include/acpi.h index 572a2dba5..e01744b4c 100644 --- a/devicemodel/include/acpi.h +++ b/devicemodel/include/acpi.h @@ -39,6 +39,27 @@ #define IO_PMTMR 0x0 /* PM Timer is disabled in ACPI */ +struct acpi_table_hdr { + /* ASCII table signature */ + char signature[4]; + /* Length of table in bytes, including this header */ + uint32_t length; + /* ACPI Specification minor version number */ + uint8_t revision; + /* To make sum of entire table == 0 */ + uint8_t checksum; + /* ASCII OEM identification */ + char oem_id[6]; + /* ASCII OEM table identification */ + char oem_table_id[8]; + /* OEM revision number */ + uint32_t oem_revision; + /* ASCII ASL compiler vendor ID */ + char asl_compiler_id[4]; + /* ASL compiler version */ + uint32_t asl_compiler_revision; +} __packed; + /* All dynamic table entry no. */ #define NHLT_ENTRY_NO 8 diff --git a/devicemodel/include/rtct.h b/devicemodel/include/rtct.h index 679705dac..b8f141acf 100644 --- a/devicemodel/include/rtct.h +++ b/devicemodel/include/rtct.h @@ -7,10 +7,46 @@ #ifndef RTCT_H #define RTCT_H +#define RTCT_ENTRY_TYPE_PTCD_LIMIT 1U +#define RTCT_ENTRY_TYPE_PTCM_BINARY 2U +#define RTCT_ENTRY_TYPE_WRC_L3_MASKS 3U +#define RTCT_ENTRY_TYPE_GT_L3_MASKS 4U +#define RTCT_ENTRY_TYPE_PSRAM 5U +#define RTCT_ENTRY_TYPE_STREAM_DATAPATH 6U +#define RTCT_ENTRY_TYPE_TIMEAWARE_SUBSYS 7U +#define RTCT_ENTRY_TYPE_RT_IOMMU 8U +#define RTCT_ENTRY_TYPE_MEM_HIERARCHY_LATENCY 9U /* TODO: Move to high-memory region. */ #define SOFTWARE_SRAM_BASE_HPA 0x40080000UL #define SOFTWARE_SRAM_BASE_GPA 0x40080000UL #define SOFTWARE_SRAM_MAX_SIZE 0x00800000UL +struct rtct_entry { + uint16_t size; + uint16_t format; + uint32_t type; + uint32_t data[64]; +} __packed; + + +struct rtct_entry_data_psram { + uint32_t cache_level; + uint64_t base; + uint32_t ways; + uint32_t size; + uint32_t apic_id_tbl[64]; +} __packed; + + +struct rtct_entry_data_mem_hi_latency { + uint32_t hierarchy; + uint32_t clock_cycles; + uint32_t apic_id_tbl[64]; +} __packed; + +uint64_t get_software_sram_base_hpa(void); +uint64_t get_software_sram_size(void); +uint8_t *build_vrtct(struct vmctx *ctx, void *cfg); + #endif /* RTCT_H */ diff --git a/devicemodel/include/types.h b/devicemodel/include/types.h index dce9e2b7a..80933b9cc 100644 --- a/devicemodel/include/types.h +++ b/devicemodel/include/types.h @@ -89,6 +89,14 @@ flsl(uint64_t mask) return mask ? 64 - __builtin_clzl(mask) : 0; } +/* Returns the number of 1-bits in bits. */ +static inline int +bitmap_weight(uint64_t bits) +{ + return __builtin_popcountl(bits); + +} + /* memory barrier */ #define mb() ({ asm volatile("mfence" ::: "memory"); (void)0; }) diff --git a/devicemodel/include/vmmapi.h b/devicemodel/include/vmmapi.h index 1ffcd6d22..80b5b9c24 100644 --- a/devicemodel/include/vmmapi.h +++ b/devicemodel/include/vmmapi.h @@ -140,6 +140,7 @@ int vm_add_hv_vdev(struct vmctx *ctx, struct acrn_emul_dev *dev); int vm_remove_hv_vdev(struct vmctx *ctx, struct acrn_emul_dev *dev); int acrn_parse_cpu_affinity(char *arg); +uint64_t vm_get_cpu_affinity_dm(void); int vm_create_vcpu(struct vmctx *ctx, uint16_t vcpu_id); int vm_set_vcpu_regs(struct vmctx *ctx, struct acrn_set_vcpu_regs *cpu_regs);