/* $NetBSD: route80h.c,v 1.1.1.1 2014/04/01 16:16:06 jakllsch Exp $ */ #include #include /* this example program changes the Reserved Page Route (RPR) bit on ICH10's General * Control And Status Register (GCS) from LPC to PCI. In practical terms, it routes * outb to port 80h to the PCI bus. */ #define GCS_OFFSET_ADDR 0x3410 #define GCS_RPR_SHIFT 2 #define GCS_RPR_PCI 1 #define GCS_RPR_LPC 0 #define VENDOR_ID_INTEL 0x8086 #define DEVICE_ID_LPCIF 0x3a16 #define DEVICE_ID_COUGARPOINT_LPCIF 0x1c56 static EFI_HANDLE ImageHandle; typedef struct { uint16_t vendor_id; /* 00-01 */ uint16_t device_id; /* 02-03 */ char pad[0xEB]; /* 04-EF */ uint32_t rcba; /* F0-F3 */ uint32_t reserved[3]; /* F4-FF */ } lpcif_t; static inline void set_bit(volatile uint32_t *flag, int bit, int value) { uint32_t val = *flag; Print(L"current value is 0x%2x\n", val); if (value) { val |= (1 << bit); } else { val &= ~(1 << bit); } Print(L"setting value to 0x%2x\n", val); *flag = val; val = *flag; Print(L"new value is 0x%2x\n", val); } static inline int configspace_matches_ids(void *config, uint32_t vendor_id, uint32_t device_id) { uint32_t *cfg = config; if (cfg[0] == vendor_id && cfg[1] == device_id) return 1; return 0; } static int is_device(EFI_PCI_IO *pciio, uint16_t vendor_id, uint16_t device_id) { lpcif_t lpcif; EFI_STATUS rc; rc = uefi_call_wrapper(pciio->Pci.Read, 5, pciio, EfiPciIoWidthUint16, 0, 2, &lpcif); if (EFI_ERROR(rc)) return 0; if (vendor_id == lpcif.vendor_id && device_id == lpcif.device_id) return 1; return 0; } static EFI_STATUS find_pci_device(uint16_t vendor_id, uint16_t device_id, EFI_PCI_IO **pciio) { EFI_STATUS rc; EFI_HANDLE *Handles; UINTN NoHandles; int i; if (!pciio) return EFI_INVALID_PARAMETER; rc = LibLocateHandle(ByProtocol, &PciIoProtocol, NULL, &NoHandles, &Handles); if (EFI_ERROR(rc)) return rc; for (i = 0; i < NoHandles; i++) { void *pciio_tmp = NULL; rc = uefi_call_wrapper(BS->OpenProtocol, 6, Handles[i], &PciIoProtocol, &pciio_tmp, ImageHandle, NULL, EFI_OPEN_PROTOCOL_GET_PROTOCOL); if (EFI_ERROR(rc)) continue; *pciio = pciio_tmp; if (!is_device(*pciio, vendor_id, device_id)) { *pciio = NULL; continue; } return EFI_SUCCESS; } return EFI_NOT_FOUND; } EFI_STATUS efi_main (EFI_HANDLE image_handle, EFI_SYSTEM_TABLE *systab) { InitializeLib(image_handle, systab); EFI_PCI_IO *pciio = NULL; lpcif_t lpcif; EFI_STATUS rc; struct { uint16_t vendor; uint16_t device; } devices[] = { { VENDOR_ID_INTEL, DEVICE_ID_LPCIF }, { VENDOR_ID_INTEL, DEVICE_ID_COUGARPOINT_LPCIF }, { 0, 0 } }; int i; ImageHandle = image_handle; for (i = 0; devices[i].vendor != 0; i++) { rc = find_pci_device(devices[i].vendor, devices[i].device, &pciio); if (EFI_ERROR(rc)) continue; } if (rc == EFI_NOT_FOUND) { Print(L"Device not found.\n"); return rc; } else if (EFI_ERROR(rc)) { return rc; } rc = uefi_call_wrapper(pciio->Pci.Read, 5, pciio, EfiPciIoWidthUint32, EFI_FIELD_OFFSET(lpcif_t, rcba), 1, &lpcif.rcba); if (EFI_ERROR(rc)) return rc; if (!(lpcif.rcba & 1)) { Print(L"rcrb is not mapped, cannot route port 80h\n"); return EFI_UNSUPPORTED; } lpcif.rcba &= ~1UL; Print(L"rcba: 0x%8x\n", lpcif.rcba, lpcif.rcba); set_bit((uint32_t *)(uint64_t)(lpcif.rcba + GCS_OFFSET_ADDR), GCS_RPR_SHIFT, GCS_RPR_PCI); return EFI_SUCCESS; }