1 /* $NetBSD: route80h.c,v 1.1.1.2 2018/08/16 18:17:47 jmcneill Exp $ */
2
3 #include <efi.h>
4 #include <efilib.h>
5
6 /* this example program changes the Reserved Page Route (RPR) bit on ICH10's General
7 * Control And Status Register (GCS) from LPC to PCI. In practical terms, it routes
8 * outb to port 80h to the PCI bus. */
9
10 #define GCS_OFFSET_ADDR 0x3410
11 #define GCS_RPR_SHIFT 2
12 #define GCS_RPR_PCI 1
13 #define GCS_RPR_LPC 0
14
15 #define VENDOR_ID_INTEL 0x8086
16 #define DEVICE_ID_LPCIF 0x3a16
17 #define DEVICE_ID_COUGARPOINT_LPCIF 0x1c56
18
19 static EFI_HANDLE ImageHandle;
20
21 typedef struct {
22 uint16_t vendor_id; /* 00-01 */
23 uint16_t device_id; /* 02-03 */
24 char pad[0xEB]; /* 04-EF */
25 uint32_t rcba; /* F0-F3 */
26 uint32_t reserved[3]; /* F4-FF */
27 } lpcif_t;
28
set_bit(volatile uint32_t * flag,int bit,int value)29 static inline void set_bit(volatile uint32_t *flag, int bit, int value)
30 {
31 uint32_t val = *flag;
32 Print(L"current value is 0x%2x\n", val);
33
34 if (value) {
35 val |= (1 << bit);
36 } else {
37 val &= ~(1 << bit);
38 }
39 Print(L"setting value to 0x%2x\n", val);
40 *flag = val;
41 val = *flag;
42 Print(L"new value is 0x%2x\n", val);
43 }
44
is_device(EFI_PCI_IO * pciio,uint16_t vendor_id,uint16_t device_id)45 static int is_device(EFI_PCI_IO *pciio, uint16_t vendor_id, uint16_t device_id)
46 {
47 lpcif_t lpcif;
48 EFI_STATUS rc;
49
50 rc = uefi_call_wrapper(pciio->Pci.Read, 5, pciio, EfiPciIoWidthUint16, 0, 2, &lpcif);
51 if (EFI_ERROR(rc))
52 return 0;
53
54 if (vendor_id == lpcif.vendor_id && device_id == lpcif.device_id)
55 return 1;
56 return 0;
57 }
58
find_pci_device(uint16_t vendor_id,uint16_t device_id,EFI_PCI_IO ** pciio)59 static EFI_STATUS find_pci_device(uint16_t vendor_id, uint16_t device_id,
60 EFI_PCI_IO **pciio)
61 {
62 EFI_STATUS rc;
63 EFI_HANDLE *Handles;
64 UINTN NoHandles, i;
65
66 if (!pciio)
67 return EFI_INVALID_PARAMETER;
68
69 rc = LibLocateHandle(ByProtocol, &PciIoProtocol, NULL, &NoHandles,
70 &Handles);
71 if (EFI_ERROR(rc))
72 return rc;
73
74 for (i = 0; i < NoHandles; i++) {
75 void *pciio_tmp = NULL;
76 rc = uefi_call_wrapper(BS->OpenProtocol, 6, Handles[i],
77 &PciIoProtocol, &pciio_tmp, ImageHandle,
78 NULL, EFI_OPEN_PROTOCOL_GET_PROTOCOL);
79 if (EFI_ERROR(rc))
80 continue;
81 *pciio = pciio_tmp;
82 if (!is_device(*pciio, vendor_id, device_id)) {
83 *pciio = NULL;
84 continue;
85 }
86
87 return EFI_SUCCESS;
88 }
89 return EFI_NOT_FOUND;
90 }
91
92 EFI_STATUS
efi_main(EFI_HANDLE image_handle,EFI_SYSTEM_TABLE * systab)93 efi_main (EFI_HANDLE image_handle, EFI_SYSTEM_TABLE *systab)
94 {
95 InitializeLib(image_handle, systab);
96 EFI_PCI_IO *pciio = NULL;
97 lpcif_t lpcif;
98 EFI_STATUS rc = EFI_SUCCESS;
99 struct {
100 uint16_t vendor;
101 uint16_t device;
102 } devices[] = {
103 { VENDOR_ID_INTEL, DEVICE_ID_LPCIF },
104 { VENDOR_ID_INTEL, DEVICE_ID_COUGARPOINT_LPCIF },
105 { 0, 0 }
106 };
107 int i;
108
109 ImageHandle = image_handle;
110 for (i = 0; devices[i].vendor != 0; i++) {
111 rc = find_pci_device(devices[i].vendor, devices[i].device, &pciio);
112 if (EFI_ERROR(rc))
113 continue;
114 }
115
116 if (rc == EFI_NOT_FOUND) {
117 Print(L"Device not found.\n");
118 return rc;
119 } else if (EFI_ERROR(rc)) {
120 return rc;
121 }
122
123 rc = uefi_call_wrapper(pciio->Pci.Read, 5, pciio, EfiPciIoWidthUint32,
124 EFI_FIELD_OFFSET(lpcif_t, rcba), 1, &lpcif.rcba);
125 if (EFI_ERROR(rc))
126 return rc;
127 if (!(lpcif.rcba & 1)) {
128 Print(L"rcrb is not mapped, cannot route port 80h\n");
129 return EFI_UNSUPPORTED;
130 }
131 lpcif.rcba &= ~1UL;
132
133 Print(L"rcba: 0x%8x\n", lpcif.rcba, lpcif.rcba);
134 set_bit((uint32_t *)(intptr_t)(lpcif.rcba + GCS_OFFSET_ADDR),
135 GCS_RPR_SHIFT, GCS_RPR_PCI);
136
137 return EFI_SUCCESS;
138 }
139