1 /* SPDX-License-Identifier: BSD-3-Clause 2 * Copyright(c) 2010-2014 Intel Corporation. 3 * Copyright 2013-2014 6WIND S.A. 4 */ 5 6 #include <string.h> 7 #include <inttypes.h> 8 #include <stdint.h> 9 #include <stdlib.h> 10 #include <stdio.h> 11 #include <sys/queue.h> 12 13 #include <rte_errno.h> 14 #include <rte_interrupts.h> 15 #include <rte_log.h> 16 #include <rte_bus.h> 17 #include <rte_eal_paging.h> 18 #include <rte_per_lcore.h> 19 #include <rte_memory.h> 20 #include <rte_eal.h> 21 #include <rte_string_fns.h> 22 #include <rte_common.h> 23 #include <rte_debug.h> 24 25 #include "rte_pci.h" 26 27 static inline const char * 28 get_u8_pciaddr_field(const char *in, void *_u8, char dlm) 29 { 30 unsigned long val; 31 uint8_t *u8 = _u8; 32 char *end; 33 34 /* empty string is an error though strtoul() returns 0 */ 35 if (*in == '\0') 36 return NULL; 37 38 /* PCI field starting with spaces is forbidden. 39 * Negative wrap-around is not reported as an error by strtoul. 40 */ 41 if (*in == ' ' || *in == '-') 42 return NULL; 43 44 errno = 0; 45 val = strtoul(in, &end, 16); 46 if (errno != 0 || end[0] != dlm || val > UINT8_MAX) { 47 errno = errno ? errno : EINVAL; 48 return NULL; 49 } 50 *u8 = (uint8_t)val; 51 return end + 1; 52 } 53 54 static int 55 pci_bdf_parse(const char *input, struct rte_pci_addr *dev_addr) 56 { 57 const char *in = input; 58 59 dev_addr->domain = 0; 60 in = get_u8_pciaddr_field(in, &dev_addr->bus, ':'); 61 if (in == NULL) 62 return -EINVAL; 63 in = get_u8_pciaddr_field(in, &dev_addr->devid, '.'); 64 if (in == NULL) 65 return -EINVAL; 66 in = get_u8_pciaddr_field(in, &dev_addr->function, '\0'); 67 if (in == NULL) 68 return -EINVAL; 69 return 0; 70 } 71 72 static int 73 pci_dbdf_parse(const char *input, struct rte_pci_addr *dev_addr) 74 { 75 const char *in = input; 76 unsigned long val; 77 char *end; 78 79 /* PCI id starting with spaces is forbidden. 80 * Negative wrap-around is not reported as an error by strtoul. 81 */ 82 if (*in == ' ' || *in == '-') 83 return -EINVAL; 84 85 errno = 0; 86 val = strtoul(in, &end, 16); 87 /* Empty string is not an error for strtoul, but the check 88 * end[0] != ':' 89 * will detect the issue. 90 */ 91 if (errno != 0 || end[0] != ':' || val > UINT32_MAX) 92 return -EINVAL; 93 dev_addr->domain = (uint32_t)val; 94 in = end + 1; 95 in = get_u8_pciaddr_field(in, &dev_addr->bus, ':'); 96 if (in == NULL) 97 return -EINVAL; 98 in = get_u8_pciaddr_field(in, &dev_addr->devid, '.'); 99 if (in == NULL) 100 return -EINVAL; 101 in = get_u8_pciaddr_field(in, &dev_addr->function, '\0'); 102 if (in == NULL) 103 return -EINVAL; 104 return 0; 105 } 106 107 void 108 rte_pci_device_name(const struct rte_pci_addr *addr, 109 char *output, size_t size) 110 { 111 RTE_VERIFY(size >= PCI_PRI_STR_SIZE); 112 RTE_VERIFY(snprintf(output, size, PCI_PRI_FMT, 113 addr->domain, addr->bus, 114 addr->devid, addr->function) >= 0); 115 } 116 117 int 118 rte_pci_addr_cmp(const struct rte_pci_addr *addr, 119 const struct rte_pci_addr *addr2) 120 { 121 uint64_t dev_addr, dev_addr2; 122 123 if ((addr == NULL) || (addr2 == NULL)) 124 return -1; 125 126 dev_addr = ((uint64_t)addr->domain << 24) | 127 (addr->bus << 16) | (addr->devid << 8) | addr->function; 128 dev_addr2 = ((uint64_t)addr2->domain << 24) | 129 (addr2->bus << 16) | (addr2->devid << 8) | addr2->function; 130 131 if (dev_addr > dev_addr2) 132 return 1; 133 else if (dev_addr < dev_addr2) 134 return -1; 135 else 136 return 0; 137 } 138 139 int 140 rte_pci_addr_parse(const char *str, struct rte_pci_addr *addr) 141 { 142 if (pci_bdf_parse(str, addr) == 0 || 143 pci_dbdf_parse(str, addr) == 0) 144 return 0; 145 return -1; 146 } 147