xref: /netbsd-src/sys/external/bsd/gnu-efi/dist/apps/route80h.c (revision b7b7574d3bf8eeb51a1fa3977b59142ec6434a55)
1 /*	$NetBSD: route80h.c,v 1.1.1.1 2014/04/01 16:16:06 jakllsch 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 
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 
45 static inline int configspace_matches_ids(void *config, uint32_t vendor_id,
46 				uint32_t device_id)
47 {
48 	uint32_t *cfg = config;
49 	if (cfg[0] == vendor_id && cfg[1] == device_id)
50 		return 1;
51 	return 0;
52 }
53 
54 static int is_device(EFI_PCI_IO *pciio, uint16_t vendor_id, uint16_t device_id)
55 {
56 	lpcif_t lpcif;
57 	EFI_STATUS rc;
58 
59 	rc = uefi_call_wrapper(pciio->Pci.Read, 5, pciio, EfiPciIoWidthUint16, 0, 2, &lpcif);
60 	if (EFI_ERROR(rc))
61 		return 0;
62 
63 	if (vendor_id == lpcif.vendor_id && device_id == lpcif.device_id)
64 		return 1;
65 	return 0;
66 }
67 
68 static EFI_STATUS find_pci_device(uint16_t vendor_id, uint16_t device_id,
69 				EFI_PCI_IO **pciio)
70 {
71 	EFI_STATUS rc;
72 	EFI_HANDLE *Handles;
73 	UINTN NoHandles;
74 	int i;
75 
76 	if (!pciio)
77 		return EFI_INVALID_PARAMETER;
78 
79 	rc = LibLocateHandle(ByProtocol, &PciIoProtocol, NULL, &NoHandles,
80 			     &Handles);
81 	if (EFI_ERROR(rc))
82 		return rc;
83 
84 	for (i = 0; i < NoHandles; i++) {
85 		void *pciio_tmp = NULL;
86 		rc = uefi_call_wrapper(BS->OpenProtocol, 6, Handles[i],
87 				    &PciIoProtocol, &pciio_tmp, ImageHandle,
88 				    NULL, EFI_OPEN_PROTOCOL_GET_PROTOCOL);
89 		if (EFI_ERROR(rc))
90 			continue;
91 		*pciio = pciio_tmp;
92 		if (!is_device(*pciio, vendor_id, device_id)) {
93 			*pciio = NULL;
94 			continue;
95 		}
96 
97 		return EFI_SUCCESS;
98 	}
99 	return EFI_NOT_FOUND;
100 }
101 
102 EFI_STATUS
103 efi_main (EFI_HANDLE image_handle, EFI_SYSTEM_TABLE *systab)
104 {
105 	InitializeLib(image_handle, systab);
106 	EFI_PCI_IO *pciio = NULL;
107 	lpcif_t lpcif;
108 	EFI_STATUS rc;
109 	struct {
110 		uint16_t vendor;
111 		uint16_t device;
112 	} devices[] = {
113 		{ VENDOR_ID_INTEL, DEVICE_ID_LPCIF },
114 		{ VENDOR_ID_INTEL, DEVICE_ID_COUGARPOINT_LPCIF },
115 		{ 0, 0 }
116 	};
117 	int i;
118 
119 	ImageHandle = image_handle;
120 	for (i = 0; devices[i].vendor != 0; i++) {
121 		rc = find_pci_device(devices[i].vendor, devices[i].device, &pciio);
122 		if (EFI_ERROR(rc))
123 			continue;
124 	}
125 
126 	if (rc == EFI_NOT_FOUND) {
127 		Print(L"Device not found.\n");
128 		return rc;
129 	} else if (EFI_ERROR(rc)) {
130 		return rc;
131 	}
132 
133 	rc = uefi_call_wrapper(pciio->Pci.Read, 5, pciio, EfiPciIoWidthUint32,
134 		EFI_FIELD_OFFSET(lpcif_t, rcba), 1, &lpcif.rcba);
135 	if (EFI_ERROR(rc))
136 		return rc;
137 	if (!(lpcif.rcba & 1)) {
138 		Print(L"rcrb is not mapped, cannot route port 80h\n");
139 		return EFI_UNSUPPORTED;
140 	}
141 	lpcif.rcba &= ~1UL;
142 
143 	Print(L"rcba: 0x%8x\n", lpcif.rcba, lpcif.rcba);
144 	set_bit((uint32_t *)(uint64_t)(lpcif.rcba + GCS_OFFSET_ADDR),
145 		     GCS_RPR_SHIFT, GCS_RPR_PCI);
146 
147 	return EFI_SUCCESS;
148 }
149