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, &regs) < 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