1 /*
2 * Copyright (C) 2003-2015 FreeIPMI Core Team
3 *
4 * This program is free software: you can redistribute it and/or modify
5 * it under the terms of the GNU General Public License as published by
6 * the Free Software Foundation, either version 3 of the License, or
7 * (at your option) any later version.
8 *
9 * This program is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 * GNU General Public License for more details.
13 *
14 * You should have received a copy of the GNU General Public License
15 * along with this program. If not, see <http://www.gnu.org/licenses/>.
16 *
17 */
18
19 #include <stdio.h>
20 #include <stdlib.h>
21 #ifdef STDC_HEADERS
22 #include <string.h>
23 #endif /* STDC_HEADERS */
24 #include <assert.h>
25 #include <errno.h>
26
27 #include "freeipmi/locate/ipmi-locate.h"
28 #include "freeipmi/driver/ipmi-ssif-driver.h"
29
30 #include "ipmi-locate-defs.h"
31 #include "ipmi-locate-trace.h"
32 #include "ipmi-locate-util.h"
33
34 #include "freeipmi-portability.h"
35
36 #ifdef UNTESTED /* __linux */ /* this code uses the /proc filesystem */
37
38 #define PCI_CLASS_REVISION 0x08 /* High 24 bits are class, low 8 revision */
39 #define PCI_REVISION_ID 0x08 /* Revision ID */
40 #define PCI_CLASS_PROG 0x09 /* Reg. Level Programming Interface */
41 #define PCI_CLASS_DEVICE 0x0a /* Device class */
42
43 #define PCI_BASE_ADDRESS_SPACE 0x01 /* 0 = memory, 1 = I/O */
44 #define PCI_BASE_ADDRESS_SPACE_IO 0x01
45 #define PCI_BASE_ADDRESS_SPACE_MEMORY 0x00
46 #define PCI_BASE_ADDRESS_MEM_TYPE_MASK 0x06
47 #define PCI_BASE_ADDRESS_MEM_TYPE_32 0x00 /* 32 bit address */
48 #define PCI_BASE_ADDRESS_MEM_TYPE_1M 0x02 /* Below 1M [obsolete] */
49 #define PCI_BASE_ADDRESS_MEM_TYPE_64 0x04 /* 64 bit address */
50 #define PCI_BASE_ADDRESS_MEM_PREFETCH 0x08 /* prefetchable? */
51 #define PCI_BASE_ADDRESS_MEM_MASK (~0x0fUL)
52 #define PCI_BASE_ADDRESS_IO_MASK (~0x03UL)
53
54 #define IPMI_CLASS 0xc
55 #define IPMI_SUBCLASS 0x7
56
57 #define PCI_SLOT(devfn) (((devfn) >> 3) & 0x1f)
58 #define PCI_FUNC(devfn) ((devfn) & 0x07)
59
60 enum pci_address_space_type
61 {
62 past_io = PCI_BASE_ADDRESS_SPACE_IO,
63 past_memory = PCI_BASE_ADDRESS_SPACE_MEMORY,
64 };
65
66 typedef enum pci_address_space_type pci_address_space_type_t;
67
68 enum pci_address_mem_type
69 {
70 pamt_32 = PCI_BASE_ADDRESS_MEM_TYPE_32,
71 pamt_1M = PCI_BASE_ADDRESS_MEM_TYPE_1M,
72 pamt_64 = PCI_BASE_ADDRESS_MEM_TYPE_64,
73 };
74
75 typedef enum pci_address_mem_type pci_address_mem_type_t;
76
77 struct pci_class_regs
78 {
79 uint8_t pci_class;
80 uint8_t pci_subclass;
81 uint8_t pci_prog_interface;
82 uint8_t pci_rev;
83 };
84
85 typedef struct pci_class_regs pci_class_regs_t;
86
87 /* pci_get_regs - read the file under /proc that is the image of a device's PCI registers */
88 /* bus = bus number from devices file */
89 /* dev = device number from devices file */
90 /* func = function number from devices file */
91 /* pregs = pointer to structure where to store the important registers */
92 /* return : pregs if successful, otherwise NULL */
93
94 static int
_pci_get_regs(ipmi_locate_ctx_t ctx,uint8_t bus,uint8_t dev,uint16_t func,pci_class_regs_t * pregs)95 _pci_get_regs (ipmi_locate_ctx_t ctx,
96 uint8_t bus,
97 uint8_t dev,
98 uint16_t func,
99 pci_class_regs_t* pregs)
100 {
101 FILE* fp = NULL;
102 size_t n;
103 char fname[128];
104 int rv = -1;
105
106 assert (ctx);
107 assert (ctx->magic == IPMI_LOCATE_CTX_MAGIC);
108 assert (pregs);
109
110 snprintf (fname, sizeof (fname), "/proc/bus/pci/%02x/%02x.%d", bus, dev, func);
111
112 if (!(fp = fopen (fname, "r")))
113 {
114 LOCATE_ERRNO_TO_LOCATE_ERRNUM (ctx, errno);
115 goto cleanup;
116 }
117
118 if (fseek (fp, PCI_CLASS_REVISION, SEEK_SET) < 0)
119 {
120 LOCATE_ERRNO_TO_LOCATE_ERRNUM (ctx, errno);
121 goto cleanup;
122 }
123
124 if ((n = fread (&(pregs->pci_rev), 1, 1, fp)) < 0)
125 {
126 LOCATE_ERRNO_TO_LOCATE_ERRNUM (ctx, errno);
127 goto cleanup;
128 }
129 if (n != 1)
130 {
131 LOCATE_SET_ERRNUM (ctx, IPMI_LOCATE_ERR_SYSTEM_ERROR);
132 goto cleanup;
133 }
134 if ((n = fread (&(pregs->pci_prog_interface), 1, 1, fp)) < 0)
135 {
136 LOCATE_ERRNO_TO_LOCATE_ERRNUM (ctx, errno);
137 goto cleanup;
138 }
139 if (n != 1)
140 {
141 LOCATE_SET_ERRNUM (ctx, IPMI_LOCATE_ERR_SYSTEM_ERROR);
142 goto cleanup;
143 }
144 if ((n = fread (&(pregs->pci_subclass), 1, 1, fp)) < 0)
145 {
146 LOCATE_ERRNO_TO_LOCATE_ERRNUM (ctx, errno);
147 goto cleanup;
148 }
149 if (n != 1)
150 {
151 LOCATE_SET_ERRNUM (ctx, IPMI_LOCATE_ERR_SYSTEM_ERROR);
152 goto cleanup;
153 }
154 if ((n = fread (&(pregs->pci_class), 1, 1, fp)) < 0)
155 {
156 LOCATE_ERRNO_TO_LOCATE_ERRNUM (ctx, errno);
157 goto cleanup;
158 }
159 if (n != 1)
160 {
161 LOCATE_SET_ERRNUM (ctx, IPMI_LOCATE_ERR_SYSTEM_ERROR);
162 goto cleanup;
163 }
164
165 rv = 0;
166 cleanup:
167 fclose (fp);
168 return (rv);
169 }
170
171 #if (__WORDSIZE == 32)
172 #define FORMAT_X64 "%Lx"
173 #elif (__WORDSIZE == 64)
174 #define FORMAT_X64 "%lx"
175 #endif
176
177 /* pci_get_dev_info - probe PCI for IPMI interrupt number and register base */
178 /* type = which interface (KCS, SMIC, BT) */
179 /* pinfo = pointer to information structure filled in by this function */
180
181 int
ipmi_locate_pci_get_device_info(ipmi_locate_ctx_t ctx,ipmi_interface_type_t type,struct ipmi_locate_info * info)182 ipmi_locate_pci_get_device_info (ipmi_locate_ctx_t ctx,
183 ipmi_interface_type_t type,
184 struct ipmi_locate_info *info)
185 {
186 unsigned dfn;
187 unsigned vendor;
188 unsigned bus;
189 unsigned dev;
190 unsigned func;
191 unsigned irq;
192 uint64_t base_address[6];
193 char buf[512];
194 FILE* fp_devices;
195 int items;
196 unsigned int i;
197 struct ipmi_locate_info linfo;
198 int rv = -1;
199
200 if (!ctx || ctx->magic != IPMI_LOCATE_CTX_MAGIC)
201 {
202 ERR_TRACE (ipmi_locate_ctx_errormsg (ctx), ipmi_locate_ctx_errnum (ctx));
203 return (-1);
204 }
205
206 if (!IPMI_INTERFACE_TYPE_VALID (type) || !info)
207 {
208 LOCATE_SET_ERRNUM (ctx, IPMI_LOCATE_ERR_PARAMETERS);
209 return (-1);
210 }
211
212 memset (&linfo, '\0', sizeof (struct ipmi_locate_info));
213 linfo.interface_type = type;
214 if (type == IPMI_INTERFACE_SSIF)
215 {
216 strncpy (linfo.driver_device, IPMI_DEFAULT_I2C_DEVICE, IPMI_LOCATE_PATH_MAX);
217 linfo.driver_device[IPMI_LOCATE_PATH_MAX - 1] = '\0';
218 }
219
220 if (!(fp_devices = fopen ("/proc/bus/pci/devices", "r")))
221 {
222 LOCATE_ERRNO_TO_LOCATE_ERRNUM (ctx, errno);
223 goto cleanup;
224 }
225
226 while (fgets (buf, sizeof (buf), fp_devices) != NULL)
227 {
228 pci_class_regs_t regs;
229
230 items = sscanf (buf, "%x %x %x " FORMAT_X64 " " FORMAT_X64 " " FORMAT_X64 " " FORMAT_X64 " " FORMAT_X64 " " FORMAT_X64,
231 &dfn, &vendor, &irq,
232 &base_address[0], &base_address[1], &base_address[2], &base_address[3], &base_address[4], &base_address[5]);
233 linfo.intr_num = (uint16_t)irq;
234
235 if (items != 9)
236 {
237 LOCATE_SET_ERRNUM (ctx, IPMI_LOCATE_ERR_SYSTEM_ERROR);
238 goto cleanup;
239 }
240 bus = dfn >> 8U;
241 dev = PCI_SLOT (dfn & 0xff);
242 func = PCI_FUNC (dfn & 0xff);
243
244 if (_pci_get_regs (ctx, bus, dev, func, ®s) < 0)
245 goto cleanup;
246
247 if (regs.pci_class != IPMI_CLASS ||
248 regs.pci_subclass != IPMI_SUBCLASS ||
249 regs.pci_prog_interface + 1 != type)
250 continue;
251
252 for (i = 0; i < 6; i++)
253 {
254 if (base_address[i] == 0 || base_address[i] == ~0)
255 continue;
256
257 switch (base_address[i] & PCI_BASE_ADDRESS_SPACE)
258 {
259 case past_io:
260 linfo.address_space_id = IPMI_ADDRESS_SPACE_ID_SYSTEM_MEMORY;
261 linfo.driver_address = base_address[i] & ~PCI_BASE_ADDRESS_IO_MASK;
262 memcpy (info, &linfo, sizeof (struct ipmi_locate_info));
263 rv = 0;
264 goto cleanup;
265
266 case past_memory:
267 linfo.address_space_id = IPMI_ADDRESS_SPACE_ID_SYSTEM_IO;
268 linfo.driver_address = base_address[i] & ~PCI_BASE_ADDRESS_MEM_MASK;
269 memcpy (info, &linfo, sizeof (struct ipmi_locate_info));
270 rv = 0;
271 goto cleanup;
272 }
273 }
274 }
275
276 cleanup:
277 if (fp_devices)
278 fclose (fp_devices);
279 return (rv);
280 }
281
282 #else /* __linux */
283
284 int
ipmi_locate_pci_get_device_info(ipmi_locate_ctx_t ctx,ipmi_interface_type_t type,struct ipmi_locate_info * info)285 ipmi_locate_pci_get_device_info (ipmi_locate_ctx_t ctx,
286 ipmi_interface_type_t type,
287 struct ipmi_locate_info *info)
288 {
289 if (!ctx || ctx->magic != IPMI_LOCATE_CTX_MAGIC)
290 {
291 ERR_TRACE (ipmi_locate_ctx_errormsg (ctx), ipmi_locate_ctx_errnum (ctx));
292 return (-1);
293 }
294
295 if (!IPMI_INTERFACE_TYPE_VALID (type) || !info)
296 {
297 LOCATE_SET_ERRNUM (ctx, IPMI_LOCATE_ERR_PARAMETERS);
298 return (-1);
299 }
300
301 LOCATE_SET_ERRNUM (ctx, IPMI_LOCATE_ERR_SYSTEM_ERROR);
302 return (-1);
303 }
304
305 #endif /* !__linux */
306