/** @file Shared helpers for detecting and accessing the Simics UEFI PCI config device. Copyright (c) 2026, Intel Corporation. All rights reserved.
SPDX-License-Identifier: BSD-2-Clause-Patent **/ #include #include #include #include #include #include static EFI_PCI_ROOT_BRIDGE_IO_PROTOCOL *mPciIo = NULL; static UINT64 mConfigAddr = 0; static BOOLEAN mInitDone = FALSE; /** Locate the PCI Root Bridge I/O protocol and build the config address once, caching both for subsequent calls. @param[out] PciIo Cached protocol pointer. @param[out] ConfigAddr Cached config address. @retval EFI_SUCCESS Protocol located and address built. @retval other LocateProtocol failure. **/ static EFI_STATUS EnsureInitialized ( OUT EFI_PCI_ROOT_BRIDGE_IO_PROTOCOL **PciIo, OUT UINT64 *ConfigAddr ) { EFI_STATUS Status; if (!mInitDone) { Status = gBS->LocateProtocol ( &gEfiPciRootBridgeIoProtocolGuid, NULL, (VOID **)&mPciIo ); if (EFI_ERROR (Status)) { DEBUG ((DEBUG_ERROR, "SimicsUefiDeviceLib: Could not locate PciRootBridgeIo.\n")); return Status; } mConfigAddr = ((UINT64)PcdGet8 (PcdUefiDeviceBus) << 24) | ((UINT64)PcdGet8 (PcdUefiDeviceDevice) << 16) | ((UINT64)PcdGet8 (PcdUefiDeviceFunction) << 8); mInitDone = TRUE; } *PciIo = mPciIo; *ConfigAddr = mConfigAddr; return EFI_SUCCESS; } /** Check for the presence of the Simics UEFI PCI config device. @param[out] PciRootBridgeIo On success, the PCI Root Bridge I/O instance. @param[out] ConfigAddress On success, the assembled PCI config address. @retval EFI_SUCCESS Simics UEFI device found. @retval EFI_NOT_FOUND Device not present or IDs do not match. @retval other Protocol locate or PCI read failure. **/ EFI_STATUS EFIAPI SimicsUefiDeviceCheck ( VOID ) { EFI_STATUS Status; EFI_PCI_ROOT_BRIDGE_IO_PROTOCOL *PciIo; UINT64 Addr; UINT16 DevValues[4]; Status = EnsureInitialized (&PciIo, &Addr); if (EFI_ERROR (Status)) { return Status; } Status = PciIo->Pci.Read (PciIo, EfiPciWidthUint16, Addr + 0x00, 1, &DevValues[0]); if (EFI_ERROR (Status)) { return Status; } Status = PciIo->Pci.Read (PciIo, EfiPciWidthUint16, Addr + 0x02, 1, &DevValues[1]); if (EFI_ERROR (Status)) { return Status; } Status = PciIo->Pci.Read (PciIo, EfiPciWidthUint16, Addr + 0x2c, 1, &DevValues[2]); if (EFI_ERROR (Status)) { return Status; } Status = PciIo->Pci.Read (PciIo, EfiPciWidthUint16, Addr + 0x2e, 1, &DevValues[3]); if (EFI_ERROR (Status)) { return Status; } // // The Simics UEFI device is a hidden paravirtualization device. // It uses VID/DID = 0xFFFF ("no device present" to normal PCI // enumeration) but is identified by its Subsystem VID/ID pair // of 0x8086/0x8086. This ensures normal OS and firmware PCI // probing ignores the device while Simics-aware code can still // find it. // if ((DevValues[0] == 0xFFFF) && (DevValues[1] == 0xFFFF) && (DevValues[2] == 0x8086) && (DevValues[3] == 0x8086)) { DEBUG ((DEBUG_INFO, "Simics UEFI device detected.\n")); return EFI_SUCCESS; } DEBUG ((DEBUG_INFO, "Simics UEFI device NOT detected.\n")); return EFI_NOT_FOUND; } /** Read from PCI config space of the Simics UEFI device. @param[in] Offset Byte offset within PCI config space. @param[in] Width Access width in bytes (1, 2, 4, or 8). @param[out] Buffer Destination buffer. @retval EFI_SUCCESS Read succeeded. @retval EFI_INVALID_PARAMETER Width is not 1, 2, 4, or 8. @retval other Device not found or read failed. **/ EFI_STATUS EFIAPI SimicsUefiDeviceRead ( IN UINT64 Offset, IN UINT8 Width, OUT VOID *Buffer ) { EFI_STATUS Status; EFI_PCI_ROOT_BRIDGE_IO_PROTOCOL *PciIo; UINT64 Addr; EFI_PCI_ROOT_BRIDGE_IO_PROTOCOL_WIDTH PciWidth; switch (Width) { case 1: PciWidth = EfiPciWidthUint8; break; case 2: PciWidth = EfiPciWidthUint16; break; case 4: PciWidth = EfiPciWidthUint32; break; case 8: PciWidth = EfiPciWidthUint64; break; default: return EFI_INVALID_PARAMETER; } Status = EnsureInitialized (&PciIo, &Addr); if (EFI_ERROR (Status)) { return Status; } return PciIo->Pci.Read (PciIo, PciWidth, Addr + Offset, 1, Buffer); } /** Write to PCI config space of the Simics UEFI device. @param[in] Offset Byte offset within PCI config space. @param[in] Width Access width in bytes (1, 2, 4, or 8). @param[in] Buffer Source data. @retval EFI_SUCCESS Write succeeded. @retval EFI_INVALID_PARAMETER Width is not 1, 2, 4, or 8. @retval other Device not found or write failed. **/ EFI_STATUS EFIAPI SimicsUefiDeviceWrite ( IN UINT64 Offset, IN UINT8 Width, IN VOID *Buffer ) { EFI_STATUS Status; EFI_PCI_ROOT_BRIDGE_IO_PROTOCOL *PciIo; UINT64 Addr; EFI_PCI_ROOT_BRIDGE_IO_PROTOCOL_WIDTH PciWidth; switch (Width) { case 1: PciWidth = EfiPciWidthUint8; break; case 2: PciWidth = EfiPciWidthUint16; break; case 4: PciWidth = EfiPciWidthUint32; break; case 8: PciWidth = EfiPciWidthUint64; break; default: return EFI_INVALID_PARAMETER; } Status = EnsureInitialized (&PciIo, &Addr); if (EFI_ERROR (Status)) { return Status; } return PciIo->Pci.Write (PciIo, PciWidth, Addr + Offset, 1, Buffer); }