diff options
author | Damjan Marion <damarion@cisco.com> | 2016-04-13 18:03:20 +0200 |
---|---|---|
committer | Gerrit Code Review <gerrit@fd.io> | 2016-04-14 23:29:13 +0000 |
commit | a42cd34f106869d5afc26f5b5db7e0cb2f73ae97 (patch) | |
tree | 0c2b31263d7d77a57db3b56dc0736a1d8ef96e07 /vlib | |
parent | 550b5f62528c435e4b9d41729f1d92e8ed9e161a (diff) |
Rework of DPDK PCI device uio driver binding process
This is complete rework of DPDK PCI initialization. It drops
previous scheme where lspci/route/awk/sed are used and instead
sysfs is solely used for discovering Ethernet PCI devices. Criteria
for blacklisting device is changed from exsiting routing table entry
to simple interface state obtained by SIOCGIFFLAGS ioctl().
It checks for IFF_UP flag, so as long as interface is declared
up and even when carrier is down interface will be blacklisted.
Change-Id: I59961ddcf1c19c728934e7fe746f343983741bf1
Signed-off-by: Damjan Marion <damarion@cisco.com>
Diffstat (limited to 'vlib')
-rw-r--r-- | vlib/Makefile.am | 6 | ||||
-rw-r--r-- | vlib/vlib/pci/pci.h | 176 | ||||
-rw-r--r-- | vlib/vlib/pci/pci_config.h | 727 | ||||
-rw-r--r-- | vlib/vlib/unix/pci.c | 235 | ||||
-rw-r--r-- | vlib/vlib/unix/pci.h | 13 | ||||
-rw-r--r-- | vlib/vlib/unix/unix.h | 14 | ||||
-rw-r--r-- | vlib/vlib/unix/util.c | 165 |
7 files changed, 1227 insertions, 109 deletions
diff --git a/vlib/Makefile.am b/vlib/Makefile.am index b38c843fc5f..17b23dfd1a8 100644 --- a/vlib/Makefile.am +++ b/vlib/Makefile.am @@ -65,6 +65,8 @@ nobase_include_HEADERS = \ vlib/node_funcs.h \ vlib/node.h \ vlib/physmem.h \ + vlib/pci/pci.h \ + vlib/pci/pci_config.h \ vlib/threads.h \ vlib/trace_funcs.h \ vlib/trace.h \ @@ -81,12 +83,14 @@ libvlib_unix_la_SOURCES = \ vlib/unix/plugin.c \ vlib/unix/plugin.h \ vlib/unix/physmem.c \ - vlib/unix/unix.h + vlib/unix/pci.c \ + vlib/unix/util.c nobase_include_HEADERS += \ vlib/unix/cj.h \ vlib/unix/mc_socket.h \ vlib/unix/physmem.h \ + vlib/unix/pci.h \ vlib/unix/plugin.h \ vlib/unix/unix.h diff --git a/vlib/vlib/pci/pci.h b/vlib/vlib/pci/pci.h new file mode 100644 index 00000000000..737e28ea52a --- /dev/null +++ b/vlib/vlib/pci/pci.h @@ -0,0 +1,176 @@ +/* + * Copyright (c) 2016 Cisco and/or its affiliates. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +/* + * pci.h: PCI definitions. + * + * Copyright (c) 2008 Eliot Dresselhaus + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE + * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION + * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ + +#ifndef included_vlib_pci_h +#define included_vlib_pci_h + +#include <vlib/vlib.h> +#include <vlib/pci/pci_config.h> + +typedef CLIB_PACKED (union { + struct { + u16 domain; + u8 bus; + u8 slot:5; + u8 function:3; + }; + u32 as_u32; +}) vlib_pci_addr_t; + +typedef struct { + /* Operating system handle for this device. */ + uword os_handle; + + vlib_pci_addr_t bus_address; + + /* First 64 bytes of configuration space. */ + union { + pci_config_type0_regs_t config0; + pci_config_type1_regs_t config1; + u8 config_data[256]; + }; +} vlib_pci_device_t; + +typedef struct { + u16 vendor_id, device_id; +} pci_device_id_t; + +typedef struct _pci_device_registration { + /* Driver init function. */ + clib_error_t * (* init_function) (vlib_main_t * vm, vlib_pci_device_t * dev); + + char const *kernel_driver; + u8 kernel_driver_running; + + /* List of registrations */ + struct _pci_device_registration * next_registration; + + /* Vendor/device ids supported by this driver. */ + pci_device_id_t supported_devices[]; +} pci_device_registration_t; + +#define PCI_REGISTER_DEVICE(x,...) \ + __VA_ARGS__ pci_device_registration_t x; \ +static void __vlib_add_pci_device_registration_##x (void) \ + __attribute__((__constructor__)) ; \ +static void __vlib_add_pci_device_registration_##x (void) \ +{ \ + linux_pci_main_t * lpm = vlib_unix_get_main(); \ + x.next_registration = lpm->pci_device_registrations; \ + lpm->pci_device_registrations = &x; \ +} \ +__VA_ARGS__ pci_device_registration_t x + + +/* Configuration space read/write. */ +clib_error_t * +os_read_write_pci_config (uword os_handle, + vlib_read_or_write_t read_or_write, + uword address, + void * data, + u32 n_bytes); + +#define _(t) \ +static inline clib_error_t * \ +os_read_pci_config_##t (uword os_handle, uword address, t * data) \ +{ \ + return os_read_write_pci_config (os_handle, VLIB_READ, \ + address, data, sizeof (data[0])); \ +} + +_ (u32); +_ (u16); +_ (u8); + +#undef _ + +#define _(t) \ +static inline clib_error_t * \ +os_write_pci_config_##t (uword os_handle, uword address, t * data) \ +{ \ + return os_read_write_pci_config (os_handle, VLIB_WRITE, \ + address, data, sizeof (data[0])); \ +} + +_ (u32); +_ (u16); +_ (u8); + +#undef _ + +clib_error_t * +os_map_pci_resource (uword os_handle, u32 resource, void ** result); + +clib_error_t * +os_map_pci_resource_fixed (uword os_handle, u32 resource, u8 * addr, + void ** result); + +/* Free's device. */ +void os_free_pci_device (uword os_handle); + +void os_add_pci_disable_interrupts_reg (uword os_handle, u32 resource, u32 reg_offset, u32 reg_value); + +format_function_t format_os_pci_handle; + +static inline uword +unformat_vlib_pci_addr (unformat_input_t * input, va_list * args) +{ + vlib_pci_addr_t * addr = va_arg (* args, vlib_pci_addr_t *); + u32 x[4]; + + if (!unformat (input, "%x:%x:%x.%x", &x[0], &x[1], &x[2], &x[3])) + return 0; + + addr->domain = x[0]; + addr->bus = x[1]; + addr->slot = x[2]; + addr->function = x[3]; + + return 1; +} + +static inline u8 * +format_vlib_pci_addr (u8 * s, va_list * va) +{ + vlib_pci_addr_t * addr = va_arg (* va, vlib_pci_addr_t *); + return format (s, "%04x:%02x:%02x.%x", addr->domain, addr->bus, + addr->slot, addr->function); +} + +#endif /* included_vlib_pci_h */ diff --git a/vlib/vlib/pci/pci_config.h b/vlib/vlib/pci/pci_config.h new file mode 100644 index 00000000000..38215d82e95 --- /dev/null +++ b/vlib/vlib/pci/pci_config.h @@ -0,0 +1,727 @@ +/* + * Copyright (c) 2016 Cisco and/or its affiliates. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +/* + * pci.h: PCI definitions. + * + * Copyright (c) 2008 Eliot Dresselhaus + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE + * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION + * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ + +#ifndef included_vlib_pci_config_h +#define included_vlib_pci_config_h + +#include <vppinfra/byte_order.h> +#include <vppinfra/error.h> + +typedef enum { + PCI_CLASS_NOT_DEFINED = 0x0000, + PCI_CLASS_NOT_DEFINED_VGA = 0x0001, + + PCI_CLASS_STORAGE_SCSI = 0x0100, + PCI_CLASS_STORAGE_IDE = 0x0101, + PCI_CLASS_STORAGE_FLOPPY = 0x0102, + PCI_CLASS_STORAGE_IPI = 0x0103, + PCI_CLASS_STORAGE_RAID = 0x0104, + PCI_CLASS_STORAGE_OTHER = 0x0180, + PCI_CLASS_STORAGE = 0x0100, + + PCI_CLASS_NETWORK_ETHERNET = 0x0200, + PCI_CLASS_NETWORK_TOKEN_RING = 0x0201, + PCI_CLASS_NETWORK_FDDI = 0x0202, + PCI_CLASS_NETWORK_ATM = 0x0203, + PCI_CLASS_NETWORK_OTHER = 0x0280, + PCI_CLASS_NETWORK = 0x0200, + + PCI_CLASS_DISPLAY_VGA = 0x0300, + PCI_CLASS_DISPLAY_XGA = 0x0301, + PCI_CLASS_DISPLAY_3D = 0x0302, + PCI_CLASS_DISPLAY_OTHER = 0x0380, + PCI_CLASS_DISPLAY = 0x0300, + + PCI_CLASS_MULTIMEDIA_VIDEO = 0x0400, + PCI_CLASS_MULTIMEDIA_AUDIO = 0x0401, + PCI_CLASS_MULTIMEDIA_PHONE = 0x0402, + PCI_CLASS_MULTIMEDIA_OTHER = 0x0480, + PCI_CLASS_MULTIMEDIA = 0x0400, + + PCI_CLASS_MEMORY_RAM = 0x0500, + PCI_CLASS_MEMORY_FLASH = 0x0501, + PCI_CLASS_MEMORY_OTHER = 0x0580, + PCI_CLASS_MEMORY = 0x0500, + + PCI_CLASS_BRIDGE_HOST = 0x0600, + PCI_CLASS_BRIDGE_ISA = 0x0601, + PCI_CLASS_BRIDGE_EISA = 0x0602, + PCI_CLASS_BRIDGE_MC = 0x0603, + PCI_CLASS_BRIDGE_PCI = 0x0604, + PCI_CLASS_BRIDGE_PCMCIA = 0x0605, + PCI_CLASS_BRIDGE_NUBUS = 0x0606, + PCI_CLASS_BRIDGE_CARDBUS = 0x0607, + PCI_CLASS_BRIDGE_RACEWAY = 0x0608, + PCI_CLASS_BRIDGE_OTHER = 0x0680, + PCI_CLASS_BRIDGE = 0x0600, + + PCI_CLASS_COMMUNICATION_SERIAL = 0x0700, + PCI_CLASS_COMMUNICATION_PARALLEL = 0x0701, + PCI_CLASS_COMMUNICATION_MULTISERIAL = 0x0702, + PCI_CLASS_COMMUNICATION_MODEM = 0x0703, + PCI_CLASS_COMMUNICATION_OTHER = 0x0780, + PCI_CLASS_COMMUNICATION = 0x0700, + + PCI_CLASS_SYSTEM_PIC = 0x0800, + PCI_CLASS_SYSTEM_DMA = 0x0801, + PCI_CLASS_SYSTEM_TIMER = 0x0802, + PCI_CLASS_SYSTEM_RTC = 0x0803, + PCI_CLASS_SYSTEM_PCI_HOTPLUG = 0x0804, + PCI_CLASS_SYSTEM_OTHER = 0x0880, + PCI_CLASS_SYSTEM = 0x0800, + + PCI_CLASS_INPUT_KEYBOARD = 0x0900, + PCI_CLASS_INPUT_PEN = 0x0901, + PCI_CLASS_INPUT_MOUSE = 0x0902, + PCI_CLASS_INPUT_SCANNER = 0x0903, + PCI_CLASS_INPUT_GAMEPORT = 0x0904, + PCI_CLASS_INPUT_OTHER = 0x0980, + PCI_CLASS_INPUT = 0x0900, + + PCI_CLASS_DOCKING_GENERIC = 0x0a00, + PCI_CLASS_DOCKING_OTHER = 0x0a80, + PCI_CLASS_DOCKING = 0x0a00, + + PCI_CLASS_PROCESSOR_386 = 0x0b00, + PCI_CLASS_PROCESSOR_486 = 0x0b01, + PCI_CLASS_PROCESSOR_PENTIUM = 0x0b02, + PCI_CLASS_PROCESSOR_ALPHA = 0x0b10, + PCI_CLASS_PROCESSOR_POWERPC = 0x0b20, + PCI_CLASS_PROCESSOR_MIPS = 0x0b30, + PCI_CLASS_PROCESSOR_CO = 0x0b40, + PCI_CLASS_PROCESSOR = 0x0b00, + + PCI_CLASS_SERIAL_FIREWIRE = 0x0c00, + PCI_CLASS_SERIAL_ACCESS = 0x0c01, + PCI_CLASS_SERIAL_SSA = 0x0c02, + PCI_CLASS_SERIAL_USB = 0x0c03, + PCI_CLASS_SERIAL_FIBER = 0x0c04, + PCI_CLASS_SERIAL_SMBUS = 0x0c05, + PCI_CLASS_SERIAL = 0x0c00, + + PCI_CLASS_INTELLIGENT_I2O = 0x0e00, + PCI_CLASS_INTELLIGENT = 0x0e00, + + PCI_CLASS_SATELLITE_TV = 0x0f00, + PCI_CLASS_SATELLITE_AUDIO = 0x0f01, + PCI_CLASS_SATELLITE_VOICE = 0x0f03, + PCI_CLASS_SATELLITE_DATA = 0x0f04, + PCI_CLASS_SATELLITE = 0x0f00, + + PCI_CLASS_CRYPT_NETWORK = 0x1000, + PCI_CLASS_CRYPT_ENTERTAINMENT = 0x1001, + PCI_CLASS_CRYPT_OTHER = 0x1080, + PCI_CLASS_CRYPT = 0x1000, + + PCI_CLASS_SP_DPIO = 0x1100, + PCI_CLASS_SP_OTHER = 0x1180, + PCI_CLASS_SP = 0x1100, +} pci_device_class_t; + +static inline pci_device_class_t +pci_device_class_base (pci_device_class_t c) +{ return c &~ 0xff; } + +/* + * Under PCI, each device has 256 bytes of configuration address space, + * of which the first 64 bytes are standardized as follows: + */ +typedef struct { + u16 vendor_id; + u16 device_id; + + u16 command; +#define PCI_COMMAND_IO (1 << 0) /* Enable response in I/O space */ +#define PCI_COMMAND_MEMORY (1 << 1) /* Enable response in Memory space */ +#define PCI_COMMAND_BUS_MASTER (1 << 2) /* Enable bus mastering */ +#define PCI_COMMAND_SPECIAL (1 << 3) /* Enable response to special cycles */ +#define PCI_COMMAND_WRITE_INVALIDATE (1 << 4) /* Use memory write and invalidate */ +#define PCI_COMMAND_VGA_PALETTE_SNOOP (1 << 5) +#define PCI_COMMAND_PARITY (1 << 6) +#define PCI_COMMAND_WAIT (1 << 7) /* Enable address/data stepping */ +#define PCI_COMMAND_SERR (1 << 8) /* Enable SERR */ +#define PCI_COMMAND_BACK_TO_BACK_WRITE (1 << 9) +#define PCI_COMMAND_INTX_DISABLE (1 << 10) /* INTx Emulation Disable */ + + u16 status; +#define PCI_STATUS_INTX_PENDING (1 << 3) +#define PCI_STATUS_CAPABILITY_LIST (1 << 4) +#define PCI_STATUS_66MHZ (1 << 5) /* Support 66 Mhz PCI 2.1 bus */ +#define PCI_STATUS_UDF (1 << 6) /* Support User Definable Features (obsolete) */ +#define PCI_STATUS_BACK_TO_BACK_WRITE (1 << 7) /* Accept fast-back to back */ +#define PCI_STATUS_PARITY_ERROR (1 << 8) /* Detected parity error */ +#define PCI_STATUS_DEVSEL_GET(x) ((x >> 9) & 3) /* DEVSEL timing */ +#define PCI_STATUS_DEVSEL_FAST (0 << 9) +#define PCI_STATUS_DEVSEL_MEDIUM (1 << 9) +#define PCI_STATUS_DEVSEL_SLOW (2 << 9) +#define PCI_STATUS_SIG_TARGET_ABORT (1 << 11) /* Set on target abort */ +#define PCI_STATUS_REC_TARGET_ABORT (1 << 12) /* Master ack of " */ +#define PCI_STATUS_REC_MASTER_ABORT (1 << 13) /* Set on master abort */ +#define PCI_STATUS_SIG_SYSTEM_ERROR (1 << 14) /* Set when we drive SERR */ +#define PCI_STATUS_DETECTED_PARITY_ERROR (1 << 15) + + u8 revision_id; + u8 programming_interface_class; /* Reg. Level Programming Interface */ + + pci_device_class_t device_class : 16; + + u8 cache_size; + u8 latency_timer; + + u8 header_type; +#define PCI_HEADER_TYPE_NORMAL 0 +#define PCI_HEADER_TYPE_BRIDGE 1 +#define PCI_HEADER_TYPE_CARDBUS 2 + + u8 bist; +#define PCI_BIST_CODE_MASK 0x0f /* Return result */ +#define PCI_BIST_START 0x40 /* 1 to start BIST, 2 secs or less */ +#define PCI_BIST_CAPABLE 0x80 /* 1 if BIST capable */ +} pci_config_header_t; + +/* Byte swap config header. */ +always_inline void +pci_config_header_little_to_host (pci_config_header_t * r) +{ + if (! CLIB_ARCH_IS_BIG_ENDIAN) + return; +#define _(f,t) r->f = clib_byte_swap_##t (r->f) + _ (vendor_id, u16); + _ (device_id, u16); + _ (command, u16); + _ (status, u16); + _ (device_class, u16); +#undef _ +} + +/* Header type 0 (normal devices) */ +typedef struct { + pci_config_header_t header; + + /* + * Base addresses specify locations in memory or I/O space. + * Decoded size can be determined by writing a value of + * 0xffffffff to the register, and reading it back. Only + * 1 bits are decoded. + */ + u32 base_address[6]; + + u16 cardbus_cis; + + u16 subsystem_vendor_id; + u16 subsystem_id; + + u32 rom_address; +#define PCI_ROM_ADDRESS 0x30 /* Bits 31..11 are address, 10..1 reserved */ +#define PCI_ROM_ADDRESS_ENABLE 0x01 +#define PCI_ROM_ADDRESS_MASK (~0x7ffUL) + + u8 first_capability_offset; + CLIB_PAD_FROM_TO (0x35, 0x3c); + + u8 interrupt_line; + u8 interrupt_pin; + u8 min_grant; + u8 max_latency; + + u8 capability_data[0]; +} pci_config_type0_regs_t; + +always_inline void +pci_config_type0_little_to_host (pci_config_type0_regs_t * r) +{ + int i; + if (! CLIB_ARCH_IS_BIG_ENDIAN) + return; + pci_config_header_little_to_host (&r->header); +#define _(f,t) r->f = clib_byte_swap_##t (r->f) + for (i = 0; i < ARRAY_LEN (r->base_address); i++) + _ (base_address[i], u32); + _ (cardbus_cis, u16); + _ (subsystem_vendor_id, u16); + _ (subsystem_id, u16); + _ (rom_address, u32); +#undef _ +} + +/* Header type 1 (PCI-to-PCI bridges) */ +typedef struct { + pci_config_header_t header; + + u32 base_address[2]; + + /* Primary/secondary bus number. */ + u8 primary_bus; + u8 secondary_bus; + + /* Highest bus number behind the bridge */ + u8 subordinate_bus; + + u8 secondary_bus_latency_timer; + + /* I/O range behind bridge. */ + u8 io_base, io_limit; + + /* Secondary status register, only bit 14 used */ + u16 secondary_status; + + /* Memory range behind bridge in units of 64k bytes. */ + u16 memory_base, memory_limit; +#define PCI_MEMORY_RANGE_TYPE_MASK 0x0fUL +#define PCI_MEMORY_RANGE_MASK (~0x0fUL) + + u16 prefetchable_memory_base, prefetchable_memory_limit; +#define PCI_PREF_RANGE_TYPE_MASK 0x0fUL +#define PCI_PREF_RANGE_TYPE_32 0x00 +#define PCI_PREF_RANGE_TYPE_64 0x01 +#define PCI_PREF_RANGE_MASK (~0x0fUL) + + u32 prefetchable_memory_base_upper_32bits; + u32 prefetchable_memory_limit_upper_32bits; + u16 io_base_upper_16bits; + u16 io_limit_upper_16bits; + + /* Same as for type 0. */ + u8 capability_list_offset; + CLIB_PAD_FROM_TO (0x35, 0x37); + + u32 rom_address; + CLIB_PAD_FROM_TO (0x3c, 0x3e); + + u16 bridge_control; +#define PCI_BRIDGE_CTL_PARITY 0x01 /* Enable parity detection on secondary interface */ +#define PCI_BRIDGE_CTL_SERR 0x02 /* The same for SERR forwarding */ +#define PCI_BRIDGE_CTL_NO_ISA 0x04 /* Disable bridging of ISA ports */ +#define PCI_BRIDGE_CTL_VGA 0x08 /* Forward VGA addresses */ +#define PCI_BRIDGE_CTL_MASTER_ABORT 0x20 /* Report master aborts */ +#define PCI_BRIDGE_CTL_BUS_RESET 0x40 /* Secondary bus reset */ +#define PCI_BRIDGE_CTL_FAST_BACK 0x80 /* Fast Back2Back enabled on secondary interface */ + + u8 capability_data[0]; +} pci_config_type1_regs_t; + +always_inline void +pci_config_type1_little_to_host (pci_config_type1_regs_t * r) +{ + int i; + if (! CLIB_ARCH_IS_BIG_ENDIAN) + return; + pci_config_header_little_to_host (&r->header); +#define _(f,t) r->f = clib_byte_swap_##t (r->f) + for (i = 0; i < ARRAY_LEN (r->base_address); i++) + _ (base_address[i], u32); + _ (secondary_status, u16); + _ (memory_base, u16); + _ (memory_limit, u16); + _ (prefetchable_memory_base, u16); + _ (prefetchable_memory_limit, u16); + _ (prefetchable_memory_base_upper_32bits, u32); + _ (prefetchable_memory_limit_upper_32bits, u32); + _ (io_base_upper_16bits, u16); + _ (io_limit_upper_16bits, u16); + _ (rom_address, u32); + _ (bridge_control, u16); +#undef _ +} + +/* Capabilities. */ +typedef enum pci_capability_type { + /* Power Management */ + PCI_CAP_ID_PM = 1, + + /* Accelerated Graphics Port */ + PCI_CAP_ID_AGP = 2, + + /* Vital Product Data */ + PCI_CAP_ID_VPD = 3, + + /* Slot Identification */ + PCI_CAP_ID_SLOTID = 4, + + /* Message Signalled Interrupts */ + PCI_CAP_ID_MSI = 5, + + /* CompactPCI HotSwap */ + PCI_CAP_ID_CHSWP = 6, + + /* PCI-X */ + PCI_CAP_ID_PCIX = 7, + + /* Hypertransport. */ + PCI_CAP_ID_HYPERTRANSPORT = 8, + + /* PCI Standard Hot-Plug Controller */ + PCI_CAP_ID_SHPC = 0xc, + + /* PCI Express */ + PCI_CAP_ID_PCIE = 0x10, + + /* MSI-X */ + PCI_CAP_ID_MSIX = 0x11, +} pci_capability_type_t; + +/* Common header for capabilities. */ +typedef CLIB_PACKED (struct { + enum pci_capability_type type : 8; + + u8 next_offset; +}) pci_capability_regs_t; + +always_inline void * +pci_config_find_capability (pci_config_type0_regs_t * t, int cap_type) +{ + pci_capability_regs_t * c; + u32 next_offset; + u32 ttl = 48; + + if (! (t->header.status & PCI_STATUS_CAPABILITY_LIST)) + return 0; + + next_offset = t->first_capability_offset; + while (ttl-- && next_offset >= 0x40) + { + c = (void *) t + (next_offset &~ 3); + if (c->type == 0xff) + break; + if (c->type == cap_type) + return c; + next_offset = c->next_offset; + } + return 0; +} + +/* Power Management Registers */ +typedef CLIB_PACKED (struct { + pci_capability_regs_t header; + + u16 capabilities; +#define PCI_PM_CAP_VER_MASK 0x0007 /* Version */ +#define PCI_PM_CAP_PME_CLOCK 0x0008 /* PME clock required */ +#define PCI_PM_CAP_RESERVED 0x0010 /* Reserved field */ +#define PCI_PM_CAP_DSI 0x0020 /* Device specific initialization */ +#define PCI_PM_CAP_AUX_POWER 0x01C0 /* Auxilliary power support mask */ +#define PCI_PM_CAP_D1 0x0200 /* D1 power state support */ +#define PCI_PM_CAP_D2 0x0400 /* D2 power state support */ +#define PCI_PM_CAP_PME 0x0800 /* PME pin supported */ +#define PCI_PM_CAP_PME_MASK 0xF800 /* PME Mask of all supported states */ +#define PCI_PM_CAP_PME_D0 0x0800 /* PME# from D0 */ +#define PCI_PM_CAP_PME_D1 0x1000 /* PME# from D1 */ +#define PCI_PM_CAP_PME_D2 0x2000 /* PME# from D2 */ +#define PCI_PM_CAP_PME_D3 0x4000 /* PME# from D3 (hot) */ +#define PCI_PM_CAP_PME_D3cold 0x8000 /* PME# from D3 (cold) */ + + u16 control; +#define PCI_PM_CTRL_STATE_MASK 0x0003 /* Current power state (D0 to D3) */ +#define PCI_PM_CTRL_PME_ENABLE 0x0100 /* PME pin enable */ +#define PCI_PM_CTRL_DATA_SEL_MASK 0x1e00 /* Data select (??) */ +#define PCI_PM_CTRL_DATA_SCALE_MASK 0x6000 /* Data scale (??) */ +#define PCI_PM_CTRL_PME_STATUS 0x8000 /* PME pin status */ + + u8 extensions; +#define PCI_PM_PPB_B2_B3 0x40 /* Stop clock when in D3hot (??) */ +#define PCI_PM_BPCC_ENABLE 0x80 /* Bus power/clock control enable (??) */ + + u8 data; +}) pci_power_management_regs_t; + +/* AGP registers */ +typedef CLIB_PACKED (struct { + pci_capability_regs_t header; + u8 version; + u8 rest_of_capability_flags; + + u32 status; + u32 command; + /* Command & status common bits. */ +#define PCI_AGP_RQ_MASK 0xff000000 /* Maximum number of requests - 1 */ +#define PCI_AGP_SBA 0x0200 /* Sideband addressing supported */ +#define PCI_AGP_64BIT 0x0020 /* 64-bit addressing supported */ +#define PCI_AGP_ALLOW_TRANSACTIONS 0x0100 /* Allow processing of AGP transactions */ +#define PCI_AGP_FW 0x0010 /* FW transfers supported/forced */ +#define PCI_AGP_RATE4 0x0004 /* 4x transfer rate supported */ +#define PCI_AGP_RATE2 0x0002 /* 2x transfer rate supported */ +#define PCI_AGP_RATE1 0x0001 /* 1x transfer rate supported */ +}) pci_agp_regs_t; + +/* Vital Product Data */ +typedef CLIB_PACKED (struct { + pci_capability_regs_t header; + u16 address; +#define PCI_VPD_ADDR_MASK 0x7fff /* Address mask */ +#define PCI_VPD_ADDR_F 0x8000 /* Write 0, 1 indicates completion */ + + u32 data; +}) pci_vpd_regs_t; + +/* Slot Identification */ +typedef CLIB_PACKED (struct { + pci_capability_regs_t header; + u8 esr; +#define PCI_SID_ESR_NSLOTS 0x1f /* Number of expansion slots available */ +#define PCI_SID_ESR_FIC 0x20 /* First In Chassis Flag */ + u8 chassis; +}) pci_sid_regs_t; + +/* Message Signalled Interrupts registers */ +typedef CLIB_PACKED (struct { + pci_capability_regs_t header; + + u16 flags; +#define PCI_MSI_FLAGS_ENABLE (1 << 0) /* MSI feature enabled */ +#define PCI_MSI_FLAGS_GET_MAX_QUEUE_SIZE(x) ((x >> 1) & 0x7) +#define PCI_MSI_FLAGS_MAX_QUEUE_SIZE(x) (((x) & 0x7) << 1) +#define PCI_MSI_FLAGS_GET_QUEUE_SIZE(x) ((x >> 4) & 0x7) +#define PCI_MSI_FLAGS_QUEUE_SIZE(x) (((x) & 0x7) << 4) +#define PCI_MSI_FLAGS_64BIT (1 << 7) /* 64-bit addresses allowed */ +#define PCI_MSI_FLAGS_MASKBIT (1 << 8) /* 64-bit mask bits allowed */ + + u32 address; + u32 data; + u32 mask_bits; +}) pci_msi32_regs_t; + +typedef CLIB_PACKED (struct { + pci_capability_regs_t header; + u16 flags; + u32 address[2]; + u32 data; + u32 mask_bits; +}) pci_msi64_regs_t; + +/* CompactPCI Hotswap Register */ +typedef CLIB_PACKED (struct { + pci_capability_regs_t header; + + u16 control_status; +#define PCI_CHSWP_DHA 0x01 /* Device Hiding Arm */ +#define PCI_CHSWP_EIM 0x02 /* ENUM# Signal Mask */ +#define PCI_CHSWP_PIE 0x04 /* Pending Insert or Extract */ +#define PCI_CHSWP_LOO 0x08 /* LED On / Off */ +#define PCI_CHSWP_PI 0x30 /* Programming Interface */ +#define PCI_CHSWP_EXT 0x40 /* ENUM# status - extraction */ +#define PCI_CHSWP_INS 0x80 /* ENUM# status - insertion */ +}) pci_chswp_regs_t; + +/* PCIX registers */ +typedef CLIB_PACKED (struct { + pci_capability_regs_t header; + + u16 command; +#define PCIX_CMD_DPERR_E 0x0001 /* Data Parity Error Recovery Enable */ +#define PCIX_CMD_ERO 0x0002 /* Enable Relaxed Ordering */ +#define PCIX_CMD_MAX_READ 0x000c /* Max Memory Read Byte Count */ +#define PCIX_CMD_MAX_SPLIT 0x0070 /* Max Outstanding Split Transactions */ +#define PCIX_CMD_VERSION(x) (((x) >> 12) & 3) /* Version */ + + u32 status; +#define PCIX_STATUS_DEVFN 0x000000ff /* A copy of devfn */ +#define PCIX_STATUS_BUS 0x0000ff00 /* A copy of bus nr */ +#define PCIX_STATUS_64BIT 0x00010000 /* 64-bit device */ +#define PCIX_STATUS_133MHZ 0x00020000 /* 133 MHz capable */ +#define PCIX_STATUS_SPL_DISC 0x00040000 /* Split Completion Discarded */ +#define PCIX_STATUS_UNX_SPL 0x00080000 /* Unexpected Split Completion */ +#define PCIX_STATUS_COMPLEX 0x00100000 /* Device Complexity */ +#define PCIX_STATUS_MAX_READ 0x00600000 /* Designed Max Memory Read Count */ +#define PCIX_STATUS_MAX_SPLIT 0x03800000 /* Designed Max Outstanding Split Transactions */ +#define PCIX_STATUS_MAX_CUM 0x1c000000 /* Designed Max Cumulative Read Size */ +#define PCIX_STATUS_SPL_ERR 0x20000000 /* Rcvd Split Completion Error Msg */ +#define PCIX_STATUS_266MHZ 0x40000000 /* 266 MHz capable */ +#define PCIX_STATUS_533MHZ 0x80000000 /* 533 MHz capable */ +}) pcix_config_regs_t; + +static inline int pcie_size_to_code (int bytes) +{ + ASSERT (is_pow2 (bytes)); + ASSERT (bytes <= 4096); + return min_log2 (bytes) - 7; +} + +static inline int pcie_code_to_size (int code) +{ + int size = 1 << (code + 7); + ASSERT (size <= 4096); + return size; +} + +/* PCI Express capability registers */ +typedef CLIB_PACKED (struct { + pci_capability_regs_t header; + + u16 pcie_capabilities; +#define PCIE_CAP_VERSION(x) (((x) >> 0) & 0xf) +#define PCIE_CAP_DEVICE_TYPE(x) (((x) >> 4) & 0xf) +#define PCIE_DEVICE_TYPE_ENDPOINT 0 +#define PCIE_DEVICE_TYPE_LEGACY_ENDPOINT 1 +#define PCIE_DEVICE_TYPE_ROOT_PORT 4 + /* Upstream/downstream port of PCI Express switch. */ +#define PCIE_DEVICE_TYPE_SWITCH_UPSTREAM 5 +#define PCIE_DEVICE_TYPE_SWITCH_DOWNSTREAM 6 +#define PCIE_DEVICE_TYPE_PCIE_TO_PCI_BRIDGE 7 +#define PCIE_DEVICE_TYPE_PCI_TO_PCIE_BRIDGE 8 + /* Root complex integrated endpoint. */ +#define PCIE_DEVICE_TYPE_ROOT_COMPLEX_ENDPOINT 9 +#define PCIE_DEVICE_TYPE_ROOT_COMPLEX_EVENT_COLLECTOR 10 +#define PCIE_CAP_SLOW_IMPLEMENTED (1 << 8) +#define PCIE_CAP_MSI_IRQ(x) (((x) >> 9) & 0x1f) + + u32 dev_capabilities; +#define PCIE_DEVCAP_MAX_PAYLOAD(x) (128 << (((x) >> 0) & 0x7)) +#define PCIE_DEVCAP_PHANTOM_BITS(x) (((x) >> 3) & 0x3) +#define PCIE_DEVCAP_EXTENTED_TAG (1 << 5) +#define PCIE_DEVCAP_L0S 0x1c0 /* L0s Acceptable Latency */ +#define PCIE_DEVCAP_L1 0xe00 /* L1 Acceptable Latency */ +#define PCIE_DEVCAP_ATN_BUT 0x1000 /* Attention Button Present */ +#define PCIE_DEVCAP_ATN_IND 0x2000 /* Attention Indicator Present */ +#define PCIE_DEVCAP_PWR_IND 0x4000 /* Power Indicator Present */ +#define PCIE_DEVCAP_PWR_VAL 0x3fc0000 /* Slot Power Limit Value */ +#define PCIE_DEVCAP_PWR_SCL 0xc000000 /* Slot Power Limit Scale */ + + u16 dev_control; +#define PCIE_CTRL_CERE 0x0001 /* Correctable Error Reporting En. */ +#define PCIE_CTRL_NFERE 0x0002 /* Non-Fatal Error Reporting Enable */ +#define PCIE_CTRL_FERE 0x0004 /* Fatal Error Reporting Enable */ +#define PCIE_CTRL_URRE 0x0008 /* Unsupported Request Reporting En. */ +#define PCIE_CTRL_RELAX_EN 0x0010 /* Enable relaxed ordering */ +#define PCIE_CTRL_MAX_PAYLOAD(n) (((n) & 7) << 5) +#define PCIE_CTRL_EXT_TAG 0x0100 /* Extended Tag Field Enable */ +#define PCIE_CTRL_PHANTOM 0x0200 /* Phantom Functions Enable */ +#define PCIE_CTRL_AUX_PME 0x0400 /* Auxiliary Power PM Enable */ +#define PCIE_CTRL_NOSNOOP_EN 0x0800 /* Enable No Snoop */ +#define PCIE_CTRL_MAX_READ_REQUEST(n) (((n) & 7) << 12) + + u16 dev_status; +#define PCIE_DEVSTA_AUXPD 0x10 /* AUX Power Detected */ +#define PCIE_DEVSTA_TRPND 0x20 /* Transactions Pending */ + + u32 link_capabilities; + u16 link_control; + u16 link_status; + + u32 slot_capabilities; + u16 slot_control; + u16 slot_status; + + u16 root_control; +#define PCIE_RTCTL_SECEE 0x01 /* System Error on Correctable Error */ +#define PCIE_RTCTL_SENFEE 0x02 /* System Error on Non-Fatal Error */ +#define PCIE_RTCTL_SEFEE 0x04 /* System Error on Fatal Error */ +#define PCIE_RTCTL_PMEIE 0x08 /* PME Interrupt Enable */ +#define PCIE_RTCTL_CRSSVE 0x10 /* CRS Software Visibility Enable */ + + u16 root_capabilities; + u32 root_status; +}) pcie_config_regs_t; + +/* PCI express extended capabilities. */ +typedef enum pcie_capability_type { + PCIE_CAP_ADVANCED_ERROR = 1, + PCIE_CAP_VC = 2, + PCIE_CAP_DSN = 3, + PCIE_CAP_PWR = 4, +} pcie_capability_type_t; + +/* Common header for capabilities. */ +typedef CLIB_PACKED (struct { + enum pcie_capability_type type : 16; + + u16 version : 4; + + u16 next_capability : 12; +}) pcie_capability_regs_t; + +typedef CLIB_PACKED (struct { + pcie_capability_regs_t header; + + u32 uncorrectable_status; +#define PCIE_ERROR_UNC_LINK_TRAINING (1 << 0) +#define PCIE_ERROR_UNC_DATA_LINK_PROTOCOL (1 << 4) +#define PCIE_ERROR_UNC_SURPRISE_DOWN (1 << 5) +#define PCIE_ERROR_UNC_POISONED_TLP (1 << 12) +#define PCIE_ERROR_UNC_FLOW_CONTROL (1 << 13) +#define PCIE_ERROR_UNC_COMPLETION_TIMEOUT (1 << 14) +#define PCIE_ERROR_UNC_COMPLETER_ABORT (1 << 15) +#define PCIE_ERROR_UNC_UNEXPECTED_COMPLETION (1 << 16) +#define PCIE_ERROR_UNC_RX_OVERFLOW (1 << 17) +#define PCIE_ERROR_UNC_MALFORMED_TLP (1 << 18) +#define PCIE_ERROR_UNC_CRC_ERROR (1 << 19) +#define PCIE_ERROR_UNC_UNSUPPORTED_REQUEST (1 << 20) + u32 uncorrectable_mask; + u32 uncorrectable_severity; + + u32 correctable_status; +#define PCIE_ERROR_COR_RX_ERROR (1 << 0) +#define PCIE_ERROR_COR_BAD_TLP (1 << 6) +#define PCIE_ERROR_COR_BAD_DLLP (1 << 7) +#define PCIE_ERROR_COR_REPLAY_ROLLOVER (1 << 8) +#define PCIE_ERROR_COR_REPLAY_TIMER (1 << 12) +#define PCIE_ERROR_COR_ADVISORY (1 << 13) + + u32 correctable_mask; + u32 control; + u32 log[4]; + + u32 root_command; + + u32 root_status; + u16 correctable_error_source; + u16 error_source; +}) pcie_advanced_error_regs_t; + +/* Virtual Channel */ +#define PCI_VC_PORT_REG1 4 +#define PCI_VC_PORT_REG2 8 +#define PCI_VC_PORT_CTRL 12 +#define PCI_VC_PORT_STATUS 14 +#define PCI_VC_RES_CAP 16 +#define PCI_VC_RES_CTRL 20 +#define PCI_VC_RES_STATUS 26 + +/* Power Budgeting */ +#define PCI_PWR_DSR 4 /* Data Select Register */ +#define PCI_PWR_DATA 8 /* Data Register */ +#define PCI_PWR_DATA_BASE(x) ((x) & 0xff) /* Base Power */ +#define PCI_PWR_DATA_SCALE(x) (((x) >> 8) & 3) /* Data Scale */ +#define PCI_PWR_DATA_PM_SUB(x) (((x) >> 10) & 7) /* PM Sub State */ +#define PCI_PWR_DATA_PM_STATE(x) (((x) >> 13) & 3) /* PM State */ +#define PCI_PWR_DATA_TYPE(x) (((x) >> 15) & 7) /* Type */ +#define PCI_PWR_DATA_RAIL(x) (((x) >> 18) & 7) /* Power Rail */ +#define PCI_PWR_CAP 12 /* Capability */ +#define PCI_PWR_CAP_BUDGET(x) ((x) & 1) /* Included in system budget */ + +#endif /* included_vlib_pci_config_h */ diff --git a/vlib/vlib/unix/pci.c b/vlib/vlib/unix/pci.c index 02c37f72707..75241f3f1c6 100644 --- a/vlib/vlib/unix/pci.c +++ b/vlib/vlib/unix/pci.c @@ -46,87 +46,105 @@ #include <sys/stat.h> #include <fcntl.h> #include <dirent.h> +#include <sys/ioctl.h> +#include <net/if.h> +#include <linux/ethtool.h> +#include <linux/sockios.h> + linux_pci_main_t linux_pci_main; -static clib_error_t * -foreach_directory_file (char * dir_name, - clib_error_t * (* f) (void * arg, u8 * path_name, u8 * file_name), - void * arg, - int scan_dirs) +clib_error_t * +vlib_pci_bind_to_uio (vlib_pci_device_t * d, char * uio_driver_name) { - DIR * d; - struct dirent * e; clib_error_t * error = 0; - u8 * s, * t; + u8 *s = 0; + DIR *dir = 0; + struct dirent *e; + int fd; + pci_config_header_t * c; + u8 * dev_dir_name = format(0, "/sys/bus/pci/devices/%U", + format_vlib_pci_addr, &d->bus_address); + + c = &d->config0.header; + + /* if uio sub-directory exists, we are fine, device is + already bound to UIO driver */ + s = format (s, "%v/uio%c", dev_dir_name, 0); + if (access ( (char *) s, F_OK) == 0) + goto done; + vec_reset_length (s); + + /* walk trough all linux interfaces and if interface belonging to + this device is founf check if interface is admin up */ + dir = opendir ("/sys/class/net"); + s = format(s, "%U%c", format_vlib_pci_addr, &d->bus_address, 0); - d = opendir (dir_name); - if (! d) + if (!dir) { - /* System has no PCI bus. */ - if (errno == ENOENT) - return 0; - return clib_error_return_unix (0, "open `%s'", dir_name); + error = clib_error_return (0, "Skipping PCI device %U: failed to " + "read /sys/class/net", + format_vlib_pci_addr, &d->bus_address); + goto done; } - s = t = 0; - while (1) + fd = socket(PF_INET, SOCK_DGRAM, 0); + + while((e = readdir (dir))) { - e = readdir (d); - if (! e) - break; - if (scan_dirs) - { - if (e->d_type == DT_DIR - && (! strcmp (e->d_name, ".") - || ! strcmp (e->d_name, ".."))) - continue; - } - else - { - if (e->d_type == DT_DIR) - continue; - } + struct ifreq ifr; + struct ethtool_drvinfo drvinfo; - s = format (s, "%s/%s", dir_name, e->d_name); - t = format (t, "%s", e->d_name); - error = f (arg, s, t); - _vec_len (s) = 0; - _vec_len (t) = 0; + if (e->d_name[0] == '.') /* skip . and .. */ + continue; - if (error) - break; - } + memset(&ifr, 0, sizeof ifr); + memset(&drvinfo, 0, sizeof drvinfo); + ifr.ifr_data = (char *) &drvinfo; + strncpy(ifr.ifr_name, e->d_name, IFNAMSIZ); + drvinfo.cmd = ETHTOOL_GDRVINFO; + ioctl (fd, SIOCETHTOOL, &ifr); - vec_free (s); - closedir (d); + if (strcmp ((char *) s, drvinfo.bus_info)) + continue; - return error; -} + memset (&ifr, 0, sizeof(ifr)); + strncpy (ifr.ifr_name, e->d_name, IFNAMSIZ); + ioctl (fd, SIOCGIFFLAGS, &ifr); + close (fd); -static clib_error_t * -write_sys_fs (char * file_name, char * fmt, ...) -{ - u8 * s; - int fd; + if (ifr.ifr_flags & IFF_UP) + { + error = clib_error_return (0, "Skipping PCI device %U as host " + "interface %s is up", + format_vlib_pci_addr, &d->bus_address, + e->d_name); + goto done; + } + } - fd = open (file_name, O_WRONLY); - if (fd < 0) - return clib_error_return_unix (0, "open `%s'", file_name); + close (fd); + vec_reset_length (s); + + s = format (s, "%v/driver/unbind%c", dev_dir_name, 0); + write_sys_fs ((char *) s, "%U", format_vlib_pci_addr, &d->bus_address); + vec_reset_length (s); - va_list va; - va_start (va, fmt); - s = va_format (0, fmt, &va); - va_end (va); + s = format (s, "/sys/bus/pci/drivers/%s/new_id%c", uio_driver_name, 0); + write_sys_fs ((char *) s, "0x%04x 0x%04x", c->vendor_id, c->device_id); + vec_reset_length (s); - if (write (fd, s, vec_len (s)) < 0) - return clib_error_return_unix (0, "write `%s'", file_name); + s = format (s, "/sys/bus/pci/drivers/%s/bind%c", uio_driver_name, 0); + write_sys_fs ((char *) s, "%U", format_vlib_pci_addr, &d->bus_address); +done: + closedir (dir); vec_free (s); - close (fd); - return 0; + vec_free (dev_dir_name); + return error; } + static clib_error_t * scan_uio_dir (void * arg, u8 * path_name, u8 * file_name) { @@ -149,7 +167,7 @@ static clib_error_t * linux_pci_uio_read_ready (unix_file_t * uf) linux_pci_device_t * l; u32 li = uf->private_data; - l = pool_elt_at_index (pm->pci_devices, li); + l = pool_elt_at_index (pm->linux_pci_devices, li); vlib_node_set_interrupt_pending (vm, l->device_input_node_index); /* Let node know which device is interrupting. */ @@ -176,7 +194,7 @@ static uword pci_resource_size (uword os_handle, uword resource) struct stat b; uword result = 0; - p = pool_elt_at_index (pm->pci_devices, os_handle); + p = pool_elt_at_index (pm->linux_pci_devices, os_handle); file_name = format (0, "%v/resource%d%c", p->dev_dir_name, resource, 0); if (stat ((char *) file_name, &b) >= 0) @@ -193,7 +211,7 @@ void os_add_pci_disable_interrupts_reg (uword os_handle, u32 resource, char * file_name; clib_error_t * error; - l = pool_elt_at_index (pm->pci_devices, os_handle); + l = pool_elt_at_index (pm->linux_pci_devices, os_handle); ASSERT (resource == 0); ASSERT (reg_offset < pci_resource_size (os_handle, resource)); file_name = (char *) format (0, "%s/disable_interrupt_regs%c", l->dev_dir_name, 0); @@ -203,7 +221,7 @@ void os_add_pci_disable_interrupts_reg (uword os_handle, u32 resource, vec_free (file_name); } -static void add_device (pci_device_t * dev, linux_pci_device_t * pdev) +static void add_device (vlib_pci_device_t * dev, linux_pci_device_t * pdev) { linux_pci_main_t * pm = &linux_pci_main; linux_pci_device_t * l; @@ -213,30 +231,12 @@ static void add_device (pci_device_t * dev, linux_pci_device_t * pdev) c = &dev->config0.header; - pool_get (pm->pci_devices, l); + pool_get (pm->linux_pci_devices, l); l[0] = pdev[0]; l->dev_dir_name = vec_dup (l->dev_dir_name); - /* Parse bus, dev, function from directory name. */ - { - unformat_input_t input; - - unformat_init_string (&input, (char *) l->dev_dir_name, - vec_len (l->dev_dir_name)); - - if (! unformat (&input, "/sys/bus/pci/devices/%x:%x:%x.%x", - &x[0], &x[1], &x[2], &x[3])) - abort (); - - unformat_free (&input); - - l->bus_address.bus = x[1]; - l->bus_address.slot_function = (x[2] << 3) | x[3]; - dev->bus_address = l->bus_address; - } - - dev->os_handle = l - pm->pci_devices; + dev->os_handle = l - pm->linux_pci_devices; error = write_sys_fs ("/sys/bus/pci/drivers/uio_pci_dma/new_id", "%x %x", c->vendor_id, c->device_id); @@ -269,7 +269,7 @@ static void add_device (pci_device_t * dev, linux_pci_device_t * pdev) template.read_function = linux_pci_uio_read_ready; template.file_descriptor = l->uio_fd; template.error_function = linux_pci_uio_error_ready; - template.private_data = l - pm->pci_devices; + template.private_data = l - pm->linux_pci_devices; /* To be filled in by driver. */ l->device_input_node_index = ~0; @@ -305,7 +305,7 @@ os_read_write_pci_config (uword os_handle, linux_pci_device_t * p; int n; - p = pool_elt_at_index (pm->pci_devices, os_handle); + p = pool_elt_at_index (pm->linux_pci_devices, os_handle); if (address != lseek (p->config_fd, address, SEEK_SET)) return clib_error_return_unix (0, "seek offset %d", address); @@ -338,7 +338,7 @@ os_map_pci_resource_internal (uword os_handle, int flags = MAP_SHARED; error = 0; - p = pool_elt_at_index (pm->pci_devices, os_handle); + p = pool_elt_at_index (pm->linux_pci_devices, os_handle); file_name = format (0, "%v/resource%d%c", p->dev_dir_name, resource, 0); fd = open ((char *) file_name, O_RDWR); @@ -404,9 +404,9 @@ void os_free_pci_device (uword os_handle) linux_pci_main_t * pm = &linux_pci_main; linux_pci_device_t * l; - l = pool_elt_at_index (pm->pci_devices, os_handle); + l = pool_elt_at_index (pm->linux_pci_devices, os_handle); linux_pci_device_free (l); - pool_put (pm->pci_devices, l); + pool_put (pm->linux_pci_devices, l); } u8 * format_os_pci_handle (u8 * s, va_list * va) @@ -415,10 +415,9 @@ u8 * format_os_pci_handle (u8 * s, va_list * va) uword os_pci_handle = va_arg (*va, uword); linux_pci_device_t * l; - l = pool_elt_at_index (pm->pci_devices, os_pci_handle); + l = pool_elt_at_index (pm->linux_pci_devices, os_pci_handle); return format (s, "%x/%x/%x", l->bus_address.bus, - (l->bus_address.slot_function >> 3), - (l->bus_address.slot_function & 0x7)); + l->bus_address.slot, l->bus_address.function); } static inline pci_device_registration_t * @@ -450,17 +449,17 @@ static inline u8 kernel_driver_installed (pci_device_registration_t *r) static clib_error_t * init_device_from_registered (vlib_main_t * vm, - pci_device_t * dev, + vlib_pci_device_t * dev, linux_pci_device_t * pdev) { - unix_main_t * um = vlib_unix_get_main(); + linux_pci_main_t * lpm = &linux_pci_main; pci_device_registration_t * r; pci_device_id_t * i; pci_config_header_t * c; c = &dev->config0.header; - r = um->pci_device_registrations; + r = lpm->pci_device_registrations; while (r) { @@ -490,7 +489,7 @@ init_device_from_registered (vlib_main_t * vm, static clib_error_t * init_device (vlib_main_t * vm, - pci_device_t * dev, + vlib_pci_device_t * dev, linux_pci_device_t * pdev) { return init_device_from_registered (vm, dev, pdev); @@ -500,10 +499,11 @@ static clib_error_t * scan_device (void * arg, u8 * dev_dir_name, u8 * ignored) { vlib_main_t * vm = arg; + linux_pci_main_t * pm = &linux_pci_main; int fd; u8 * f; clib_error_t * error = 0; - pci_device_t dev = {0}; + vlib_pci_device_t * dev; linux_pci_device_t pdev = {0}; f = format (0, "%v/config%c", dev_dir_name, 0); @@ -519,11 +519,14 @@ scan_device (void * arg, u8 * dev_dir_name, u8 * ignored) goto done; } + pool_get (pm->pci_devs, dev); + /* You can only read more that 64 bytes of config space as root; so we try to read the full space but fall back to just the first 64 bytes. */ - if (read (fd, &dev.config_data, sizeof (dev.config_data)) != sizeof (dev.config_data) - && read (fd, &dev.config0, sizeof (dev.config0)) != sizeof (dev.config0)) + if (read (fd, &dev->config_data, sizeof (dev->config_data)) != sizeof (dev->config_data) + && read (fd, &dev->config0, sizeof (dev->config0)) != sizeof (dev->config0)) { + pool_put (pm->pci_devs, dev); error = clib_error_return_unix (0, "read `%s'", f); goto done; } @@ -532,23 +535,44 @@ scan_device (void * arg, u8 * dev_dir_name, u8 * ignored) static pci_config_header_t all_ones; if (all_ones.vendor_id == 0) memset (&all_ones, ~0, sizeof (all_ones)); - - if (! memcmp (&dev.config0.header, &all_ones, sizeof (all_ones))) + + if (! memcmp (&dev->config0.header, &all_ones, sizeof (all_ones))) { + pool_put (pm->pci_devs, dev); error = clib_error_return (0, "invalid PCI config for `%s'", f); goto done; } } - if (dev.config0.header.header_type == 0) - pci_config_type0_little_to_host (&dev.config0); + if (dev->config0.header.header_type == 0) + pci_config_type0_little_to_host (&dev->config0); else - pci_config_type1_little_to_host (&dev.config1); + pci_config_type1_little_to_host (&dev->config1); + + /* Parse bus, dev, function from directory name. */ + { + unformat_input_t input; + + unformat_init_string (&input, (char *) dev_dir_name, + vec_len (dev_dir_name)); + + if (! unformat (&input, "/sys/bus/pci/devices/%U", + unformat_vlib_pci_addr, &dev->bus_address)) + abort (); + + unformat_free (&input); + + pdev.bus_address = dev->bus_address; + } + pdev.config_fd = fd; pdev.dev_dir_name = dev_dir_name; - error = init_device (vm, &dev, &pdev); + hash_set(pm->pci_dev_index_by_pci_addr, dev->bus_address.as_u32, + dev - pm->pci_devs); + + error = init_device (vm, dev, &pdev); done: vec_free (f); @@ -565,6 +589,9 @@ clib_error_t * pci_bus_init (vlib_main_t * vm) if ((error = vlib_call_init_function (vm, unix_input_init))) return error; + ASSERT(sizeof(vlib_pci_addr_t) == sizeof(u32)); + pm->pci_dev_index_by_pci_addr = hash_create (0, sizeof (uword)); + error = foreach_directory_file ("/sys/bus/pci/devices", scan_device, vm, /* scan_dirs */ 0); /* Complain and continue. might not be root, etc. */ diff --git a/vlib/vlib/unix/pci.h b/vlib/vlib/unix/pci.h index b384250eb47..dcbf1cfbf60 100644 --- a/vlib/vlib/unix/pci.h +++ b/vlib/vlib/unix/pci.h @@ -53,7 +53,7 @@ typedef struct { int config_fd; /* PCI bus address for this devices parsed from /sys/bus/pci/devices name. */ - pci_bus_address_t bus_address; + vlib_pci_addr_t bus_address; /* File descriptor for /dev/uio%d */ int uio_fd; @@ -74,16 +74,19 @@ typedef struct { /* Pool of PCI devices. */ typedef struct { vlib_main_t * vlib_main; - linux_pci_device_t * pci_devices; + vlib_pci_device_t * pci_devs; + linux_pci_device_t * linux_pci_devices; + pci_device_registration_t * pci_device_registrations; + uword * pci_dev_index_by_pci_addr; } linux_pci_main_t; extern linux_pci_main_t linux_pci_main; always_inline linux_pci_device_t * -pci_dev_for_linux (pci_device_t * dev) +pci_dev_for_linux (vlib_pci_device_t * dev) { linux_pci_main_t * pm = &linux_pci_main; - return pool_elt_at_index (pm->pci_devices, dev->os_handle); + return pool_elt_at_index (pm->linux_pci_devices, dev->os_handle); } /* Call to allocate/initialize the pci subsystem. @@ -91,4 +94,6 @@ pci_dev_for_linux (pci_device_t * dev) pci only when it's needed. */ clib_error_t * pci_bus_init (vlib_main_t * vm); +clib_error_t * vlib_pci_bind_to_uio (vlib_pci_device_t * d, char * uio_driver_name); + #endif /* included_unix_pci_h */ diff --git a/vlib/vlib/unix/unix.h b/vlib/vlib/unix/unix.h index e3e6aa14176..44a8853e356 100644 --- a/vlib/vlib/unix/unix.h +++ b/vlib/vlib/unix/unix.h @@ -175,4 +175,18 @@ static inline unix_main_t * vlib_unix_get_main (void) /* thread stack array; vec_len = max number of threads */ u8 **vlib_thread_stacks; +/* utils */ + +clib_error_t * +write_sys_fs (char * file_name, char * fmt, ...); + +clib_error_t * +read_sys_fs (char * file_name, char * fmt, ...); + +clib_error_t * +foreach_directory_file (char * dir_name, + clib_error_t * (* f) (void * arg, u8 * path_name, + u8 * file_name), + void * arg, int scan_dirs); + #endif /* included_unix_unix_h */ diff --git a/vlib/vlib/unix/util.c b/vlib/vlib/unix/util.c new file mode 100644 index 00000000000..d64036024bf --- /dev/null +++ b/vlib/vlib/unix/util.c @@ -0,0 +1,165 @@ +/* + * Copyright (c) 2016 Cisco and/or its affiliates. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +/* + * pci.c: Linux user space PCI bus management. + * + * Copyright (c) 2008 Eliot Dresselhaus + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE + * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION + * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ + +#include <vlib/vlib.h> +#include <vlib/unix/unix.h> + +#include <sys/types.h> +#include <sys/stat.h> +#include <fcntl.h> +#include <dirent.h> + +clib_error_t * +foreach_directory_file (char * dir_name, + clib_error_t * (* f) (void * arg, u8 * path_name, u8 * file_name), + void * arg, + int scan_dirs) +{ + DIR * d; + struct dirent * e; + clib_error_t * error = 0; + u8 * s, * t; + + d = opendir (dir_name); + if (! d) + { + /* System has no PCI bus. */ + if (errno == ENOENT) + return 0; + return clib_error_return_unix (0, "open `%s'", dir_name); + } + + s = t = 0; + while (1) + { + e = readdir (d); + if (! e) + break; + if (scan_dirs) + { + if (e->d_type == DT_DIR + && (! strcmp (e->d_name, ".") + || ! strcmp (e->d_name, ".."))) + continue; + } + else + { + if (e->d_type == DT_DIR) + continue; + } + + s = format (s, "%s/%s", dir_name, e->d_name); + t = format (t, "%s", e->d_name); + error = f (arg, s, t); + _vec_len (s) = 0; + _vec_len (t) = 0; + + if (error) + break; + } + + vec_free (s); + closedir (d); + + return error; +} + +clib_error_t * +write_sys_fs (char * file_name, char * fmt, ...) +{ + u8 * s; + int fd; + + fd = open (file_name, O_WRONLY); + if (fd < 0) + return clib_error_return_unix (0, "open `%s'", file_name); + + va_list va; + va_start (va, fmt); + s = va_format (0, fmt, &va); + va_end (va); + + if (write (fd, s, vec_len (s)) < 0) + return clib_error_return_unix (0, "write `%s'", file_name); + + vec_free (s); + close (fd); + return 0; +} + +clib_error_t * +read_sys_fs (char * file_name, char * fmt, ...) +{ + unformat_input_t input; + u8 * s = 0; + int fd; + ssize_t sz; + uword result; + + fd = open (file_name, O_RDONLY); + if (fd < 0) + return clib_error_return_unix (0, "open `%s'", file_name); + + vec_validate(s, 4095); + + sz = read(fd, s, vec_len (s)); + if (sz < 0) + { + close(fd); + vec_free(s); + return clib_error_return_unix (0, "read `%s'", file_name); + } + + _vec_len(s) = sz; + unformat_init_vector(&input, s); + + va_list va; + va_start (va, fmt); + result = va_unformat (&input, fmt, &va); + va_end (va); + + vec_free (s); + close (fd); + + if (result == 0) + return clib_error_return (0, "unformat error"); + + return 0; +} + |