/*- * SPDX-License-Identifier: BSD-2-Clause * * Copyright (c) 2006-2010 Adaptec, Inc. * Copyright (c) 2010-2012 PMC-Sierra, Inc. * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * */ #include __FBSDID("$FreeBSD$"); /* * Debugging support. */ #include "opt_aacraid.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #ifdef AACRAID_DEBUG /* * Dump the command queue indices */ void aacraid_print_queues(struct aac_softc *sc) { device_printf(sc->aac_dev, "AACQ_FREE %d/%d\n", sc->aac_qstat[AACQ_FREE].q_length, sc->aac_qstat[AACQ_FREE].q_max); device_printf(sc->aac_dev, "AACQ_READY %d/%d\n", sc->aac_qstat[AACQ_READY].q_length, sc->aac_qstat[AACQ_READY].q_max); device_printf(sc->aac_dev, "AACQ_BUSY %d/%d\n", sc->aac_qstat[AACQ_BUSY].q_length, sc->aac_qstat[AACQ_BUSY].q_max); } /* * Print a FIB */ void aacraid_print_fib(struct aac_softc *sc, struct aac_fib *fib, const char *caller) { if (fib == NULL) { device_printf(sc->aac_dev, "aac_print_fib called with NULL fib\n"); return; } device_printf(sc->aac_dev, "%s: FIB @ %p\n", caller, fib); device_printf(sc->aac_dev, " XferState %b\n", fib->Header.XferState, "\20" "\1HOSTOWNED" "\2ADAPTEROWNED" "\3INITIALISED" "\4EMPTY" "\5FROMPOOL" "\6FROMHOST" "\7FROMADAP" "\10REXPECTED" "\11RNOTEXPECTED" "\12DONEADAP" "\13DONEHOST" "\14HIGH" "\15NORM" "\16ASYNC" "\17PAGEFILEIO" "\20SHUTDOWN" "\21LAZYWRITE" "\22ADAPMICROFIB" "\23BIOSFIB" "\24FAST_RESPONSE" "\25APIFIB\n"); device_printf(sc->aac_dev, " Command %d\n", fib->Header.Command); device_printf(sc->aac_dev, " StructType %d\n", fib->Header.StructType); device_printf(sc->aac_dev, " Size %d\n", fib->Header.Size); device_printf(sc->aac_dev, " SenderSize %d\n", fib->Header.SenderSize); device_printf(sc->aac_dev, " SenderAddress 0x%x\n", fib->Header.SenderFibAddress); device_printf(sc->aac_dev, " RcvrAddress 0x%x\n", fib->Header.u.ReceiverFibAddress); device_printf(sc->aac_dev, " Handle 0x%x\n", fib->Header.Handle); switch(fib->Header.Command) { case ContainerCommand: { struct aac_blockread *br; struct aac_blockwrite *bw; struct aac_sg_table *sg; int i; br = (struct aac_blockread*)fib->data; bw = (struct aac_blockwrite*)fib->data; sg = NULL; if (br->Command == VM_CtBlockRead) { device_printf(sc->aac_dev, " BlockRead: container %d 0x%x/%d\n", br->ContainerId, br->BlockNumber, br->ByteCount); sg = &br->SgMap; } if (bw->Command == VM_CtBlockWrite) { device_printf(sc->aac_dev, " BlockWrite: container %d 0x%x/%d " "(%s)\n", bw->ContainerId, bw->BlockNumber, bw->ByteCount, bw->Stable == CSTABLE ? "stable" : "unstable"); sg = &bw->SgMap; } if (sg != NULL) { device_printf(sc->aac_dev, " %d s/g entries\n", sg->SgCount); for (i = 0; i < sg->SgCount; i++) device_printf(sc->aac_dev, " 0x%08x/%d\n", sg->SgEntry[i].SgAddress, sg->SgEntry[i].SgByteCount); } break; } default: device_printf(sc->aac_dev, " %16D\n", fib->data, " "); device_printf(sc->aac_dev, " %16D\n", fib->data + 16, " "); break; } } /* * Describe an AIF we have received. */ void aacraid_print_aif(struct aac_softc *sc, struct aac_aif_command *aif) { switch(aif->command) { case AifCmdEventNotify: device_printf(sc->aac_dev, "EventNotify(%d)\n", aif->seqNumber); switch(aif->data.EN.type) { case AifEnGeneric: /* Generic notification */ device_printf(sc->aac_dev, "(Generic) %.*s\n", (int)sizeof(aif->data.EN.data.EG), aif->data.EN.data.EG.text); break; case AifEnTaskComplete: /* Task has completed */ device_printf(sc->aac_dev, "(TaskComplete)\n"); break; case AifEnConfigChange: /* Adapter configuration change * occurred */ device_printf(sc->aac_dev, "(ConfigChange)\n"); break; case AifEnContainerChange: /* Adapter specific container * configuration change */ device_printf(sc->aac_dev, "(ContainerChange) " "container %d,%d\n", aif->data.EN.data.ECC.container[0], aif->data.EN.data.ECC.container[1]); break; case AifEnDeviceFailure: /* SCSI device failed */ device_printf(sc->aac_dev, "(DeviceFailure) " "handle %d\n", aif->data.EN.data.EDF.deviceHandle); break; case AifEnMirrorFailover: /* Mirror failover started */ device_printf(sc->aac_dev, "(MirrorFailover) " "container %d failed, " "migrating from slice %d to %d\n", aif->data.EN.data.EMF.container, aif->data.EN.data.EMF.failedSlice, aif->data.EN.data.EMF.creatingSlice); break; case AifEnContainerEvent: /* Significant container * event */ device_printf(sc->aac_dev, "(ContainerEvent) " "container %d event " "%d\n", aif->data.EN.data.ECE.container, aif->data.EN.data.ECE.eventType); break; case AifEnFileSystemChange: /* File system changed */ device_printf(sc->aac_dev, "(FileSystemChange)\n"); break; case AifEnConfigPause: /* Container pause event */ device_printf(sc->aac_dev, "(ConfigPause)\n"); break; case AifEnConfigResume: /* Container resume event */ device_printf(sc->aac_dev, "(ConfigResume)\n"); break; case AifEnFailoverChange: /* Failover space assignment * changed */ device_printf(sc->aac_dev, "(FailoverChange)\n"); break; case AifEnRAID5RebuildDone: /* RAID5 rebuild finished */ device_printf(sc->aac_dev, "(RAID5RebuildDone)\n"); break; case AifEnEnclosureManagement: /* Enclosure management event */ device_printf(sc->aac_dev, "(EnclosureManagement) " "EMPID %d unit %d " "event %d\n", aif->data.EN.data.EEE.empID, aif->data.EN.data.EEE.unitID, aif->data.EN.data.EEE.eventType); break; case AifEnBatteryEvent: /* Significant NV battery * event */ device_printf(sc->aac_dev, "(BatteryEvent) %d " "(state was %d, is %d\n", aif->data.EN.data.EBE.transition_type, aif->data.EN.data.EBE.current_state, aif->data.EN.data.EBE.prior_state); break; case AifEnAddContainer: /* A new container was * created. */ device_printf(sc->aac_dev, "(AddContainer)\n"); break; case AifEnDeleteContainer: /* A container was deleted. */ device_printf(sc->aac_dev, "(DeleteContainer)\n"); break; case AifEnBatteryNeedsRecond: /* The battery needs * reconditioning */ device_printf(sc->aac_dev, "(BatteryNeedsRecond)\n"); break; case AifEnClusterEvent: /* Some cluster event */ device_printf(sc->aac_dev, "(ClusterEvent) event %d\n", aif->data.EN.data.ECLE.eventType); break; case AifEnDiskSetEvent: /* A disk set event occurred. */ device_printf(sc->aac_dev, "(DiskSetEvent) event %d " "diskset %jd creator %jd\n", aif->data.EN.data.EDS.eventType, (intmax_t)aif->data.EN.data.EDS.DsNum, (intmax_t)aif->data.EN.data.EDS.CreatorId); break; case AifDenMorphComplete: /* A morph operation * completed */ device_printf(sc->aac_dev, "(MorphComplete)\n"); break; case AifDenVolumeExtendComplete: /* A volume expand operation * completed */ device_printf(sc->aac_dev, "(VolumeExtendComplete)\n"); break; default: device_printf(sc->aac_dev, "(%d)\n", aif->data.EN.type); break; } break; case AifCmdJobProgress: { char *status; switch(aif->data.PR[0].status) { case AifJobStsSuccess: status = "success"; break; case AifJobStsFinished: status = "finished"; break; case AifJobStsAborted: status = "aborted"; break; case AifJobStsFailed: status = "failed"; break; case AifJobStsSuspended: status = "suspended"; break; case AifJobStsRunning: status = "running"; break; default: status = "unknown status"; break; } device_printf(sc->aac_dev, "JobProgress (%d) - %s (%d, %d)\n", aif->seqNumber, status, aif->data.PR[0].currentTick, aif->data.PR[0].finalTick); switch(aif->data.PR[0].jd.type) { case AifJobScsiZero: /* SCSI dev clear operation */ device_printf(sc->aac_dev, "(ScsiZero) handle %d\n", aif->data.PR[0].jd.client.scsi_dh); break; case AifJobScsiVerify: /* SCSI device Verify operation * NO REPAIR */ device_printf(sc->aac_dev, "(ScsiVerify) handle %d\n", aif->data.PR[0].jd.client.scsi_dh); break; case AifJobScsiExercise: /* SCSI device Exercise * operation */ device_printf(sc->aac_dev, "(ScsiExercise) handle %d\n", aif->data.PR[0].jd.client.scsi_dh); break; case AifJobScsiVerifyRepair: /* SCSI device Verify operation * WITH repair */ device_printf(sc->aac_dev, "(ScsiVerifyRepair) handle %d\n", aif->data.PR[0].jd.client.scsi_dh); break; case AifJobCtrZero: /* Container clear operation */ device_printf(sc->aac_dev, "(ContainerZero) container %d\n", aif->data.PR[0].jd.client.container.src); break; case AifJobCtrCopy: /* Container copy operation */ device_printf(sc->aac_dev, "(ContainerCopy) container %d to %d\n", aif->data.PR[0].jd.client.container.src, aif->data.PR[0].jd.client.container.dst); break; case AifJobCtrCreateMirror: /* Container Create Mirror * operation */ device_printf(sc->aac_dev, "(ContainerCreateMirror) container %d\n", aif->data.PR[0].jd.client.container.src); /* XXX two containers? */ break; case AifJobCtrMergeMirror: /* Container Merge Mirror * operation */ device_printf(sc->aac_dev, "(ContainerMergeMirror) container %d\n", aif->data.PR[0].jd.client.container.src); /* XXX two containers? */ break; case AifJobCtrScrubMirror: /* Container Scrub Mirror * operation */ device_printf(sc->aac_dev, "(ContainerScrubMirror) container %d\n", aif->data.PR[0].jd.client.container.src); break; case AifJobCtrRebuildRaid5: /* Container Rebuild Raid5 * operation */ device_printf(sc->aac_dev, "(ContainerRebuildRaid5) container %d\n", aif->data.PR[0].jd.client.container.src); break; case AifJobCtrScrubRaid5: /* Container Scrub Raid5 * operation */ device_printf(sc->aac_dev, "(ContainerScrubRaid5) container %d\n", aif->data.PR[0].jd.client.container.src); break; case AifJobCtrMorph: /* Container morph operation */ device_printf(sc->aac_dev, "(ContainerMorph) container %d\n", aif->data.PR[0].jd.client.container.src); /* XXX two containers? */ break; case AifJobCtrPartCopy: /* Container Partition copy * operation */ device_printf(sc->aac_dev, "(ContainerPartCopy) container %d to " "%d\n", aif->data.PR[0].jd.client.container.src, aif->data.PR[0].jd.client.container.dst); break; case AifJobCtrRebuildMirror: /* Container Rebuild Mirror * operation */ device_printf(sc->aac_dev, "(ContainerRebuildMirror) container " "%d\n", aif->data.PR[0].jd.client.container.src); break; case AifJobCtrCrazyCache: /* crazy cache */ device_printf(sc->aac_dev, "(ContainerCrazyCache) container %d\n", aif->data.PR[0].jd.client.container.src); /* XXX two containers? */ break; case AifJobFsCreate: /* File System Create * operation */ device_printf(sc->aac_dev, "(FsCreate)\n"); break; case AifJobFsVerify: /* File System Verify * operation */ device_printf(sc->aac_dev, "(FsVerivy)\n"); break; case AifJobFsExtend: /* File System Extend * operation */ device_printf(sc->aac_dev, "(FsExtend)\n"); break; case AifJobApiFormatNTFS: /* Format a drive to NTFS */ device_printf(sc->aac_dev, "(FormatNTFS)\n"); break; case AifJobApiFormatFAT: /* Format a drive to FAT */ device_printf(sc->aac_dev, "(FormatFAT)\n"); break; case AifJobApiUpdateSnapshot: /* update the read/write half * of a snapshot */ device_printf(sc->aac_dev, "(UpdateSnapshot)\n"); break; case AifJobApiFormatFAT32: /* Format a drive to FAT32 */ device_printf(sc->aac_dev, "(FormatFAT32)\n"); break; case AifJobCtlContinuousCtrVerify: /* Adapter operation */ device_printf(sc->aac_dev, "(ContinuousCtrVerify)\n"); break; default: device_printf(sc->aac_dev, "(%d)\n", aif->data.PR[0].jd.type); break; } break; } case AifCmdAPIReport: device_printf(sc->aac_dev, "APIReport (%d)\n", aif->seqNumber); break; case AifCmdDriverNotify: device_printf(sc->aac_dev, "DriverNotify (%d)\n", aif->seqNumber); break; default: device_printf(sc->aac_dev, "AIF %d (%d)\n", aif->command, aif->seqNumber); break; } } #endif /* AACRAID_DEBUG */ /* * Debug flags to be put into the HBA flags field when initialized */ const unsigned long aacraid_debug_flags = /* Variable to setup with above flags. */ /* HBA_FLAGS_DBG_KERNEL_PRINT_B | */ HBA_FLAGS_DBG_FW_PRINT_B | /* HBA_FLAGS_DBG_FUNCTION_ENTRY_B | */ HBA_FLAGS_DBG_FUNCTION_EXIT_B | HBA_FLAGS_DBG_ERROR_B | HBA_FLAGS_DBG_INIT_B | /* HBA_FLAGS_DBG_OS_COMMANDS_B | */ /* HBA_FLAGS_DBG_SCAN_B | */ /* HBA_FLAGS_DBG_COALESCE_B | */ /* HBA_FLAGS_DBG_IOCTL_COMMANDS_B | */ /* HBA_FLAGS_DBG_SYNC_COMMANDS_B | */ HBA_FLAGS_DBG_COMM_B | /* HBA_FLAGS_DBG_AIF_B | */ /* HBA_FLAGS_DBG_CSMI_COMMANDS_B | */ HBA_FLAGS_DBG_DEBUG_B | /* HBA_FLAGS_DBG_FLAGS_MASK | */ 0; int aacraid_get_fw_debug_buffer(struct aac_softc *sc) { u_int32_t MonDriverBufferPhysAddrLow = 0; u_int32_t MonDriverBufferPhysAddrHigh = 0; u_int32_t MonDriverBufferSize = 0; u_int32_t MonDriverHeaderSize = 0; /* * Get the firmware print buffer parameters from the firmware * If the command was successful map in the address. */ if (!aacraid_sync_command(sc, AAC_MONKER_GETDRVPROP, 0, 0, 0, 0, NULL, NULL)) { MonDriverBufferPhysAddrLow = AAC_GET_MAILBOX(sc, 1); MonDriverBufferPhysAddrHigh = AAC_GET_MAILBOX(sc, 2); MonDriverBufferSize = AAC_GET_MAILBOX(sc, 3); MonDriverHeaderSize = AAC_GET_MAILBOX(sc, 4); if (MonDriverBufferSize) { unsigned long Offset = MonDriverBufferPhysAddrLow - rman_get_start(sc->aac_regs_res1); /* * See if the address is already mapped in and if so set it up * from the base address */ if ((MonDriverBufferPhysAddrHigh == 0) && (Offset + MonDriverBufferSize < rman_get_size(sc->aac_regs_res1))) { sc->DebugOffset = Offset; sc->DebugHeaderSize = MonDriverHeaderSize; sc->FwDebugBufferSize = MonDriverBufferSize; sc->FwDebugFlags = 0; sc->DebugFlags = aacraid_debug_flags; return 1; } } } /* * The GET_DRIVER_BUFFER_PROPERTIES command failed */ return 0; } #define PRINT_TIMEOUT 250000 /* 1/4 second */ void aacraid_fw_printf(struct aac_softc *sc, unsigned long PrintFlags, const char * fmt, ...) { va_list args; u_int32_t Count, i; char PrintBuffer_P[PRINT_BUFFER_SIZE]; unsigned long PrintType; PrintType = PrintFlags & ~(HBA_FLAGS_DBG_KERNEL_PRINT_B|HBA_FLAGS_DBG_FW_PRINT_B); if (((PrintType!=0) && (sc!=NULL) && ((sc->DebugFlags & PrintType)==0)) || ((sc!=NULL) && (sc->DebugFlags & (HBA_FLAGS_DBG_KERNEL_PRINT_B|HBA_FLAGS_DBG_FW_PRINT_B)) == 0)) return; /* * Set up parameters and call sprintf function to format the data */ va_start(args, fmt); vsprintf(PrintBuffer_P, fmt, args); va_end(args); /* * Make sure the HBA structure has been passed in for this section */ if ((sc != NULL) && (sc->FwDebugBufferSize)) { /* * If we are set up for a Firmware print */ if ((sc->DebugFlags & HBA_FLAGS_DBG_FW_PRINT_B) && ((PrintFlags & (HBA_FLAGS_DBG_KERNEL_PRINT_B|HBA_FLAGS_DBG_FW_PRINT_B)) != HBA_FLAGS_DBG_KERNEL_PRINT_B)) { /* * Make sure the string size is within boundaries */ Count = strlen(PrintBuffer_P); if (Count > sc->FwDebugBufferSize) Count = (u_int16_t)sc->FwDebugBufferSize; /* * Wait for no more than PRINT_TIMEOUT for the previous * message length to clear (the handshake). */ for (i = 0; i < PRINT_TIMEOUT; ++i) { if (!AAC_MEM1_GETREG4(sc, sc->DebugOffset + FW_DEBUG_STR_LENGTH_OFFSET)) { break; } DELAY(1); } /* * If the Length is clear, copy over the message, the * flags, and the length. Make sure the length is the * last because that is the signal for the Firmware to * pick it up. */ if (!AAC_MEM1_GETREG4(sc, sc->DebugOffset + FW_DEBUG_STR_LENGTH_OFFSET)) { for (i = 0; i < Count; ++i) { AAC_MEM1_SETREG1(sc, sc->DebugOffset + sc->DebugHeaderSize + i, PrintBuffer_P[i]); } AAC_MEM1_SETREG4(sc, sc->DebugOffset + FW_DEBUG_FLAGS_OFFSET, sc->FwDebugFlags); AAC_MEM1_SETREG4(sc, sc->DebugOffset + FW_DEBUG_STR_LENGTH_OFFSET, Count); } else sc->DebugFlags &= ~HBA_FLAGS_DBG_FW_PRINT_B; } /* * If the Kernel Debug Print flag is set, send it off to the * Kernel debugger */ if ((sc->DebugFlags & HBA_FLAGS_DBG_KERNEL_PRINT_B) && ((PrintFlags & (HBA_FLAGS_DBG_KERNEL_PRINT_B|HBA_FLAGS_DBG_FW_PRINT_B)) != HBA_FLAGS_DBG_FW_PRINT_B)) { if (sc->FwDebugFlags & FW_DEBUG_FLAGS_NO_HEADERS_B) printf ("%s\n", PrintBuffer_P); else device_printf (sc->aac_dev, "%s\n", PrintBuffer_P); } } else { /* * No HBA structure passed in so it has to be for the Kernel Debugger */ if ((sc != NULL) && (sc->FwDebugFlags & FW_DEBUG_FLAGS_NO_HEADERS_B)) printf ("%s\n", PrintBuffer_P); else if (sc != NULL) device_printf (sc->aac_dev, "%s\n", PrintBuffer_P); else printf("%s\n", PrintBuffer_P); } } void aacraid_fw_print_mem(struct aac_softc *sc, unsigned long PrintFlags, u_int8_t *Addr, int Count) { int Offset, i; u_int32_t DebugFlags = 0; char Buffer[100]; char *LineBuffer_P; /* * If we have an HBA structure, save off the flags and set the no * headers flag so we don't have garbage between our lines of data */ if (sc != NULL) { DebugFlags = sc->FwDebugFlags; sc->FwDebugFlags |= FW_DEBUG_FLAGS_NO_HEADERS_B; } Offset = 0; /* * Loop through all the data */ while (Offset < Count) { /* * We will format each line into a buffer and then print out * the entire line so set the pointer to the beginning of the * buffer */ LineBuffer_P = Buffer; /* * Set up the address in HEX */ sprintf(LineBuffer_P, "\n%04x ", Offset); LineBuffer_P += 6; /* * Set up 16 bytes in HEX format */ for (i = 0; i < 16; ++i) { /* * If we are past the count of data bytes to output, * pad with blanks */ if ((Offset + i) >= Count) sprintf (LineBuffer_P, " "); else sprintf (LineBuffer_P, "%02x ", Addr[Offset+i]); LineBuffer_P += 3; /* * At the mid point we will put in a divider */ if (i == 7) { sprintf (LineBuffer_P, "- "); LineBuffer_P += 2; } } /* * Now do the same 16 bytes at the end of the line in ASCII * format */ sprintf (LineBuffer_P, " "); LineBuffer_P += 2; for (i = 0; i < 16; ++i) { /* * If all data processed, OUT-O-HERE */ if ((Offset + i) >= Count) break; /* * If this is a printable ASCII character, convert it */ if ((Addr[Offset+i] > 0x1F) && (Addr[Offset+i] < 0x7F)) sprintf (LineBuffer_P, "%c", Addr[Offset+i]); else sprintf (LineBuffer_P, "."); ++LineBuffer_P; } /* * The line is now formatted, so print it out */ aacraid_fw_printf(sc, PrintFlags, "%s", Buffer); /* * Bump the offset by 16 for the next line */ Offset += 16; } /* * Restore the saved off flags */ if (sc != NULL) sc->FwDebugFlags = DebugFlags; }