-
Notifications
You must be signed in to change notification settings - Fork 2
/
smictrl.c
222 lines (179 loc) · 7.19 KB
/
smictrl.c
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
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
/* -*- c-basic-offset: 4 -*-
*
* Copyright (C) 2006, 2010 Jan Kiszka <[email protected]>.
*
* -g, -v, -d, -m, -c options by Marty Vona <[email protected]>
*
* -g option based on https://bugzilla.kernel.org/attachment.cgi?id=23059
*
* Based on Xenomai's SMI workaround.
*
* If there is an accompanying Makefile, just run "make". Otherwise the
* suggested build commands on a Debian-like system are:
*
* sudo apt-get install libpci-dev
* KSRC=/lib/modules/`uname -r`/source
* cc -O2 -Wall -I $KSRC/include -lz -lpci smictrl.c -o smictrl
*
* smictrl is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*/
#include <inttypes.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <sys/io.h>
#include <pci/pci.h>
/* Intel chipset LPC (Low Pin Count) bus controller: PCI device=31 function=0 */
#define LPC_DEV 31
#define LPC_FUNC 0
#define PMBASE_B0 0x40
#define PMBASE_B1 0x41
#define SMI_CTRL_ADDR 0x30
#define SMI_STATUS_ADDR 0x34
#define SMI_ALT_GPIO_ADDR 0x38
#define SMI_MON_ADDR 0x40
/* SMI_EN register: ICH[0](16 bits), ICH[2-5](32 bits) */
#define INTEL_USB2_EN_BIT (0x01 << 18) /* ICH4, ... */
#define LEGACY_USB2_EN_BIT (0x01 << 17) /* ICH4, ... */
#define PERIODIC_EN_BIT (0x01 << 14) /* called 1MIN_ in ICH0 */
#define TCO_EN_BIT (0x01 << 13)
#define MCSMI_EN_BIT (0x01 << 11)
#define SWSMI_TMR_EN_BIT (0x01 << 6)
#define APMC_EN_BIT (0x01 << 5)
#define SLP_EN_BIT (0x01 << 4)
#define LEGACY_USB_EN_BIT (0x01 << 3)
#define BIOS_EN_BIT (0x01 << 2)
#define GBL_SMI_EN_BIT (0x01) /* This is reset by a PCI reset event! */
static uint16_t get_smi_en_addr(struct pci_dev *dev, uint8_t gpio)
{
uint8_t byte0, byte1;
byte0 = pci_read_byte(dev, PMBASE_B0);
byte1 = pci_read_byte(dev, PMBASE_B1);
return ((gpio) ? SMI_ALT_GPIO_ADDR : SMI_CTRL_ADDR) +
(((byte1 << 1) | (byte0 >> 7)) << 7); // bits 7-15
}
static void print_bits(FILE *stream, uint32_t *val) {
#define PRINT_BIT(f) { \
fprintf(stream, "%20s (0x%08x)", #f, f); \
if (val) fprintf(stream, " = %s", ((*val)&f) ? "1" : "0"); \
fprintf(stream, "\n"); \
}
PRINT_BIT(INTEL_USB2_EN_BIT);
PRINT_BIT(LEGACY_USB2_EN_BIT);
PRINT_BIT(PERIODIC_EN_BIT);
PRINT_BIT(TCO_EN_BIT);
PRINT_BIT(MCSMI_EN_BIT);
PRINT_BIT(SWSMI_TMR_EN_BIT);
PRINT_BIT(APMC_EN_BIT);
PRINT_BIT(SLP_EN_BIT);
PRINT_BIT(LEGACY_USB_EN_BIT);
PRINT_BIT(BIOS_EN_BIT);
PRINT_BIT(GBL_SMI_EN_BIT);
#undef PRINT_BIT
}
int main(int argc, char *argv[])
{
char vendor_name[128];
char device_name[128];
struct pci_access *pacc;
struct pci_dev *dev;
uint16_t smi_en_addr;
uint32_t set_bits = 0, clr_bits = 0, opt_bits = 0;
uint32_t orig_value, new_value, new_new_value;
int c, set_value = 0, dry = 0, gpio = 0, verb = 0;
const char *reg_name = "SMI_EN";
int reg_width = 8; /* nybbles */
while ((c = getopt(argc,argv,"hdgvs:m:c:")) != EOF)
switch (c) {
case 'd':
dry = 1;
break;
case 'g':
gpio = 1;
reg_name = "alt GPIO SMI_EN";
reg_width = 4;
break;
case 'v':
verb = 1;
break;
case 's': case 'm': case 'c':
set_value = 1;
opt_bits = strtol(optarg, NULL,
(strncmp(optarg, "0x", 2) == 0) ? 16 : 10);
if (c == 'm') { set_bits |= opt_bits; clr_bits &= ~opt_bits; }
if (c == 'c') { clr_bits |= opt_bits; set_bits &= ~opt_bits; }
else { set_bits = opt_bits; clr_bits = ~opt_bits; }
break;
case 'h': default:
fprintf(stderr, "usage: smictrl [-h] [-d] [-s <bits>] "
"[-m <bits>] [-c <bits>]\n");
fprintf(stderr, " <bits> are in decimal or 0xHEX\n");
fprintf(stderr, " -s sets all bits\n");
fprintf(stderr, " -m marks (sets) individual bits\n");
fprintf(stderr, " -c clears individual bits\n");
fprintf(stderr, " -g operate on alternate GPIO SMI_EN\n");
fprintf(stderr, " -v show individual bits\n");
fprintf(stderr, " -d dry run\n");
fprintf(stderr, " multiple options are combined in order\n");
fprintf(stderr, " common SMI_EN register bits "
"(not for alternate GPIO):\n");
print_bits(stderr, 0);
exit(2);
}
printf(" attemting to read %s - run with -h for help\n", reg_name);
if (iopl(3) < 0) {
printf(" root permissions required\n");
exit(1);
}
pacc = pci_alloc();
pci_init(pacc);
pci_scan_bus(pacc);
for (dev = pacc->devices; dev; dev = dev->next) {
pci_fill_info(dev, PCI_FILL_IDENT | PCI_FILL_BASES);
if (dev->vendor_id != PCI_VENDOR_ID_INTEL ||
dev->device_class != PCI_CLASS_BRIDGE_ISA ||
dev->dev != LPC_DEV || dev->func != LPC_FUNC)
continue;
pci_lookup_name(pacc, vendor_name, sizeof(vendor_name),
PCI_LOOKUP_VENDOR, dev->vendor_id);
pci_lookup_name(pacc, device_name, sizeof(device_name),
PCI_LOOKUP_DEVICE, dev->vendor_id, dev->device_id);
printf(" SMI-enabled chipset found:\n %s %s (%04x:%04x)\n",
vendor_name, device_name, dev->vendor_id, dev->device_id);
smi_en_addr = get_smi_en_addr(dev, gpio);
if (!gpio) orig_value = inl(smi_en_addr);
else orig_value = inw(smi_en_addr);
printf(" %s register current value:\t0x%0*x\n",
reg_name, reg_width, orig_value);
if (!gpio && verb) print_bits(stdout, &orig_value);
if (set_value) {
new_value = orig_value;
new_value |= set_bits;
new_value &= ~clr_bits;
printf(" %s set %s to value:\t0x%0*x\n",
(dry) ? "(dry run) would" : "attempting to",
reg_name, reg_width, new_value);
if (!gpio && verb) print_bits(stdout, &new_value);
if (!dry) {
if (!gpio) outl(new_value, smi_en_addr);
else outw(new_value, smi_en_addr);
if (!gpio) new_new_value = inl(smi_en_addr);
else new_new_value = inw(smi_en_addr);
printf(" %s register new value:\t0x%0*x\n",
reg_name, reg_width, new_new_value);
if (!gpio && verb) print_bits(stdout, &new_new_value);
}
} else {
printf(" %s register unchanged\n", reg_name);
}
goto out;
}
printf("No SMI-enabled chipset found\n");
out:
pci_cleanup(pacc);
return 0;
}