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