diff --git a/lib/libvmmapi/vmmapi.h b/lib/libvmmapi/vmmapi.h --- a/lib/libvmmapi/vmmapi.h +++ b/lib/libvmmapi/vmmapi.h @@ -180,6 +180,8 @@ vm_paddr_t gpa, size_t len, vm_paddr_t hpa); int vm_unmap_pptdev_mmio(struct vmctx *ctx, int bus, int slot, int func, vm_paddr_t gpa, size_t len); +int vm_get_memory_region_info(struct vmctx *const ctx, vm_paddr_t *const base, + vm_paddr_t *const size, const enum vm_memory_region_type type); int vm_setup_pptdev_msi(struct vmctx *ctx, int vcpu, int bus, int slot, int func, uint64_t addr, uint64_t msg, int numvec); int vm_setup_pptdev_msix(struct vmctx *ctx, int vcpu, int bus, int slot, diff --git a/lib/libvmmapi/vmmapi.c b/lib/libvmmapi/vmmapi.c --- a/lib/libvmmapi/vmmapi.c +++ b/lib/libvmmapi/vmmapi.c @@ -1012,6 +1012,25 @@ return (ioctl(ctx->fd, VM_UNMAP_PPTDEV_MMIO, &pptmmio)); } +int +vm_get_memory_region_info(struct vmctx *const ctx, vm_paddr_t *const base, + vm_paddr_t *const size, const enum vm_memory_region_type type) +{ + struct vm_memory_region_info memory_region_info; + + bzero(&memory_region_info, sizeof(memory_region_info)); + memory_region_info.type = type; + + const int error = ioctl(ctx->fd, VM_GET_MEMORY_REGION_INFO, &memory_region_info); + + if (base) + *base = memory_region_info.base; + if (size) + *size = memory_region_info.size; + + return (error); +} + int vm_setup_pptdev_msi(struct vmctx *ctx, int vcpu, int bus, int slot, int func, uint64_t addr, uint64_t msg, int numvec) @@ -1687,7 +1706,7 @@ VM_SET_CAPABILITY, VM_GET_CAPABILITY, VM_BIND_PPTDEV, VM_UNBIND_PPTDEV, VM_MAP_PPTDEV_MMIO, VM_PPTDEV_MSI, VM_PPTDEV_MSIX, VM_UNMAP_PPTDEV_MMIO, VM_PPTDEV_DISABLE_MSIX, - VM_INJECT_NMI, VM_STATS, VM_STAT_DESC, + VM_GET_MEMORY_REGION_INFO, VM_INJECT_NMI, VM_STATS, VM_STAT_DESC, VM_SET_X2APIC_STATE, VM_GET_X2APIC_STATE, VM_GET_HPET_CAPABILITIES, VM_GET_GPA_PMAP, VM_GLA2GPA, VM_GLA2GPA_NOFAULT, diff --git a/sys/amd64/include/vmm.h b/sys/amd64/include/vmm.h --- a/sys/amd64/include/vmm.h +++ b/sys/amd64/include/vmm.h @@ -741,6 +741,12 @@ } u; }; +enum vm_memory_region_type { + MEMORY_REGION_INTEL_GSM, + MEMORY_REGION_INTEL_OPREGION, + MEMORY_REGION_TPM_CONTROL_ADDRESS, +}; + /* APIs to inject faults into the guest */ void vm_inject_fault(void *vm, int vcpuid, int vector, int errcode_valid, int errcode); diff --git a/sys/amd64/include/vmm_dev.h b/sys/amd64/include/vmm_dev.h --- a/sys/amd64/include/vmm_dev.h +++ b/sys/amd64/include/vmm_dev.h @@ -146,6 +146,17 @@ size_t len; }; +struct vm_memory_region_info { + vm_paddr_t base; + vm_paddr_t size; + enum vm_memory_region_type type; +}; + +#ifdef _KERNEL +extern vm_paddr_t intel_graphics_stolen_base; +extern vm_paddr_t intel_graphics_stolen_size; +#endif + struct vm_pptdev_msi { int vcpu; int bus; @@ -309,6 +320,7 @@ IOCNUM_PPTDEV_MSIX = 44, IOCNUM_PPTDEV_DISABLE_MSIX = 45, IOCNUM_UNMAP_PPTDEV_MMIO = 46, + IOCNUM_GET_MEMORY_REGION_INFO = 47, /* statistics */ IOCNUM_VM_STATS = 50, @@ -427,6 +439,8 @@ _IOW('v', IOCNUM_PPTDEV_DISABLE_MSIX, struct vm_pptdev) #define VM_UNMAP_PPTDEV_MMIO \ _IOW('v', IOCNUM_UNMAP_PPTDEV_MMIO, struct vm_pptdev_mmio) +#define VM_GET_MEMORY_REGION_INFO \ + _IOWR('v', IOCNUM_GET_MEMORY_REGION_INFO, struct vm_memory_region_info) #define VM_INJECT_NMI \ _IOW('v', IOCNUM_INJECT_NMI, struct vm_nmi) #define VM_STATS \ diff --git a/sys/amd64/vmm/intel/intelgpu.h b/sys/amd64/vmm/intel/intelgpu.h new file mode 100644 --- /dev/null +++ b/sys/amd64/vmm/intel/intelgpu.h @@ -0,0 +1,185 @@ +/*- + * SPDX-License-Identifier: BSD-2-Clause-FreeBSD + * + * Copyright (c) 2021 Beckhoff Automation GmbH & Co. KG + * Author: Corvin Köhne + */ + +#pragma once + +/* + * See + * + */ + +#define IGD_OPREGION_HEADER_SIGN "IntelGraphicsMem" +#define IGD_OPREGION_HEADER_MBOX1 BIT0 +#define IGD_OPREGION_HEADER_MBOX2 BIT1 +#define IGD_OPREGION_HEADER_MBOX3 BIT2 +#define IGD_OPREGION_HEADER_MBOX4 BIT3 +#define IGD_OPREGION_HEADER_MBOX5 BIT4 + +#define IGD_OPREGION_VBT_SIZE_6K (6 * 1024UL) + +/** + OpRegion structures: + Sub-structures define the different parts of the OpRegion followed by the + main structure representing the entire OpRegion. + + @note These structures are packed to 1 byte offsets because the exact + data location is required by the supporting design specification due to + the fact that the data is used by ASL and Graphics driver code compiled + separately. +**/ +#pragma pack(1) +/// +/// OpRegion Mailbox 0 Header structure. The OpRegion Header is used to +/// identify a block of memory as the graphics driver OpRegion. +/// Offset 0x0, Size 0x100 +/// +struct igd_opregion_header { + int8_t sign[0x10]; ///< Offset 0x00 OpRegion Signature + uint32_t size; ///< Offset 0x10 OpRegion Size + uint32_t over; ///< Offset 0x14 OpRegion Structure Version + uint8_t sver[0x20]; ///< Offset 0x18 System BIOS Build Version + uint8_t vver[0x10]; ///< Offset 0x38 Video BIOS Build Version + uint8_t gver[0x10]; ///< Offset 0x48 Graphic Driver Build Version + uint32_t mbox; ///< Offset 0x58 Supported Mailboxes + uint32_t dmod; ///< Offset 0x5C Driver Model + uint32_t pcon; ///< Offset 0x60 Platform Configuration + int16_t dver[0x10]; ///< Offset 0x64 GOP Version + uint8_t rm01[0x7C]; ///< Offset 0x84 Reserved Must be zero +}; + +/// +/// OpRegion Mailbox 1 - Public ACPI Methods +/// Offset 0x100, Size 0x100 +/// +struct igd_opregion_mbox1 { + uint32_t drdy; ///< Offset 0x100 Driver Readiness + uint32_t csts; ///< Offset 0x104 Status + uint32_t cevt; ///< Offset 0x108 Current Event + uint8_t rm11[0x14]; ///< Offset 0x10C Reserved Must be Zero + uint32_t didl[8]; ///< Offset 0x120 Supported Display Devices ID List + uint32_t + cpdl[8]; ///< Offset 0x140 Currently Attached Display Devices List + uint32_t + cadl[8]; ///< Offset 0x160 Currently Active Display Devices List + uint32_t nadl[8]; ///< Offset 0x180 Next Active Devices List + uint32_t aslp; ///< Offset 0x1A0 ASL Sleep Time Out + uint32_t tidx; ///< Offset 0x1A4 Toggle Table Index + uint32_t chpd; ///< Offset 0x1A8 Current Hotplug Enable Indicator + uint32_t clid; ///< Offset 0x1AC Current Lid State Indicator + uint32_t cdck; ///< Offset 0x1B0 Current Docking State Indicator + uint32_t sxsw; ///< Offset 0x1B4 Display Switch Notification on Sx + ///< StateResume + uint32_t evts; ///< Offset 0x1B8 Events supported by ASL + uint32_t cnot; ///< Offset 0x1BC Current OS Notification + uint32_t NRDY; ///< Offset 0x1C0 Driver Status + uint8_t did2[0x1C]; ///< Offset 0x1C4 Extended Supported Devices ID + ///< List(DOD) + uint8_t + cpd2[0x1C]; ///< Offset 0x1E0 Extended Attached Display Devices List + uint8_t rm12[4]; ///< Offset 0x1FC - 0x1FF Reserved Must be zero +}; + +/// +/// OpRegion Mailbox 2 - Software SCI Interface +/// Offset 0x200, Size 0x100 +/// +struct igd_opregion_mbox2 { + uint32_t scic; ///< Offset 0x200 Software SCI Command / Status / Data + uint32_t parm; ///< Offset 0x204 Software SCI Parameters + uint32_t dslp; ///< Offset 0x208 Driver Sleep Time Out + uint8_t rm21[0xF4]; ///< Offset 0x20C - 0x2FF Reserved Must be zero +}; + +/// +/// OpRegion Mailbox 3 - BIOS/Driver Notification - ASLE Support +/// Offset 0x300, Size 0x100 +/// +struct igd_opregion_mbox3 { + uint32_t ardy; ///< Offset 0x300 Driver Readiness + uint32_t aslc; ///< Offset 0x304 ASLE Interrupt Command / Status + uint32_t tche; ///< Offset 0x308 Technology Enabled Indicator + uint32_t alsi; ///< Offset 0x30C Current ALS Luminance Reading + uint32_t bclp; ///< Offset 0x310 Requested Backlight Brightness + uint32_t pfit; ///< Offset 0x314 Panel Fitting State or Request + uint32_t cblv; ///< Offset 0x318 Current Brightness Level + uint16_t bclm[0x14]; ///< Offset 0x31C Backlight Brightness Levels Duty + ///< Cycle Mapping Table + uint32_t cpfm; ///< Offset 0x344 Current Panel Fitting Mode + uint32_t epfm; ///< Offset 0x348 Enabled Panel Fitting Modes + uint8_t plut[0x4A]; ///< Offset 0x34C Panel Look Up Table & Identifier + uint32_t pfmb; ///< Offset 0x396 PWM Frequency and Minimum Brightness + uint32_t ccdv; ///< Offset 0x39A Color Correction Default Values + uint32_t pcft; ///< Offset 0x39E Power Conservation Features + uint32_t srot; ///< Offset 0x3A2 Supported Rotation Angles + uint32_t iuer; ///< Offset 0x3A6 Intel Ultrabook(TM) Event Register + uint64_t fdss; ///< Offset 0x3AA DSS Buffer address allocated for IFFS + ///< feature + uint32_t fdsp; ///< Offset 0x3B2 Size of DSS buffer + uint32_t stat; ///< Offset 0x3B6 State Indicator + uint64_t rvda; ///< Offset 0x3BA Absolute/Relative Address of Raw VBT + ///< Data from OpRegion Base + uint32_t rvds; ///< Offset 0x3C2 Raw VBT Data Size + uint8_t rsvd2[0x3A]; ///< Offset 0x3C6 - 0x3FF Reserved Must be zero. + ///< Bug in spec 0x45(69) +}; + +/// +/// OpRegion Mailbox 4 - VBT Video BIOS Table +/// Offset 0x400, Size 0x1800 +/// +struct igd_opregion_mbox4 { + uint8_t rvbt[IGD_OPREGION_VBT_SIZE_6K]; ///< Offset 0x400 - 0x1BFF Raw + ///< VBT Data +}; + +/// +/// OpRegion Mailbox 5 - BIOS/Driver Notification - Data storage BIOS to Driver +/// data sync Offset 0x1C00, Size 0x400 +/// +struct igd_opregion_mbox5 { + uint32_t phed; ///< Offset 0x1C00 Panel Header + uint8_t bddc[0x100]; ///< Offset 0x1C04 Panel EDID (DDC data) + uint8_t rm51[0x2FC]; ///< Offset 0x1D04 - 0x1FFF Reserved Must be zero +}; + +/// +/// IGD OpRegion Structure +/// +struct igd_opregion { + struct igd_opregion_header + header; ///< OpRegion header (Offset 0x0, Size 0x100) + struct igd_opregion_mbox1 mbox1; ///< Mailbox 1: Public ACPI Methods + ///< (Offset 0x100, Size 0x100) + struct igd_opregion_mbox2 mbox2; ///< Mailbox 2: Software SCI Interface + ///< (Offset 0x200, Size 0x100) + struct igd_opregion_mbox3 + mbox3; ///< Mailbox 3: BIOS to Driver Notification (Offset 0x300, + ///< Size 0x100) + struct igd_opregion_mbox4 mbox4; ///< Mailbox 4: Video BIOS Table (VBT) + ///< (Offset 0x400, Size 0x1800) + struct igd_opregion_mbox5 + mbox5; ///< Mailbox 5: BIOS to Driver Notification Extension (Offset + ///< 0x1C00, Size 0x400) +}; + +/// +/// VBT Header Structure +/// +struct vbt_header { + uint8_t product_string[20]; + uint16_t version; + uint16_t header_size; + uint16_t table_size; + uint8_t checksum; + uint8_t reserved1; + uint32_t bios_data_offset; + uint32_t aim_data_offset[4]; +}; + +#pragma pack() + +int vm_intelgpu_get_opregion(vm_paddr_t *const base, vm_paddr_t *const size); diff --git a/sys/amd64/vmm/intel/intelgpu.c b/sys/amd64/vmm/intel/intelgpu.c new file mode 100644 --- /dev/null +++ b/sys/amd64/vmm/intel/intelgpu.c @@ -0,0 +1,55 @@ +/*- + * SPDX-License-Identifier: BSD-2-Clause-FreeBSD + * + * Copyright (c) 2021 Beckhoff Automation GmbH & Co. KG + * Author: Corvin Köhne + */ + +#include +__FBSDID("$FreeBSD$"); + +#include +#include +#include + +#include +#include + +#include +#include + +#include "intelgpu.h" + +#define KB (1024UL) + +int +vm_intelgpu_get_opregion(vm_paddr_t *const base, vm_paddr_t *const size) +{ + /* intel graphics device is always located at 0:2.0 */ + device_t dev = pci_find_bsf(0, 2, 0); + if (dev == NULL) { + return (ENOENT); + } + + if ((pci_get_vendor(dev) != PCI_VENDOR_INTEL) || + (pci_get_class(dev) != PCIC_DISPLAY) || + (pci_get_subclass(dev) != PCIS_DISPLAY_VGA)) { + return (ENODEV); + } + + const uint64_t asls = pci_read_config(dev, PCIR_ASLS_CTL, 4); + + const struct igd_opregion_header *const opregion_header = + (struct igd_opregion_header *)pmap_map(NULL, asls, + asls + sizeof(*opregion_header), VM_PROT_READ); + if (opregion_header == NULL || + memcmp(opregion_header->sign, IGD_OPREGION_HEADER_SIGN, + sizeof(opregion_header->sign))) { + return (ENODEV); + } + + *base = asls; + *size = opregion_header->size * KB; + + return (0); +} diff --git a/sys/amd64/vmm/io/acpi.h b/sys/amd64/vmm/io/acpi.h new file mode 100644 --- /dev/null +++ b/sys/amd64/vmm/io/acpi.h @@ -0,0 +1,14 @@ +/*- + * SPDX-License-Identifier: BSD-2-Clause-FreeBSD + * + * Copyright (c) 2021 Beckhoff Automation GmbH & Co. KG + * Author: Corvin Köhne + */ + +#pragma once + +#include +#include + +int vmm_tpm2_get_control_address(vm_paddr_t *const base, + vm_paddr_t *const size); diff --git a/sys/amd64/vmm/io/acpi.c b/sys/amd64/vmm/io/acpi.c new file mode 100644 --- /dev/null +++ b/sys/amd64/vmm/io/acpi.c @@ -0,0 +1,37 @@ +/*- + * SPDX-License-Identifier: BSD-2-Clause-FreeBSD + * + * Copyright (c) 2021 Beckhoff Automation GmbH & Co. KG + * Author: Corvin Köhne + */ + +#include +__FBSDID("$FreeBSD$"); + +#include +#include +#include + +#include +#include + +#include "acpi.h" +int +vmm_tpm2_get_control_address(vm_paddr_t *const base, vm_paddr_t *const size) +{ + ACPI_TABLE_HEADER *tpm_header; + if (!ACPI_SUCCESS(AcpiGetTable("TPM2", 1, &tpm_header))) { + return (ENOENT); + } + + if (base) { + const ACPI_TABLE_TPM2 *const tpm_table = (ACPI_TABLE_TPM2 *) + tpm_header; + *base = tpm_table->ControlAddress; + } + if (size) { + *size = 0; + } + + return (0); +} diff --git a/sys/amd64/vmm/vmm_dev.c b/sys/amd64/vmm/vmm_dev.c --- a/sys/amd64/vmm/vmm_dev.c +++ b/sys/amd64/vmm/vmm_dev.c @@ -60,9 +60,11 @@ #include #include +#include "intel/intelgpu.h" #include "vmm_lapic.h" #include "vmm_stat.h" #include "vmm_mem.h" +#include "io/acpi.h" #include "io/ppt.h" #include "io/vatpic.h" #include "io/vioapic.h" @@ -373,6 +375,7 @@ struct vm_capability *vmcap; struct vm_pptdev *pptdev; struct vm_pptdev_mmio *pptmmio; + struct vm_memory_region_info *memory_region_info; struct vm_pptdev_msi *pptmsi; struct vm_pptdev_msix *pptmsix; struct vm_nmi *vmnmi; @@ -540,6 +543,29 @@ error = ppt_unmap_mmio(sc->vm, pptmmio->bus, pptmmio->slot, pptmmio->func, pptmmio->gpa, pptmmio->len); break; + case VM_GET_MEMORY_REGION_INFO: + memory_region_info = (struct vm_memory_region_info *)data; + switch (memory_region_info->type) { + case MEMORY_REGION_INTEL_GSM: + memory_region_info->base = intel_graphics_stolen_base; + memory_region_info->size = intel_graphics_stolen_size; + error = 0; + break; + case MEMORY_REGION_INTEL_OPREGION: + error = + vm_intelgpu_get_opregion(&memory_region_info->base, + &memory_region_info->size); + break; + case MEMORY_REGION_TPM_CONTROL_ADDRESS: + error = vmm_tpm2_get_control_address( + &memory_region_info->base, + &memory_region_info->size); + break; + default: + error = EINVAL; + break; + } + break; case VM_BIND_PPTDEV: pptdev = (struct vm_pptdev *)data; error = vm_assign_pptdev(sc->vm, pptdev->bus, pptdev->slot, diff --git a/sys/dev/pci/pcireg.h b/sys/dev/pci/pcireg.h --- a/sys/dev/pci/pcireg.h +++ b/sys/dev/pci/pcireg.h @@ -1098,3 +1098,14 @@ #define PCIM_OSC_CTL_PCIE_PME 0x04 /* PCIe Native Power Mgt Events */ #define PCIM_OSC_CTL_PCIE_AER 0x08 /* PCIe Advanced Error Reporting */ #define PCIM_OSC_CTL_PCIE_CAP_STRUCT 0x10 /* Various Capability Structures */ + +/* + * Intel graphics device definitions + */ +#define PCIR_BDSM 0x5C /* Base of Data Stolen Memory register */ +#define PCIR_ASLS_CTL 0xFC /* Opregion start address register */ + +/* + * PCI Vendors + */ +#define PCI_VENDOR_INTEL 0x8086 diff --git a/sys/modules/vmm/Makefile b/sys/modules/vmm/Makefile --- a/sys/modules/vmm/Makefile +++ b/sys/modules/vmm/Makefile @@ -29,7 +29,8 @@ x86.c .PATH: ${SRCTOP}/sys/amd64/vmm/io -SRCS+= iommu.c \ +SRCS+= acpi.c \ + iommu.c \ ppt.c \ vatpic.c \ vatpit.c \ @@ -42,6 +43,7 @@ # intel-specific files .PATH: ${SRCTOP}/sys/amd64/vmm/intel SRCS+= ept.c \ + intelgpu.c \ vmcs.c \ vmx_msr.c \ vmx_support.S \