| #include <efi.h> |
| #include <efilib.h> |
| |
| /* 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; |
| } |