diff --git a/hypervisor/Makefile b/hypervisor/Makefile index fde511f0d..47776ac37 100644 --- a/hypervisor/Makefile +++ b/hypervisor/Makefile @@ -313,6 +313,7 @@ VP_DM_C_SRCS += dm/vpci/vmsix.c VP_DM_C_SRCS += dm/vpci/vmsix_on_msi.c VP_DM_C_SRCS += dm/vpci/vsriov.c VP_DM_C_SRCS += dm/mmio_dev.c +VP_DM_C_SRCS += dm/vgpio.c VP_DM_C_SRCS += arch/x86/guest/vlapic.c VP_DM_C_SRCS += arch/x86/guest/pm.c VP_DM_C_SRCS += arch/x86/guest/assign.c diff --git a/hypervisor/arch/x86/guest/vm.c b/hypervisor/arch/x86/guest/vm.c index 2e0422eb7..2b694f95f 100644 --- a/hypervisor/arch/x86/guest/vm.c +++ b/hypervisor/arch/x86/guest/vm.c @@ -34,6 +34,7 @@ #include #include #include +#include vm_sw_loader_t vm_sw_loader; @@ -263,6 +264,12 @@ static void prepare_prelaunched_vm_memmap(struct acrn_vm *vm, const struct acrn_ for (i = 0U; i < MAX_MMIO_DEV_NUM; i++) { (void)assign_mmio_dev(vm, &vm_config->mmiodevs[i]); + +#ifdef P2SB_VGPIO_DM_ENABLED + if ((vm_config->pt_p2sb_bar) && (vm_config->mmiodevs[i].base_hpa == P2SB_BAR_ADDR)) { + register_vgpio_handler(vm, &vm_config->mmiodevs[i]); + } +#endif } } diff --git a/hypervisor/dm/vgpio.c b/hypervisor/dm/vgpio.c new file mode 100644 index 000000000..3a5f856ba --- /dev/null +++ b/hypervisor/dm/vgpio.c @@ -0,0 +1,150 @@ +/* + * Copyright (C) 2020 Intel Corporation. All rights reserved. + * + * SPDX-License-Identifier: BSD-3-Clause + */ + + +/* +* Emulate GPIO registers which are only accessible through Primary to Sideband Bridge (P2SB). +* +* Intercept accesses to MISCCFG.GPDMINTSEL[31:24] and PADCFG1.INTSEL[7:0] GPIO registers which hold physical interrupt +* lines and return virtualized values upon read in accordance with the gsi to vgsi mappings given by the VM config. +* +* P2SB_BAR_ADDR: 0xFD000000 (fixed by BIOS) +* +* +* -------------------------------------------------------------------------------------- +* SideBand Endpoint Name | Port ID +* -------------------------------------------------------------------------------------- +* GPIO Community 5 | 0x69 +* GPIO Community 4 | 0x6A +* GPIO Community 3 | 0x6B +* GPIO Community 2 | 0x6C +* GPIO Community 1 | 0x6D +* GPIO Community 0 | 0x6E +* -------------------------------------------------------------------------------------- +* +* Private Configuration Register (PCR) Address = P2SB_BAR_ADDR + (Port ID << 16) + Register Offset +* e.g.) +* GPIO_COMMUNITY_5_PCR_BASE = P2SB_BAR_ADDR + (0x69 << 16) = 0xFD690000 +* GPIO_COMMUNITY_5_MISCCFG = GPIO_COMMUNITY_5_PCR_BASE + 0x010 = 0xFD690010 +* GPIO_COMMUNITY_5_PAD0_CFG1 = GPIO_COMMUNITY_5_PCR_BASE + 0x704 = 0xFD690704 +* GPIO_COMMUNITY_5_PAD1_CFG1 = GPIO_COMMUNITY_5_PCR_BASE + 0x714 = 0xFD690714 +* GPIO_COMMUNITY_5_PAD2_CFG1 = GPIO_COMMUNITY_5_PCR_BASE + 0x724 = 0xFD690724 +* .... +* +*/ + +#include +#include +#include +#include +#include +#include +#include + +#ifdef P2SB_VGPIO_DM_ENABLED + +#define P2SB_PORTID_SHIFT 16U +#define P2SB_AGENT_NUM 256U +#define P2SB_PCR_SPACE_SIZE_PER_AGENT 0x10000U +#define P2SB_PCR_SPACE_SIZE_TOTAL (P2SB_AGENT_NUM * P2SB_PCR_SPACE_SIZE_PER_AGENT) +#define P2SB_PCR_SPACE_MASK ((1UL << P2SB_PORTID_SHIFT) - 1UL) + +#define GPIO_MISCCFG 0x010U +#define GPIO_MISGCFG_GPDMINTSEL_SHIFT 24U +#define GPIO_PADBAR 0x00CU +#define GPIO_PADCFG1 0x004U +#define GPIO_PADCFG1_INTSEL_SHIFT 0U + +#define GPIO_INVALID_PIN 0xFFU + +/** + * @return vpin mapped to the given phys_pin in accordance with the VM config, if not found return 0xFF as invalid pin + */ +static uint32_t ioapic_pin_to_vpin(struct acrn_vm *vm, const struct acrn_vm_config *vm_config, const uint32_t phys_pin) +{ + uint32_t i; + uint32_t vpin = GPIO_INVALID_PIN; + struct acrn_single_vioapic *vioapic; + + for (i = 0U; i < vm_config->pt_intx_num; i++) { + if (phys_pin == gsi_to_ioapic_pin(vm_config->pt_intx[i].phys_gsi)) { + vioapic = vgsi_to_vioapic_and_vpin(vm, vm_config->pt_intx[i].virt_gsi, &vpin); + if (!vioapic) { + vpin = GPIO_INVALID_PIN; + } + break; + } + } + + return vpin; +} + +static int32_t vgpio_mmio_handler(struct io_request *io_req, void *data) +{ + struct mmio_request *mmio = &io_req->reqs.mmio; + struct acrn_vm *vm = (struct acrn_vm *) data; + struct acrn_vm_config *vm_config = get_vm_config(vm->vm_id); + int32_t ret = 0; + + uint64_t hpa = P2SB_BAR_ADDR + (mmio->address & (uint64_t)(P2SB_PCR_SPACE_SIZE_TOTAL - 1)); + void *hva = hpa2hva(hpa); + uint64_t reg_offset = hpa & P2SB_PCR_SPACE_MASK; + + uint32_t value, shift; + uint32_t padbar, pad0; + uint32_t phys_pin, virt_pin; + + /* all gpio registers have 4 bytes size */ + if (mmio->size == 4U) { + if (mmio->direction == REQUEST_READ) { + padbar = mmio_read32((const void *)hpa2hva((hpa & ~P2SB_PCR_SPACE_MASK) + GPIO_PADBAR)); + pad0 = padbar & P2SB_PCR_SPACE_MASK; + value = mmio_read32((const void *)hva); + + if ((reg_offset == GPIO_MISCCFG) || + ((reg_offset >= pad0) && ((reg_offset & 0x0FU) == GPIO_PADCFG1))) { + shift = (reg_offset == GPIO_MISCCFG) ? GPIO_MISGCFG_GPDMINTSEL_SHIFT : + GPIO_PADCFG1_INTSEL_SHIFT; + phys_pin = (value >> shift) & 0xFFU; + virt_pin = ioapic_pin_to_vpin(vm, vm_config, phys_pin); + value = (value & ~(0xFFU << shift)) | (virt_pin << shift); + } + mmio->value = (uint64_t)value; + } else { + value = (uint32_t)mmio->value; + if (reg_offset == GPIO_MISCCFG) { /* discard writes to MISCCFG.GPDMINTSEL[31:24] */ + value = (value & ~(0xFFU << GPIO_MISGCFG_GPDMINTSEL_SHIFT)) | + (mmio_read32((const void *)hva) & (0xFFU << GPIO_MISGCFG_GPDMINTSEL_SHIFT)); + } + mmio_write32(value, (void *)hva); + } + } else { + ret = -EINVAL; + } + + return ret; +} + +/** + * @pre vm != NULL && mmiodev != NULL + */ +void register_vgpio_handler(struct acrn_vm *vm, const struct acrn_mmiodev *mmiodev) +{ + uint64_t gpa_start, gpa_end, gpio_pcr_sz; + uint64_t base_hpa; + + gpa_start = mmiodev->base_gpa + (P2SB_BASE_GPIO_PORT_ID << P2SB_PORTID_SHIFT); + gpio_pcr_sz = P2SB_PCR_SPACE_SIZE_PER_AGENT * P2SB_MAX_GPIO_COMMUNITIES; + gpa_end = gpa_start + gpio_pcr_sz; + base_hpa = mmiodev->base_hpa + (P2SB_BASE_GPIO_PORT_ID << P2SB_PORTID_SHIFT); + + /* emulate MMIO access to the GPIO private configuration space registers */ + hv_access_memory_region_update((uint64_t)hpa2hva(base_hpa), gpio_pcr_sz); + register_mmio_emulation_handler(vm, vgpio_mmio_handler, gpa_start, gpa_end, (void *)vm, false); + ept_del_mr(vm, (uint64_t *)vm->arch_vm.nworld_eptp, gpa_start, gpio_pcr_sz); +} + +#endif diff --git a/hypervisor/dm/vioapic.c b/hypervisor/dm/vioapic.c index 9e967b2b3..5e02497e3 100644 --- a/hypervisor/dm/vioapic.c +++ b/hypervisor/dm/vioapic.c @@ -114,7 +114,7 @@ vioapic_set_pinstate(struct acrn_single_vioapic *vioapic, uint32_t pin, uint32_t } -static struct acrn_single_vioapic * +struct acrn_single_vioapic * vgsi_to_vioapic_and_vpin(const struct acrn_vm *vm, uint32_t vgsi, uint32_t *vpin) { struct acrn_single_vioapic *vioapic; diff --git a/hypervisor/include/dm/vgpio.h b/hypervisor/include/dm/vgpio.h new file mode 100644 index 000000000..ef6028994 --- /dev/null +++ b/hypervisor/include/dm/vgpio.h @@ -0,0 +1,12 @@ +/* + * Copyright (C) 2020 Intel Corporation. All rights reserved. + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +#ifndef VGPIO_H +#define VGPIO_H + +void register_vgpio_handler(struct acrn_vm *vm, const struct acrn_mmiodev *mmiodev); + +#endif /* MMIO_DEV_H */ diff --git a/hypervisor/include/dm/vioapic.h b/hypervisor/include/dm/vioapic.h index 6a157bca1..c5f1f1d77 100644 --- a/hypervisor/include/dm/vioapic.h +++ b/hypervisor/include/dm/vioapic.h @@ -119,6 +119,7 @@ uint32_t get_vm_gsicount(const struct acrn_vm *vm); void vioapic_broadcast_eoi(const struct acrn_vm *vm, uint32_t vector); void vioapic_get_rte(const struct acrn_vm *vm, uint32_t vgsi, union ioapic_rte *rte); int32_t vioapic_mmio_access_handler(struct io_request *io_req, void *handler_private_data); +struct acrn_single_vioapic *vgsi_to_vioapic_and_vpin(const struct acrn_vm *vm, uint32_t vgsi, uint32_t *vpin); /** * @}