summaryrefslogtreecommitdiff
path: root/apps/route80h.c
blob: e1f62f06824f77dc6ce344a118c754756406d0c7 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
#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 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, 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 *)(intptr_t)(lpcif.rcba + GCS_OFFSET_ADDR),
		     GCS_RPR_SHIFT, GCS_RPR_PCI);

	return EFI_SUCCESS;
}