1 /* 2 * Copyright (c) 2021 Broadcom. All rights reserved. 3 * The term "Broadcom" refers to Broadcom Limited and/or its subsidiaries. 4 * 5 * Redistribution and use in source and binary forms, with or without 6 * modification, are permitted provided that the following conditions are met: 7 * 8 * 1. Redistributions of source code must retain the above copyright notice, 9 * this list of conditions and the following disclaimer. 10 * 11 * 2. Redistributions in binary form must reproduce the above copyright notice, 12 * this list of conditions and the following disclaimer in the documentation 13 * and/or other materials provided with the distribution. 14 * 15 * 3. Neither the name of the copyright holder nor the names of its contributors 16 * may be used to endorse or promote products derived from this software 17 * without specific prior written permission. 18 * 19 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 20 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 21 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 22 * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE 23 * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 24 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 25 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 26 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 27 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 28 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 29 * POSSIBILITY OF SUCH DAMAGE. 30 * 31 */ 32 33 #include "ocs.h" 34 #include "ocs_gendump.h" 35 36 /* Reset all the functions associated with a bus/dev */ 37 static int 38 ocs_gen_dump_reset(uint8_t bus, uint8_t dev) 39 { 40 uint32_t index = 0; 41 ocs_t *ocs; 42 int rc = 0; 43 44 while ((ocs = ocs_get_instance(index++)) != NULL) { 45 uint8_t ocs_bus, ocs_dev, ocs_func; 46 ocs_domain_t *domain; 47 48 ocs_get_bus_dev_func(ocs, &ocs_bus, &ocs_dev, &ocs_func); 49 50 if (!(ocs_bus == bus && ocs_dev == dev)) 51 continue; 52 53 if (ocs_hw_reset(&ocs->hw, OCS_HW_RESET_FUNCTION)) { 54 ocs_log_test(ocs, "failed to reset port\n"); 55 rc = -1; 56 continue; 57 } 58 59 ocs_log_debug(ocs, "successfully reset port\n"); 60 while ((domain = ocs_list_get_head(&ocs->domain_list)) != NULL) { 61 ocs_log_debug(ocs, "free domain %p\n", domain); 62 ocs_domain_force_free(domain); 63 } 64 /* now initialize hw so user can read the dump in */ 65 if (ocs_hw_init(&ocs->hw)) { 66 ocs_log_err(ocs, "failed to initialize hw\n"); 67 rc = -1; 68 } else { 69 ocs_log_debug(ocs, "successfully initialized hw\n"); 70 } 71 } 72 return rc; 73 } 74 75 int 76 ocs_gen_dump(ocs_t *ocs) 77 { 78 uint32_t reset_required; 79 uint32_t dump_ready; 80 uint32_t ms_waited; 81 uint8_t bus, dev, func; 82 int rc = 0; 83 int index = 0, port_index = 0; 84 ocs_t *nxt_ocs; 85 uint8_t nxt_bus, nxt_dev, nxt_func; 86 uint8_t prev_port_state[OCS_MAX_HBA_PORTS] = {0,}; 87 ocs_xport_stats_t link_status; 88 89 ocs_get_bus_dev_func(ocs, &bus, &dev, &func); 90 91 /* Drop link on all ports belongs to this HBA*/ 92 while ((nxt_ocs = ocs_get_instance(index++)) != NULL) { 93 ocs_get_bus_dev_func(nxt_ocs, &nxt_bus, &nxt_dev, &nxt_func); 94 95 if (!(bus == nxt_bus && dev == nxt_dev)) 96 continue; 97 98 if ((port_index >= OCS_MAX_HBA_PORTS)) 99 continue; 100 101 /* Check current link status and save for future use */ 102 if (ocs_xport_status(nxt_ocs->xport, OCS_XPORT_PORT_STATUS, 103 &link_status) == 0) { 104 if (link_status.value == OCS_XPORT_PORT_ONLINE) { 105 prev_port_state[port_index] = 1; 106 ocs_xport_control(nxt_ocs->xport, 107 OCS_XPORT_PORT_OFFLINE); 108 } else { 109 prev_port_state[port_index] = 0; 110 } 111 } 112 port_index++; 113 } 114 115 /* Wait until all ports have quiesced */ 116 for (index = 0; (nxt_ocs = ocs_get_instance(index++)) != NULL; ) { 117 ms_waited = 0; 118 for (;;) { 119 ocs_xport_stats_t status; 120 121 ocs_xport_status(nxt_ocs->xport, OCS_XPORT_IS_QUIESCED, 122 &status); 123 if (status.value) { 124 ocs_log_debug(nxt_ocs, "port quiesced\n"); 125 break; 126 } 127 128 ocs_msleep(10); 129 ms_waited += 10; 130 if (ms_waited > 60000) { 131 ocs_log_test(nxt_ocs, 132 "timed out waiting for port to quiesce\n"); 133 break; 134 } 135 } 136 } 137 138 /* Initiate dump */ 139 if (ocs_hw_raise_ue(&ocs->hw, 1) == OCS_HW_RTN_SUCCESS) { 140 141 /* Wait for dump to complete */ 142 ocs_log_debug(ocs, "Dump requested, wait for completion.\n"); 143 144 dump_ready = 0; 145 ms_waited = 0; 146 while ((!dump_ready) && (ms_waited < 30000)) { 147 ocs_hw_get(&ocs->hw, OCS_HW_DUMP_READY, &dump_ready); 148 ocs_udelay(10000); 149 ms_waited += 10; 150 } 151 152 if (!dump_ready) { 153 ocs_log_test(ocs, "Failed to see dump after 30 secs\n"); 154 rc = -1; 155 } else { 156 ocs_log_debug(ocs, "sucessfully generated dump\n"); 157 } 158 159 /* now reset port */ 160 ocs_hw_get(&ocs->hw, OCS_HW_RESET_REQUIRED, &reset_required); 161 ocs_log_debug(ocs, "reset required=%d\n", reset_required); 162 if (reset_required) { 163 if (ocs_gen_dump_reset(bus, dev) == 0) { 164 ocs_log_debug(ocs, "all devices reset\n"); 165 } else { 166 ocs_log_test(ocs, "all devices NOT reset\n"); 167 } 168 } 169 } else { 170 ocs_log_test(ocs, "dump request to hw failed\n"); 171 rc = -1; 172 } 173 174 index = port_index = 0; 175 nxt_ocs = NULL; 176 /* Bring links on each HBA port to previous state*/ 177 while ((nxt_ocs = ocs_get_instance(index++)) != NULL) { 178 ocs_get_bus_dev_func(nxt_ocs, &nxt_bus, &nxt_dev, &nxt_func); 179 if (port_index > OCS_MAX_HBA_PORTS) { 180 ocs_log_err(NULL, "port index(%d) out of boundary\n", 181 port_index); 182 rc = -1; 183 break; 184 } 185 if ((bus == nxt_bus) && (dev == nxt_dev) && 186 prev_port_state[port_index++]) { 187 ocs_xport_control(nxt_ocs->xport, OCS_XPORT_PORT_ONLINE); 188 } 189 } 190 191 return rc; 192 } 193 194 int 195 ocs_fdb_dump(ocs_t *ocs) 196 { 197 uint32_t dump_ready; 198 uint32_t ms_waited; 199 int rc = 0; 200 201 #define FDB 2 202 203 /* Initiate dump */ 204 if (ocs_hw_raise_ue(&ocs->hw, FDB) == OCS_HW_RTN_SUCCESS) { 205 206 /* Wait for dump to complete */ 207 ocs_log_debug(ocs, "Dump requested, wait for completion.\n"); 208 209 dump_ready = 0; 210 ms_waited = 0; 211 while ((!(dump_ready == FDB)) && (ms_waited < 10000)) { 212 ocs_hw_get(&ocs->hw, OCS_HW_DUMP_READY, &dump_ready); 213 ocs_udelay(10000); 214 ms_waited += 10; 215 } 216 217 if (!dump_ready) { 218 ocs_log_err(ocs, "Failed to see dump after 10 secs\n"); 219 return -1; 220 } 221 222 ocs_log_debug(ocs, "sucessfully generated dump\n"); 223 224 } else { 225 ocs_log_err(ocs, "dump request to hw failed\n"); 226 rc = -1; 227 } 228 229 return rc; 230 } 231 232 /** 233 * @brief Create a Lancer dump into a memory buffer 234 * @par Description 235 * This function creates a DMA buffer to hold a Lancer dump, 236 * sets the dump location to point to that buffer, then calls 237 * ocs_gen_dump to cause a dump to be transferred to the buffer. 238 * After the dump is complete it copies the dump to the provided 239 * user space buffer. 240 * 241 * @param ocs Pointer to ocs structure 242 * @param buf User space buffer in which to store the dump 243 * @param buflen Length of the user buffer in bytes 244 * 245 * @return Returns 0 on success, non-zero on error. 246 */ 247 int 248 ocs_dump_to_host(ocs_t *ocs, void *buf, uint32_t buflen) 249 { 250 int rc; 251 uint32_t i, num_buffers; 252 ocs_dma_t *dump_buffers; 253 uint32_t rem_bytes, offset; 254 255 if (buflen == 0) { 256 ocs_log_test(ocs, "zero buffer length is invalid\n"); 257 return -1; 258 } 259 260 num_buffers = ((buflen + OCS_MAX_DMA_ALLOC - 1) / OCS_MAX_DMA_ALLOC); 261 262 dump_buffers = ocs_malloc(ocs, sizeof(ocs_dma_t) * num_buffers, 263 OCS_M_ZERO | OCS_M_NOWAIT); 264 if (dump_buffers == NULL) { 265 ocs_log_err(ocs, "Failed to dump buffers\n"); 266 return -1; 267 } 268 269 /* Allocate a DMA buffers to hold the dump */ 270 rem_bytes = buflen; 271 for (i = 0; i < num_buffers; i++) { 272 uint32_t num_bytes = MIN(rem_bytes, OCS_MAX_DMA_ALLOC); 273 274 rc = ocs_dma_alloc(ocs, &dump_buffers[i], num_bytes, 275 OCS_MIN_DMA_ALIGNMENT); 276 if (rc) { 277 ocs_log_err(ocs, "Failed to allocate dump buffer\n"); 278 279 /* Free any previously allocated buffers */ 280 goto free_and_return; 281 } 282 rem_bytes -= num_bytes; 283 } 284 285 rc = ocs_hw_set_dump_location(&ocs->hw, num_buffers, dump_buffers, 0); 286 if (rc) { 287 ocs_log_test(ocs, "ocs_hw_set_dump_location failed\n"); 288 goto free_and_return; 289 } 290 291 /* Generate the dump */ 292 rc = ocs_gen_dump(ocs); 293 if (rc) { 294 ocs_log_test(ocs, "ocs_gen_dump failed\n"); 295 goto free_and_return; 296 } 297 298 /* Copy the dump from the DMA buffer into the user buffer */ 299 offset = 0; 300 for (i = 0; i < num_buffers; i++) { 301 if (ocs_copy_to_user((uint8_t*)buf + offset, 302 dump_buffers[i].virt, dump_buffers[i].size)) { 303 ocs_log_test(ocs, "ocs_copy_to_user failed\n"); 304 rc = -1; 305 } 306 offset += dump_buffers[i].size; 307 } 308 309 free_and_return: 310 /* Free the DMA buffer and return */ 311 for (i = 0; i < num_buffers; i++) { 312 ocs_dma_free(ocs, &dump_buffers[i]); 313 } 314 ocs_free(ocs, dump_buffers, sizeof(ocs_dma_t) * num_buffers); 315 return rc; 316 } 317 318 int 319 ocs_function_speciic_dump(ocs_t *ocs, void *buf, uint32_t buflen) 320 { 321 int rc; 322 uint32_t i, num_buffers; 323 ocs_dma_t *dump_buffers; 324 uint32_t rem_bytes, offset; 325 326 if (buflen == 0) { 327 ocs_log_err(ocs, "zero buffer length is invalid\n"); 328 return -1; 329 } 330 331 num_buffers = ((buflen + OCS_MAX_DMA_ALLOC - 1) / OCS_MAX_DMA_ALLOC); 332 333 dump_buffers = ocs_malloc(ocs, sizeof(ocs_dma_t) * num_buffers, 334 OCS_M_ZERO | OCS_M_NOWAIT); 335 if (dump_buffers == NULL) { 336 ocs_log_err(ocs, "Failed to allocate dump buffers\n"); 337 return -1; 338 } 339 340 /* Allocate a DMA buffers to hold the dump */ 341 rem_bytes = buflen; 342 for (i = 0; i < num_buffers; i++) { 343 uint32_t num_bytes = MIN(rem_bytes, OCS_MAX_DMA_ALLOC); 344 rc = ocs_dma_alloc(ocs, &dump_buffers[i], num_bytes, 345 OCS_MIN_DMA_ALIGNMENT); 346 if (rc) { 347 ocs_log_err(ocs, "Failed to allocate dma buffer\n"); 348 349 /* Free any previously allocated buffers */ 350 goto free_and_return; 351 } 352 rem_bytes -= num_bytes; 353 } 354 355 /* register buffers for function spcific dump */ 356 rc = ocs_hw_set_dump_location(&ocs->hw, num_buffers, dump_buffers, 1); 357 if (rc) { 358 ocs_log_err(ocs, "ocs_hw_set_dump_location failed\n"); 359 goto free_and_return; 360 } 361 362 /* Invoke dump by setting fdd=1 and ip=1 in sliport_control register */ 363 rc = ocs_fdb_dump(ocs); 364 if (rc) { 365 ocs_log_err(ocs, "ocs_gen_dump failed\n"); 366 goto free_and_return; 367 } 368 369 /* Copy the dump from the DMA buffer into the user buffer */ 370 offset = 0; 371 for (i = 0; i < num_buffers; i++) { 372 if (ocs_copy_to_user((uint8_t*)buf + offset, 373 dump_buffers[i].virt, dump_buffers[i].size)) { 374 ocs_log_err(ocs, "ocs_copy_to_user failed\n"); 375 rc = -1; 376 } 377 offset += dump_buffers[i].size; 378 } 379 380 free_and_return: 381 /* Free the DMA buffer and return */ 382 for (i = 0; i < num_buffers; i++) { 383 ocs_dma_free(ocs, &dump_buffers[i]); 384 } 385 ocs_free(ocs, dump_buffers, sizeof(ocs_dma_t) * num_buffers); 386 return rc; 387 388 } 389