1/*
2 * Copyright (c) 2016 Cisco and/or its affiliates.
3 * Licensed under the Apache License, Version 2.0 (the "License");
4 * you may not use this file except in compliance with the License.
5 * You may obtain a copy of the License at:
6 *
7 *     http://www.apache.org/licenses/LICENSE-2.0
8 *
9 * Unless required by applicable law or agreed to in writing, software
10 * distributed under the License is distributed on an "AS IS" BASIS,
11 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 * See the License for the specific language governing permissions and
13 * limitations under the License.
14 */
15/*
16 * pci.c: Linux user space PCI bus management.
17 *
18 * Copyright (c) 2008 Eliot Dresselhaus
19 *
20 * Permission is hereby granted, free of charge, to any person obtaining
21 * a copy of this software and associated documentation files (the
22 * "Software"), to deal in the Software without restriction, including
23 * without limitation the rights to use, copy, modify, merge, publish,
24 * distribute, sublicense, and/or sell copies of the Software, and to
25 * permit persons to whom the Software is furnished to do so, subject to
26 * the following conditions:
27 *
28 * The above copyright notice and this permission notice shall be
29 * included in all copies or substantial portions of the Software.
30 *
31 *  THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
32 *  EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
33 *  MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
34 *  NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
35 *  LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
36 *  OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
37 *  WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
38 */
39
40#include <vlib/vlib.h>
41#include <vlib/pci/pci.h>
42#include <vlib/unix/unix.h>
43
44#include <sys/types.h>
45#include <sys/stat.h>
46#include <fcntl.h>
47#include <dirent.h>
48#include <sys/ioctl.h>
49#include <net/if.h>
50#include <linux/ethtool.h>
51#include <linux/sockios.h>
52
53vlib_pci_main_t pci_main;
54
55vlib_pci_device_info_t * __attribute__ ((weak))
56vlib_pci_get_device_info (vlib_main_t * vm, vlib_pci_addr_t * addr,
57			  clib_error_t ** error)
58{
59  if (error)
60    *error = clib_error_return (0, "unsupported");
61  return 0;
62}
63
64vlib_pci_addr_t * __attribute__ ((weak)) vlib_pci_get_all_dev_addrs ()
65{
66  return 0;
67}
68
69static clib_error_t *
70show_pci_fn (vlib_main_t * vm,
71	     unformat_input_t * input, vlib_cli_command_t * cmd)
72{
73  vlib_pci_addr_t *addr = 0, *addrs;
74  int show_all = 0;
75  u8 *s = 0;
76
77  while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT)
78    {
79      if (unformat (input, "all"))
80	show_all = 1;
81      else
82	return clib_error_return (0, "unknown input `%U'",
83				  format_unformat_error, input);
84    }
85
86  vlib_cli_output (vm, "%-13s%-5s%-12s%-13s%-16s%-32s%s",
87		   "Address", "Sock", "VID:PID", "Link Speed", "Driver",
88		   "Product Name", "Vital Product Data");
89
90  addrs = vlib_pci_get_all_dev_addrs ();
91  /* *INDENT-OFF* */
92  vec_foreach (addr, addrs)
93    {
94      vlib_pci_device_info_t *d;
95      d = vlib_pci_get_device_info (vm, addr, 0);
96
97      if (!d)
98        continue;
99
100      if (d->device_class != PCI_CLASS_NETWORK_ETHERNET && !show_all)
101        continue;
102
103        vec_reset_length (s);
104        if (d->numa_node >= 0)
105	  s = format (s, "  %d", d->numa_node);
106
107        vlib_cli_output (vm, "%-13U%-5v%04x:%04x   %-13U%-16s%-32v%U",
108			 format_vlib_pci_addr, addr, s,
109			 d->vendor_id, d->device_id,
110			 format_vlib_pci_link_speed, d,
111			 d->driver_name ? (char *) d->driver_name : "",
112			 d->product_name,
113			 format_vlib_pci_vpd, d->vpd_r, (u8 *) 0);
114	vlib_pci_free_device_info (d);
115    }
116  /* *INDENT-ON* */
117
118  vec_free (s);
119  vec_free (addrs);
120  return 0;
121}
122
123uword
124unformat_vlib_pci_addr (unformat_input_t * input, va_list * args)
125{
126  vlib_pci_addr_t *addr = va_arg (*args, vlib_pci_addr_t *);
127  u32 x[4];
128
129  if (!unformat (input, "%x:%x:%x.%x", &x[0], &x[1], &x[2], &x[3]))
130    return 0;
131
132  addr->domain = x[0];
133  addr->bus = x[1];
134  addr->slot = x[2];
135  addr->function = x[3];
136
137  return 1;
138}
139
140u8 *
141format_vlib_pci_addr (u8 * s, va_list * va)
142{
143  vlib_pci_addr_t *addr = va_arg (*va, vlib_pci_addr_t *);
144  return format (s, "%04x:%02x:%02x.%x", addr->domain, addr->bus,
145		 addr->slot, addr->function);
146}
147
148u8 *
149format_vlib_pci_link_speed (u8 * s, va_list * va)
150{
151  vlib_pci_device_info_t *d = va_arg (*va, vlib_pci_device_info_t *);
152  pcie_config_regs_t *r =
153    pci_config_find_capability (&d->config0, PCI_CAP_ID_PCIE);
154  int width;
155
156  if (!r)
157    return format (s, "unknown");
158
159  width = (r->link_status >> 4) & 0x3f;
160
161  if ((r->link_status & 0xf) == 1)
162    return format (s, "2.5 GT/s x%u", width);
163  if ((r->link_status & 0xf) == 2)
164    return format (s, "5.0 GT/s x%u", width);
165  if ((r->link_status & 0xf) == 3)
166    return format (s, "8.0 GT/s x%u", width);
167  return format (s, "unknown");
168}
169
170u8 *
171format_vlib_pci_vpd (u8 * s, va_list * args)
172{
173  u8 *data = va_arg (*args, u8 *);
174  u8 *id = va_arg (*args, u8 *);
175  u32 indent = format_get_indent (s);
176  char *string_types[] = { "PN", "EC", "SN", "MN", 0 };
177  uword p = 0;
178  int first_line = 1;
179
180  if (vec_len (data) < 3)
181    return s;
182
183  while (p + 3 < vec_len (data))
184    {
185
186      if (data[p] == 0 && data[p + 1] == 0)
187	return s;
188
189      if (p + data[p + 2] > vec_len (data))
190	return s;
191
192      if (id == 0)
193	{
194	  int is_string = 0;
195	  char **c = string_types;
196
197	  while (c[0])
198	    {
199	      if (*(u16 *) & data[p] == *(u16 *) c[0])
200		is_string = 1;
201	      c++;
202	    }
203
204	  if (data[p + 2])
205	    {
206	      if (!first_line)
207		s = format (s, "\n%U", format_white_space, indent);
208	      else
209		{
210		  first_line = 0;
211		  s = format (s, " ");
212		}
213
214	      s = format (s, "%c%c: ", data[p], data[p + 1]);
215	      if (is_string)
216		vec_add (s, data + p + 3, data[p + 2]);
217	      else
218		{
219		  int i;
220		  const int max_bytes = 8;
221		  s = format (s, "0x");
222		  for (i = 0; i < clib_min (data[p + 2], max_bytes); i++)
223		    s = format (s, " %02x", data[p + 3 + i]);
224
225		  if (data[p + 2] > max_bytes)
226		    s = format (s, " ...");
227		}
228	    }
229	}
230      else if (*(u16 *) & data[p] == *(u16 *) id)
231	{
232	  vec_add (s, data + p + 3, data[p + 2]);
233	  return s;
234	}
235
236      p += 3 + data[p + 2];
237    }
238
239  return s;
240}
241
242
243/* *INDENT-OFF* */
244VLIB_CLI_COMMAND (show_pci_command, static) = {
245  .path = "show pci",
246  .short_help = "show pci [all]",
247  .function = show_pci_fn,
248};
249/* *INDENT-ON* */
250
251clib_error_t *
252pci_bus_init (vlib_main_t * vm)
253{
254  vlib_pci_main_t *pm = &pci_main;
255  pm->log_default = vlib_log_register_class ("pci", 0);
256  return 0;
257}
258
259VLIB_INIT_FUNCTION (pci_bus_init);
260
261/*
262 * fd.io coding-style-patch-verification: ON
263 *
264 * Local Variables:
265 * eval: (c-set-style "gnu")
266 * End:
267 */
268