summaryrefslogtreecommitdiffstats
path: root/src/usr/mmio
diff options
context:
space:
mode:
Diffstat (limited to 'src/usr/mmio')
-rw-r--r--src/usr/mmio/makefile4
-rw-r--r--src/usr/mmio/mmio.C832
-rw-r--r--src/usr/mmio/mmio.H64
-rw-r--r--src/usr/mmio/test/makefile31
-rw-r--r--src/usr/mmio/test/mmiotest.H109
5 files changed, 1031 insertions, 9 deletions
diff --git a/src/usr/mmio/makefile b/src/usr/mmio/makefile
index 9b8d03ded..cea686f85 100644
--- a/src/usr/mmio/makefile
+++ b/src/usr/mmio/makefile
@@ -25,6 +25,10 @@
ROOTPATH = ../../..
MODULE = mmio
+EXTRAINCDIR += ${ROOTPATH}/src/import/chips/p9/common/include/
+EXTRAINCDIR += ${ROOTPATH}/src/import/hwpf/fapi2/include/
+EXTRAINCDIR += ${ROOTPATH}/src/include/usr/fapi2/
+
#include unique object modules
OBJS += mmio.o
diff --git a/src/usr/mmio/mmio.C b/src/usr/mmio/mmio.C
index 2b0849aa5..b6dcebbc2 100644
--- a/src/usr/mmio/mmio.C
+++ b/src/usr/mmio/mmio.C
@@ -31,31 +31,845 @@
#include <targeting/common/targetservice.H>
#include <arch/ppc.H>
+#include "mmio.H"
#include <mmio/mmio.H>
+#include <mmio/mmio_reasoncodes.H>
+
+#include <p9a_mc_scom_addresses.H>
+#include <p9a_mc_scom_addresses_fld.H>
+#include <error_info_defs.H>
+
+// Trace definition
+trace_desc_t* g_trac_mmio = NULL;
+TRAC_INIT(&g_trac_mmio, MMIO_COMP_NAME, 2*KILOBYTE, TRACE::BUFFER_SLOW);
namespace MMIO
{
-void mmioSetup()
+// Helper function declarations (definitions at the bottom of this file)
+static
+TARGETING::TargetHandle_t getParentProc(TARGETING::TargetHandle_t i_target);
+static
+errlHndl_t getProcScom(TARGETING::TargetHandle_t i_target,
+ uint64_t i_scomAddr,
+ uint64_t &o_scomData);
+static
+errlHndl_t setProcScom(TARGETING::TargetHandle_t i_target,
+ uint64_t i_scomAddr,
+ uint64_t i_scomData);
+static
+void *mmio_memcpy(void *vdest, const void *vsrc, size_t len);
+
+
+errlHndl_t mmioSetup()
{
+ errlHndl_t l_err = nullptr;
+
+ TRACDCOMP(g_trac_mmio, ENTER_MRK"mmioSetup");
+ // called from istep 12.3
+
+ do
+ {
+ // Get the base BAR address for an OCMB and use it to calculate the base
+ // BAR address for OCMB0 on PROC0 (beginning of reserved physical memory
+ // for all OCMBs).
+ // Each pair of OCMBs uses 8GB of interleaved memory,
+ // the second OCMB's memory starts 2GB after the first's.
+ TARGETING::TargetHandleList l_omiTargetList;
+
+ getAllChiplets(l_omiTargetList, TARGETING::TYPE_OMI);
+ if (l_omiTargetList.size() == 0)
+ {
+ TRACDCOMP(g_trac_mmio,
+ INFO_MRK"mmioSetup: Exiting, non-OMI system");
+ break;
+ }
+
+ auto l_omi = l_omiTargetList[0];
+ auto l_omiParentProc = getParentProc(l_omi);
+ if (l_omiParentProc == nullptr)
+ {
+ TRACFCOMP(g_trac_mmio, ERR_MRK
+ "mmioSetup: Unable to find the parent processor for an"
+ " OMI(0x%X).",
+ l_omi->getAttr<TARGETING::ATTR_HUID>());
+ /*@
+ * @errortype
+ * @moduleid MMIO::MOD_MMIO_SETUP
+ * @reasoncode MMIO::RC_PROC_NOT_FOUND
+ * @userdata1 Target huid
+ * @userdata2 None
+ * @devdesc mmioSetup> Unable to find parent processor for OMI.
+ * @custdesc Unexpected memory subsystem firmware error.
+ */
+ l_err = new ERRORLOG::ErrlEntry(
+ ERRORLOG::ERRL_SEV_UNRECOVERABLE,
+ MMIO::MOD_MMIO_SETUP,
+ MMIO::RC_PROC_NOT_FOUND,
+ l_omi->getAttr<TARGETING::ATTR_HUID>(),
+ 0,
+ ERRORLOG::ErrlEntry::ADD_SW_CALLOUT);
+
+ break;
+ }
+
+ // There's a 1:1 relationship between OMIs and OCMBs, so we can directly
+ // relate the OMI position and BAR base addr to its associated OCMB.
+
+ // Get the position of the random OCMB. (OCMBs 0-15 will be on proc0,
+ // 16-31 on proc1, etc)
+ auto l_ocmbPos = l_omi->getAttr<TARGETING::ATTR_CHIP_UNIT>();
+ l_ocmbPos += l_omiParentProc->getAttr<TARGETING::ATTR_POSITION>() *
+ fapi2::MAX_OMI_PER_PROC;
+
+ // Get the base BAR address of the OCMB.
+ auto l_ocmbBaseAddr =
+ l_omi->getAttr<TARGETING::ATTR_OMI_INBAND_BAR_BASE_ADDR_OFFSET>();
+
+ // Calculate the base BAR address of OCMB0 on PROC0 by subtracting 8GB
+ // for every pair of OCMBs beyond the first pair, and subtract an
+ // additional 2GB if the initial OCMB is the second in a pair.
+ l_ocmbBaseAddr -= ((l_ocmbPos / 2) * 8 * GIGABYTE) +
+ ((l_ocmbPos % 2) * 2 * GIGABYTE);
+
+ // map 8 OCMBs at a time, set MMIO_VM_ADDR on each OCMB
+ //
+ // loop through all the procs
+ // call mmio_dev_map() on OCMBs 0-7 and 8-15
+ // set VM_ADDR on each OCMB
+ // each pair of OCMBs has their memories interleaved with their
+ // 2GB config sections together and their 2GB mmio sections
+ // together, we will be setting VM_ADDR to point to the cfg
+ // section of each ocmb
+ // Example
+ // 0GB ocmb0 cfg
+ // 2GB ocmb1 cfg
+ // 4GB ocmb0 mmio
+ // 6GB ocmb1 mmio
+ TARGETING::TargetHandleList l_procTargetList;
+
+ getAllChips(l_procTargetList, TARGETING::TYPE_PROC);
+ for (auto & l_procTarget: l_procTargetList)
+ {
+ // map all 16 OCMBs, 8 OCMBs (32GB) at a time
+ uint64_t *l_virtAddr[2] = {nullptr};
+ uint32_t l_procNum =
+ l_procTarget->getAttr<TARGETING::ATTR_POSITION>();
+ uint64_t l_realAddr =
+ l_ocmbBaseAddr + (l_procNum * 2 * THIRTYTWO_GB);
+
+ l_virtAddr[0] = static_cast<uint64_t *>
+ (mmio_dev_map(reinterpret_cast<void *>(l_realAddr),
+ THIRTYTWO_GB));
+ l_realAddr += THIRTYTWO_GB;
+ l_virtAddr[1] = static_cast<uint64_t *>
+ (mmio_dev_map(reinterpret_cast<void *>(l_realAddr),
+ THIRTYTWO_GB));
+
+ // set VM_ADDR on each OCMB
+ TARGETING::TargetHandleList l_ocmbTargetList;
+ l_ocmbTargetList.clear();
+ getChildAffinityTargets(l_ocmbTargetList, l_procTarget,
+ TARGETING::CLASS_UNIT, TARGETING::TYPE_OCMB_CHIP);
+ for (auto & l_ocmbTarget: l_ocmbTargetList)
+ {
+ uint64_t l_ocmbVmAddr = 0;
+ uint32_t l_ocmbNum =
+ l_ocmbTarget->getAttr<TARGETING::ATTR_POSITION>();
+
+ // OCMBs 0-7 in first map, 8-15 in second map
+ l_ocmbVmAddr =
+ reinterpret_cast<uint64_t>(l_virtAddr[l_ocmbNum / 8]);
+
+ // Each pair of OCMBs uses 8GB of interleaved memory,
+ // the second OCMB's memory starts 2GB after the first's
+ l_ocmbVmAddr += ((l_ocmbNum / 2) * 8 * GIGABYTE) +
+ ((l_ocmbNum % 2) * 2 * GIGABYTE);
+
+ l_ocmbTarget->
+ setAttr<TARGETING::ATTR_MMIO_VM_ADDR>(l_ocmbVmAddr);
+ }
+ }
+ } while(0);
+
+ TRACDCOMP(g_trac_mmio, EXIT_MRK"mmioSetup");
+
+ return l_err;
}
// Direct OCMB reads and writes to the device's memory mapped memory.
DEVICE_REGISTER_ROUTE(DeviceFW::WILDCARD,
DeviceFW::MMIO,
TARGETING::TYPE_OCMB_CHIP,
- mmioPerformOp);
-
-errlHndl_t mmioPerformOp(DeviceFW::OperationType i_opType,
- TARGETING::TargetHandle_t i_target,
- void* io_buffer,
- size_t& io_buflen,
- int64_t i_accessType,
- va_list i_args)
+ ocmbMmioPerformOp);
+
+errlHndl_t ocmbMmioPerformOp(DeviceFW::OperationType i_opType,
+ TARGETING::TargetHandle_t i_target,
+ void* io_buffer,
+ size_t& io_buflen,
+ int64_t i_accessType,
+ va_list i_args)
{
errlHndl_t l_err = nullptr;
+ uint64_t l_offset = va_arg(i_args, uint64_t);
+ uint64_t l_accessLimit = va_arg(i_args, uint64_t);
+
+ TRACDCOMP(g_trac_mmio, ENTER_MRK"ocmbMmioPerformOp");
+ TRACDCOMP(g_trac_mmio, INFO_MRK"op=%d, target=0x%.8X",
+ i_opType, i_target);
+ TRACDCOMP(g_trac_mmio, INFO_MRK"buffer=%p, length=%d, accessType=%ld",
+ io_buffer, io_buflen, i_accessType);
+ TRACDCOMP(g_trac_mmio, INFO_MRK"offset=0x%lX, accessLimit=%ld",
+ l_offset, l_accessLimit);
+
+ do
+ {
+ uint64_t l_addr = i_target->getAttr<TARGETING::ATTR_MMIO_VM_ADDR>();
+
+ if (l_addr == 0)
+ {
+ TRACFCOMP(g_trac_mmio, ERR_MRK
+ "ocmbMmioPerformOp: MMIO has not been initialized!");
+
+ /*@
+ * @errortype
+ * @moduleid MMIO::MOD_MMIO_PERFORM_OP
+ * @reasoncode MMIO::RC_INVALID_SETUP
+ * @userdata1[0:31] Target huid
+ * @userdata1[32:63] Data Offset, if >= 4GB then subtract 2GB
+ * (allows offsets to fit in 32 bits)
+ * @userdata2[0:0] Operation Type
+ * @userdata2[28:31] Access Limit
+ * @userdata2[32:63] Buffer Length
+ * @devdesc mmioPerformOp> A MMIO operation was attempted
+ * before MMIO was initialized.
+ * @custdesc Unexpected memory subsystem firmware error.
+ */
+ l_err = new ERRORLOG::ErrlEntry(
+ ERRORLOG::ERRL_SEV_UNRECOVERABLE,
+ MMIO::MOD_MMIO_PERFORM_OP,
+ MMIO::RC_INVALID_SETUP,
+ TWO_UINT32_TO_UINT64(
+ i_target->getAttr<TARGETING::ATTR_HUID>(),
+ (l_offset < (4 * GIGABYTE)) ?
+ (l_offset) :
+ (l_offset - (2 * GIGABYTE))),
+ TWO_UINT32_TO_UINT64(
+ (i_opType << 31) | l_accessLimit,
+ io_buflen),
+ ERRORLOG::ErrlEntry::ADD_SW_CALLOUT);
+
+ break;
+ }
+
+ if (io_buffer == nullptr)
+ {
+ TRACFCOMP(g_trac_mmio, ERR_MRK
+ "ocmbMmioPerformOp: buffer is invalid!");
+
+ /*@
+ * @errortype
+ * @moduleid MMIO::MOD_MMIO_PERFORM_OP
+ * @reasoncode MMIO::RC_INVALID_BUFFER
+ * @userdata1[0:31] Target huid
+ * @userdata1[32:63] Data Offset, if >= 4GB then subtract 2GB
+ * (allows offsets to fit in 32 bits)
+ * @userdata2[0:0] Operation Type
+ * @userdata2[28:31] Access Limit
+ * @userdata2[32:63] Buffer Length
+ * @devdesc mmioPerformOp> Invalid data buffer for a MMIO
+ * operation.
+ * @custdesc Unexpected memory subsystem firmware error.
+ */
+ l_err = new ERRORLOG::ErrlEntry(
+ ERRORLOG::ERRL_SEV_UNRECOVERABLE,
+ MMIO::MOD_MMIO_PERFORM_OP,
+ MMIO::RC_INVALID_BUFFER,
+ TWO_UINT32_TO_UINT64(
+ i_target->getAttr<TARGETING::ATTR_HUID>(),
+ (l_offset < (4 * GIGABYTE)) ?
+ (l_offset) :
+ (l_offset - (2 * GIGABYTE))),
+ TWO_UINT32_TO_UINT64(
+ (i_opType << 31) | l_accessLimit,
+ io_buflen),
+ ERRORLOG::ErrlEntry::ADD_SW_CALLOUT);
+
+ break;
+ }
+
+ switch (l_accessLimit) {
+ case 0:
+ l_accessLimit = io_buflen; // no access size restriction
+ case 4:
+ case 8:
+ break; // expected values
+ default:
+ TRACFCOMP(g_trac_mmio, ERR_MRK
+ "ocmbMmioPerformOp: accessLimit(%ld) should be 0, 4 or 8!!!",
+ l_accessLimit);
+
+ /*@
+ * @errortype
+ * @moduleid MMIO::MOD_MMIO_PERFORM_OP
+ * @reasoncode MMIO::RC_INVALID_ACCESS_LIMIT
+ * @userdata1[0:31] Target huid
+ * @userdata1[32:63] Data Offset, if >= 4GB then subtract 2GB
+ * (allows offsets to fit in 32 bits)
+ * @userdata2[0:0] Operation Type
+ * @userdata2[28:31] Access Limit
+ * @userdata2[32:63] Buffer Length
+ * @devdesc mmioPerformOp> Specified access limit was
+ * invalid for a MMIO operation.
+ * @custdesc Unexpected memory subsystem firmware error.
+ */
+ l_err = new ERRORLOG::ErrlEntry(
+ ERRORLOG::ERRL_SEV_UNRECOVERABLE,
+ MMIO::MOD_MMIO_PERFORM_OP,
+ MMIO::RC_INVALID_ACCESS_LIMIT,
+ TWO_UINT32_TO_UINT64(
+ i_target->getAttr<TARGETING::ATTR_HUID>(),
+ (l_offset < (4 * GIGABYTE)) ?
+ (l_offset) :
+ (l_offset - (2 * GIGABYTE))),
+ TWO_UINT32_TO_UINT64(
+ (i_opType << 31) | l_accessLimit,
+ io_buflen),
+ ERRORLOG::ErrlEntry::ADD_SW_CALLOUT);
+ break;
+ }
+
+ if (l_err)
+ {
+ break;
+ }
+
+ if (io_buflen < l_accessLimit)
+ {
+ TRACFCOMP(g_trac_mmio, ERR_MRK
+ "ocmbMmioPerformOp: buffer is too small for the"
+ " request, buflen=%d, accessLimit=%ld",
+ io_buflen, l_accessLimit);
+
+ /*@
+ * @errortype
+ * @moduleid MMIO::MOD_MMIO_PERFORM_OP
+ * @reasoncode MMIO::RC_INSUFFICIENT_BUFFER
+ * @userdata1[0:31] Target huid
+ * @userdata1[32:63] Data Offset, if >= 4GB then subtract 2GB
+ * (allows offsets to fit in 32 bits)
+ * @userdata2[0:0] Operation Type
+ * @userdata2[28:31] Access Limit
+ * @userdata2[32:63] Buffer Length
+ * @devdesc mmioPerformOp> Data buffer too small for a
+ * MMIO operation.
+ * @custdesc Unexpected memory subsystem firmware error.
+ */
+ l_err = new ERRORLOG::ErrlEntry(
+ ERRORLOG::ERRL_SEV_UNRECOVERABLE,
+ MMIO::MOD_MMIO_PERFORM_OP,
+ MMIO::RC_INSUFFICIENT_BUFFER,
+ TWO_UINT32_TO_UINT64(
+ i_target->getAttr<TARGETING::ATTR_HUID>(),
+ (l_offset < (4 * GIGABYTE)) ?
+ (l_offset) :
+ (l_offset - (2 * GIGABYTE))),
+ TWO_UINT32_TO_UINT64(
+ (i_opType << 31) | l_accessLimit,
+ io_buflen),
+ ERRORLOG::ErrlEntry::ADD_SW_CALLOUT);
+
+ break;
+ }
+
+ if (io_buflen % l_accessLimit)
+ {
+ TRACFCOMP(g_trac_mmio, ERR_MRK
+ "ocmbMmioPerformOp: buffer length must be a"
+ " multiple of the access limit,"
+ " buflen=%d, accessLimit=%ld",
+ io_buflen, l_accessLimit);
+
+ /*@
+ * @errortype
+ * @moduleid MMIO::MOD_MMIO_PERFORM_OP
+ * @reasoncode MMIO::RC_INCORRECT_BUFFER_LENGTH
+ * @userdata1[0:31] Target huid
+ * @userdata1[32:63] Data Offset, if >= 4GB then subtract 2GB
+ * (allows offsets to fit in 32 bits)
+ * @userdata2[0:0] Operation Type
+ * @userdata2[28:31] Access Limit
+ * @userdata2[32:63] Buffer Length
+ * @devdesc mmioPerformOp> Buffer length not a multiple
+ * of access limit.
+ * @custdesc Unexpected memory subsystem firmware error.
+ */
+ l_err = new ERRORLOG::ErrlEntry(
+ ERRORLOG::ERRL_SEV_UNRECOVERABLE,
+ MMIO::MOD_MMIO_PERFORM_OP,
+ MMIO::RC_INCORRECT_BUFFER_LENGTH,
+ TWO_UINT32_TO_UINT64(
+ i_target->getAttr<TARGETING::ATTR_HUID>(),
+ (l_offset < (4 * GIGABYTE)) ?
+ (l_offset) :
+ (l_offset - (2 * GIGABYTE))),
+ TWO_UINT32_TO_UINT64(
+ (i_opType << 31) | l_accessLimit,
+ io_buflen),
+ ERRORLOG::ErrlEntry::ADD_SW_CALLOUT);
+
+ break;
+ }
+
+ if (!(((l_offset >= 0) && (l_offset < (2 * GIGABYTE))) ||
+ ((l_offset >= (4 * GIGABYTE)) && (l_offset < (6 * GIGABYTE)))))
+ {
+ TRACFCOMP(g_trac_mmio, ERR_MRK
+ "ocmbMmioPerformOp: offset(0x%lX) must be"
+ " either 0-2G or 4G-6G!",
+ l_offset);
+
+ /*@
+ * @errortype
+ * @moduleid MMIO::MOD_MMIO_PERFORM_OP
+ * @reasoncode MMIO::RC_INVALID_OFFSET
+ * @userdata1[0:31] Target huid
+ * @userdata1[32:63] Data Offset, if >= 4GB then subtract 2GB
+ * (allows offsets to fit in 32 bits)
+ * @userdata2[0:0] Operation Type
+ * @userdata2[28:31] Access Limit
+ * @userdata2[32:63] Buffer Length
+ * @devdesc mmioPerformOp> Invalid offset, requested
+ * address was out of range for a MMIO operation.
+ * @custdesc Unexpected memory subsystem firmware error.
+ */
+ l_err = new ERRORLOG::ErrlEntry(
+ ERRORLOG::ERRL_SEV_UNRECOVERABLE,
+ MMIO::MOD_MMIO_PERFORM_OP,
+ MMIO::RC_INVALID_OFFSET,
+ TWO_UINT32_TO_UINT64(
+ i_target->getAttr<TARGETING::ATTR_HUID>(),
+ (l_offset < (4 * GIGABYTE)) ?
+ (l_offset) :
+ (l_offset - (2 * GIGABYTE))),
+ TWO_UINT32_TO_UINT64(
+ (i_opType << 31) | l_accessLimit,
+ io_buflen),
+ ERRORLOG::ErrlEntry::ADD_SW_CALLOUT);
+
+ break;
+ }
+
+ if ( ((l_accessLimit == 4) || (l_accessLimit == 8)) &&
+ ((l_offset % l_accessLimit) != 0) )
+ {
+ TRACFCOMP(g_trac_mmio, ERR_MRK
+ "ocmbMmioPerformOp: offset must be aligned with access limit,"
+ " offset=0x%lX, accessLimit=%ld",
+ l_offset, l_accessLimit);
+
+ /*@
+ * @errortype
+ * @moduleid MMIO::MOD_MMIO_PERFORM_OP
+ * @reasoncode MMIO::RC_INVALID_OFFSET_ALIGNMENT
+ * @userdata1[0:31] Target huid
+ * @userdata1[32:63] Data Offset, if >= 4GB then subtract 2GB
+ * (allows offsets to fit in 32 bits)
+ * @userdata2[0:0] Operation Type
+ * @userdata2[28:31] Access Limit
+ * @userdata2[32:63] Buffer Length
+ * @devdesc mmioPerformOp> Requested MMIO address was not
+ * aligned properly for the associated device.
+ * @custdesc Unexpected memory subsystem firmware error.
+ */
+ l_err = new ERRORLOG::ErrlEntry(
+ ERRORLOG::ERRL_SEV_UNRECOVERABLE,
+ MMIO::MOD_MMIO_PERFORM_OP,
+ MMIO::RC_INVALID_OFFSET_ALIGNMENT,
+ TWO_UINT32_TO_UINT64(
+ i_target->getAttr<TARGETING::ATTR_HUID>(),
+ (l_offset < (4 * GIGABYTE)) ?
+ (l_offset) :
+ (l_offset - (2 * GIGABYTE))),
+ TWO_UINT32_TO_UINT64(
+ (i_opType << 31) | l_accessLimit,
+ io_buflen),
+ ERRORLOG::ErrlEntry::ADD_SW_CALLOUT);
+
+ break;
+ }
+
+ // TODO RTC 201493 - Remove these consts once HW group has defined them.
+ static const uint8_t P9A_MC_DSTLFIR_SUBCHANNEL_A_FAIL_ACTION = 20;
+ static const uint8_t P9A_MC_DSTLFIR_SUBCHANNEL_B_FAIL_ACTION = 21;
+
+ // read or write io_buflen bytes, l_accessLimit bytes at a time
+ uint8_t *mm_ptr = reinterpret_cast<uint8_t *>(l_addr + l_offset);
+ uint8_t *io_ptr = reinterpret_cast<uint8_t *>(io_buffer);
+ size_t bytes_read_or_written = 0;
+ for (size_t i = 0;i < io_buflen;i += l_accessLimit)
+ {
+ if (i_opType == DeviceFW::READ)
+ {
+ mmio_memcpy(io_ptr + i, mm_ptr + i, l_accessLimit);
+ eieio();
+ if (!memcmp(io_ptr + i,
+ &MMIO_OCMB_UE_DETECTED,
+ sizeof(MMIO_OCMB_UE_DETECTED)))
+ {
+ uint64_t scom_data = 0;
+ uint64_t scom_mask = 0;
+
+ TRACFCOMP(g_trac_mmio, ERR_MRK
+ "ocmbMmioPerformOp: unable to complete"
+ " MMIO read, SUE detected");
+
+ /*@
+ * @errortype
+ * @moduleid MMIO::MOD_MMIO_PERFORM_OP
+ * @reasoncode MMIO::RC_BAD_MMIO_READ
+ * @userdata1[0:31] Target huid
+ * @userdata1[32:63] Data Offset, if >= 4GB then subtract
+ * 2GB (allows offsets to fit in 32 bits)
+ * @userdata2[0:0] Operation Type
+ * @userdata2[28:31] Access Limit
+ * @userdata2[32:63] Buffer Length
+ * @devdesc mmioPerformOp> MMIO read of an OCMB
+ * failed.
+ * @custdesc Unexpected memory subsystem firmware
+ * error.
+ */
+ l_err = new ERRORLOG::ErrlEntry(
+ ERRORLOG::ERRL_SEV_UNRECOVERABLE,
+ MMIO::MOD_MMIO_PERFORM_OP,
+ MMIO::RC_BAD_MMIO_READ,
+ TWO_UINT32_TO_UINT64(
+ i_target->getAttr<TARGETING::ATTR_HUID>(),
+ (l_offset < (4 * GIGABYTE)) ?
+ (l_offset) :
+ (l_offset - (2 * GIGABYTE))),
+ TWO_UINT32_TO_UINT64(
+ (i_opType << 31) | l_accessLimit,
+ io_buflen),
+ ERRORLOG::ErrlEntry::NO_SW_CALLOUT);
+ // add OCMB to error log
+ l_err->addHwCallout(i_target,
+ HWAS::SRCI_PRIORITY_HIGH,
+ HWAS::DECONFIG,
+ HWAS::GARD_NULL);
+ l_err->addProcedureCallout(HWAS::EPUB_PRC_HB_CODE,
+ HWAS::SRCI_PRIORITY_LOW);
+ const auto plid = l_err->plid();
+
+ auto l_err2 = getProcScom(i_target,
+ P9A_MCC_USTLFIR,
+ scom_data);
+ if (l_err2)
+ {
+ l_err2->plid(plid);
+ errlCommit(l_err2, MMIO_COMP_ID);
+ }
+ else
+ {
+ scom_mask = (1ull << P9A_MC_USTLFIR_CHANA_BAD_DATA) |
+ (1ull << P9A_MC_USTLFIR_CHANB_BAD_DATA);
+ if (scom_data & scom_mask)
+ {
+ // TODO RTC 201588 - Error checking on Explorer side
+ TRACFCOMP(g_trac_mmio, ERR_MRK
+ "ocmbMmioPerformOp: there was an error on"
+ " the Explorer side, P9A_MCC_USTLFIR=0x%lX",
+ scom_data);
+
+ // Clear FIR bits
+ scom_data &= ~scom_mask;
+ l_err2 = setProcScom(i_target,
+ P9A_MCC_USTLFIR,
+ scom_data);
+ if (l_err2)
+ {
+ l_err2->plid(plid);
+ errlCommit(l_err2, MMIO_COMP_ID);
+ }
+ }
+ }
+
+ l_err2 = getProcScom(i_target,
+ P9A_MCC_DSTLFIR,
+ scom_data);
+ if (l_err2)
+ {
+ l_err2->plid(plid);
+ errlCommit(l_err2, MMIO_COMP_ID);
+ }
+ else
+ {
+ scom_mask =
+ (1ull << P9A_MC_DSTLFIR_SUBCHANNEL_A_FAIL_ACTION) |
+ (1ull << P9A_MC_DSTLFIR_SUBCHANNEL_B_FAIL_ACTION);
+ if (scom_data & scom_mask)
+ {
+ // A channel checkstop has occurred.
+ // TODO RTC 201778 - Channel Fail Handling for
+ // Explorer
+ TRACFCOMP(g_trac_mmio, ERR_MRK
+ "ocmbMmioPerformOp: there was an error on"
+ " the Explorer channel, P9A_MCC_DSTLFIR=0x%lX",
+ scom_data);
+ }
+ }
+
+ break;
+ }
+ }
+ else if (i_opType == DeviceFW::WRITE)
+ {
+ mmio_memcpy(mm_ptr + i, io_ptr + i, l_accessLimit);
+ eieio();
+
+ // TODO RTC 201901 - find a better OCMB register to read, should
+ // be able to optimize error handling.
+
+ // do a read on the OCMB after writing to it, since writes and
+ // reads are sequential, the read won't complete until after the
+ // write.
+ uint64_t scom_addr = (4 * GIGABYTE) + 4; // RTC 201901
+ uint8_t l_ocmbReg[8] = {0};
+
+ mmio_memcpy(l_ocmbReg, mm_ptr + scom_addr, sizeof(l_ocmbReg));
+ eieio();
+ if (!memcmp(io_ptr + i,
+ &MMIO_OCMB_UE_DETECTED,
+ sizeof(MMIO_OCMB_UE_DETECTED)))
+ {
+ uint64_t scom_data = 0;
+ uint64_t scom_mask = 0;
+
+ TRACFCOMP(g_trac_mmio, ERR_MRK
+ "ocmbMmioPerformOp: unable to complete MMIO"
+ " write, SUE detected");
+
+ /*@
+ * @errortype
+ * @moduleid MMIO::MOD_MMIO_PERFORM_OP
+ * @reasoncode MMIO::RC_BAD_MMIO_WRITE
+ * @userdata1[0:31] Target huid
+ * @userdata1[32:63] Data Offset, if >= 4GB then subtract
+ * 2GB (allows offsets to fit in 32 bits)
+ * @userdata2[0:0] Operation Type
+ * @userdata2[28:31] Access Limit
+ * @userdata2[32:63] Buffer Length
+ * @devdesc mmioPerformOp> MMIO write of an OCMB
+ * failed.
+ * @custdesc Unexpected memory subsystem firmware
+ * error.
+ */
+ l_err = new ERRORLOG::ErrlEntry(
+ ERRORLOG::ERRL_SEV_UNRECOVERABLE,
+ MMIO::MOD_MMIO_PERFORM_OP,
+ MMIO::RC_BAD_MMIO_WRITE,
+ TWO_UINT32_TO_UINT64(
+ i_target->getAttr<TARGETING::ATTR_HUID>(),
+ (l_offset < (4 * GIGABYTE)) ?
+ (l_offset) :
+ (l_offset - (2 * GIGABYTE))),
+ TWO_UINT32_TO_UINT64(
+ (i_opType << 31) | l_accessLimit,
+ io_buflen),
+ ERRORLOG::ErrlEntry::NO_SW_CALLOUT);
+ // add OCMB to error log
+ l_err->addHwCallout(i_target,
+ HWAS::SRCI_PRIORITY_HIGH,
+ HWAS::DECONFIG,
+ HWAS::GARD_NULL);
+ l_err->addProcedureCallout(HWAS::EPUB_PRC_HB_CODE,
+ HWAS::SRCI_PRIORITY_LOW);
+ const auto plid = l_err->plid();
+
+ auto l_err2 = getProcScom(i_target,
+ P9A_MCC_DSTLFIR,
+ scom_data);
+ if (l_err2)
+ {
+ l_err2->plid(plid);
+ errlCommit(l_err2, MMIO_COMP_ID);
+ }
+ else
+ {
+ scom_mask =
+ (1ull << P9A_MC_DSTLFIR_SUBCHANNEL_A_FAIL_ACTION) |
+ (1ull << P9A_MC_DSTLFIR_SUBCHANNEL_B_FAIL_ACTION);
+ if (scom_data & scom_mask)
+ {
+ // A channel checkstop has occurred.
+ // TODO RTC 201778 - Channel Fail Handling for
+ // Explorer
+ TRACFCOMP(g_trac_mmio, ERR_MRK
+ "ocmbMmioPerformOp: there was an error on"
+ " the Explorer channel, P9A_MCC_DSTLFIR=0x%lX",
+ scom_data);
+ }
+ }
+
+ break;
+ }
+ }
+
+ bytes_read_or_written += l_accessLimit;
+ }
+
+ io_buflen = bytes_read_or_written;
+ } while(0);
+
+ if (l_err)
+ {
+ l_err->collectTrace(MMIO_COMP_NAME);
+ }
+
+ TRACDCOMP(g_trac_mmio, EXIT_MRK"mmioPerformOp");
+
+ return l_err;
+}
+
+static
+TARGETING::TargetHandle_t getParentProc(TARGETING::TargetHandle_t i_target)
+{
+ TARGETING::TargetHandle_t proc = nullptr;
+ TARGETING::TargetHandleList list;
+ TARGETING::PredicateCTM pred(TARGETING::CLASS_CHIP,
+ TARGETING::TYPE_PROC);
+
+ TARGETING::TargetService().getAssociated(
+ list,
+ i_target,
+ TARGETING::TargetService::PARENT_BY_AFFINITY,
+ TARGETING::TargetService::ALL,
+ &pred);
+
+ if (list.size() == 1)
+ {
+ proc = list[0];
+ }
+
+ return proc;
+}
+
+static
+errlHndl_t getProcScom(TARGETING::TargetHandle_t i_target,
+ uint64_t i_scomAddr,
+ uint64_t &o_scomData)
+{
+ errlHndl_t l_err = nullptr;
+ auto proc = getParentProc(i_target);
+
+ if (proc == nullptr)
+ {
+ TRACFCOMP(g_trac_mmio, ERR_MRK
+ "getProcScom: Unable to find parent processor for target(0x%X)",
+ i_target->getAttr<TARGETING::ATTR_HUID>());
+
+ /*@
+ * @errortype
+ * @moduleid MMIO::MOD_MMIO_GET_PROC_SCOM
+ * @reasoncode MMIO::RC_PROC_NOT_FOUND
+ * @userdata1 Target huid
+ * @userdata2 SCOM address
+ * @devdesc getProcScom> Unable to find parent processor for target.
+ * @custdesc Unexpected memory subsystem firmware error.
+ */
+ l_err = new ERRORLOG::ErrlEntry(
+ ERRORLOG::ERRL_SEV_UNRECOVERABLE,
+ MMIO::MOD_MMIO_GET_PROC_SCOM,
+ MMIO::RC_PROC_NOT_FOUND,
+ i_target->getAttr<TARGETING::ATTR_HUID>(),
+ i_scomAddr,
+ ERRORLOG::ErrlEntry::ADD_SW_CALLOUT);
+ }
+ else
+ {
+ auto reqSize = sizeof(o_scomData);
+
+ l_err = DeviceFW::deviceRead(proc,
+ &o_scomData,
+ reqSize,
+ DEVICE_SCOM_ADDRESS(i_scomAddr));
+ }
+
+ return l_err;
+}
+
+static
+errlHndl_t setProcScom(TARGETING::TargetHandle_t i_target,
+ uint64_t i_scomAddr,
+ uint64_t i_scomData)
+{
+ errlHndl_t l_err = nullptr;
+ auto proc = getParentProc(i_target);
+
+ if (proc == nullptr)
+ {
+ TRACFCOMP(g_trac_mmio, ERR_MRK
+ "setProcScom: Unable to find parent processor for target(0x%X)",
+ i_target->getAttr<TARGETING::ATTR_HUID>());
+
+ /*@
+ * @errortype
+ * @moduleid MMIO::MOD_MMIO_SET_PROC_SCOM
+ * @reasoncode MMIO::RC_PROC_NOT_FOUND
+ * @userdata1 Target huid
+ * @userdata2 SCOM address
+ * @devdesc setProcScom> Unable to find parent processor for target.
+ * @custdesc Unexpected memory subsystem firmware error.
+ */
+ l_err = new ERRORLOG::ErrlEntry(
+ ERRORLOG::ERRL_SEV_UNRECOVERABLE,
+ MMIO::MOD_MMIO_SET_PROC_SCOM,
+ MMIO::RC_PROC_NOT_FOUND,
+ i_target->getAttr<TARGETING::ATTR_HUID>(),
+ i_scomAddr,
+ ERRORLOG::ErrlEntry::ADD_SW_CALLOUT);
+ }
+ else
+ {
+ auto reqSize = sizeof(i_scomData);
+
+ l_err = DeviceFW::deviceWrite(proc,
+ &i_scomData,
+ reqSize,
+ DEVICE_SCOM_ADDRESS(i_scomAddr));
+ }
return l_err;
}
+static
+void *mmio_memcpy(void *vdest, const void *vsrc, size_t len)
+{
+ assert((len % 4) == 0, "Length must be a multiple of 4!");
+ assert((reinterpret_cast<uintptr_t>(vdest) % 4) == 0,
+ "Destination must be 4 byte aligned!");
+
+ // Loop, copying 8 bytes every 5 instructions
+ long *ldest = reinterpret_cast<long *>(vdest);
+ const long *lsrc = reinterpret_cast<const long *>(vsrc);
+
+ while (len >= sizeof(long))
+ {
+ *ldest++ = *lsrc++;
+ len -= sizeof(long);
+ }
+
+ // Loop, copying 4 bytes every 5 instructions
+ int *idest = reinterpret_cast<int *>(ldest);
+ const int *isrc = reinterpret_cast<const int *>(lsrc);
+
+ while (len >= sizeof(int))
+ {
+ *idest++ = *isrc++;
+ len -= sizeof(int);
+ }
+
+ return vdest;
+}
+
}; // end namespace MMIO
diff --git a/src/usr/mmio/mmio.H b/src/usr/mmio/mmio.H
new file mode 100644
index 000000000..d69ade6f7
--- /dev/null
+++ b/src/usr/mmio/mmio.H
@@ -0,0 +1,64 @@
+/* IBM_PROLOG_BEGIN_TAG */
+/* This is an automatically generated prolog. */
+/* */
+/* $Source: src/usr/mmio/mmio.H $ */
+/* */
+/* OpenPOWER HostBoot Project */
+/* */
+/* Contributors Listed Below - COPYRIGHT 2018 */
+/* [+] International Business Machines Corp. */
+/* */
+/* */
+/* 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. */
+/* */
+/* IBM_PROLOG_END_TAG */
+#ifndef __MMIO_H
+#define __MMIO_H
+
+/** @file mmio.H
+ * @brief Provides interface to perform MMIO operations to Explorer chips
+ * */
+
+namespace MMIO
+{
+/**
+ * @brief Complete the MMIO operation.
+ * This function performs read or write operations on OCMBs by accessing
+ * virtual memory that was previously memory mapped to the OCMBs.
+ * It follows a pre-defined function prototype in order to be registered
+ * with the device driver framework.
+ *
+ * @param[in] i_opType Operation type, see driverif.H
+ * @param[in] i_target MMIO target
+ * @param[in/out] io_buffer Read: Pointer to output data storage
+ * Write: Pointer to input data storage
+ * @param[in/out] io_buflen Input: Read: size of data to read (in bytes)
+ * Write: Size of data to write
+ * Output: Read: Size of output data
+ * Write: Size of data written
+ * @param[in] i_accessType Access type
+ * @param[in] i_args This is an argument list for DD framework.
+ * In this function, there are two arguments,
+ * the offset (bytes) into the device, and the number
+ * of bytes to read at a time (device limitation).
+ * @return errlHndl_t
+ */
+errlHndl_t ocmbMmioPerformOp(DeviceFW::OperationType i_opType,
+ TARGETING::TargetHandle_t i_target,
+ void* io_buffer,
+ size_t& io_buflen,
+ int64_t i_accessType,
+ va_list i_args);
+}; // End namespace
+
+#endif
diff --git a/src/usr/mmio/test/makefile b/src/usr/mmio/test/makefile
new file mode 100644
index 000000000..133f3ca44
--- /dev/null
+++ b/src/usr/mmio/test/makefile
@@ -0,0 +1,31 @@
+# IBM_PROLOG_BEGIN_TAG
+# This is an automatically generated prolog.
+#
+# $Source: src/usr/mmio/test/makefile $
+#
+# OpenPOWER HostBoot Project
+#
+# Contributors Listed Below - COPYRIGHT 2011,2018
+# [+] International Business Machines Corp.
+#
+#
+# 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.
+#
+# IBM_PROLOG_END_TAG
+ROOTPATH = ../../../..
+
+MODULE = testmmio
+TESTS = *.H
+
+
+include ${ROOTPATH}/config.mk
diff --git a/src/usr/mmio/test/mmiotest.H b/src/usr/mmio/test/mmiotest.H
new file mode 100644
index 000000000..f7abd7816
--- /dev/null
+++ b/src/usr/mmio/test/mmiotest.H
@@ -0,0 +1,109 @@
+/* IBM_PROLOG_BEGIN_TAG */
+/* This is an automatically generated prolog. */
+/* */
+/* $Source: src/usr/mmio/test/mmiotest.H $ */
+/* */
+/* OpenPOWER HostBoot Project */
+/* */
+/* Contributors Listed Below - COPYRIGHT 2011,2018 */
+/* [+] International Business Machines Corp. */
+/* */
+/* */
+/* 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. */
+/* */
+/* IBM_PROLOG_END_TAG */
+#include <cxxtest/TestSuite.H>
+#include <errl/errlmanager.H>
+#include <errl/errlentry.H>
+#include <limits.h>
+#include <devicefw/driverif.H>
+#include <mmio/mmio.H>
+
+extern trace_desc_t* g_trac_mmio;
+
+class MmioTest : public CxxTest::TestSuite
+{
+ public:
+
+ /**
+ * @brief Test valid MMIO calls
+ */
+ void test_Valid(void)
+ {
+ TRACFCOMP( g_trac_mmio, "MmioTest::test_Valid> Start" );
+
+ uint64_t fails = 0;
+ uint64_t total = 0;
+ errlHndl_t l_err = nullptr;
+ uint64_t regdata = 0;
+ size_t op_size = sizeof(uint64_t);
+
+// TODO RTC 202533 - enable this test once the Axone model is IPLing
+// successfully in Simics.
+#if 0
+ // Get OCMB target, return if there is no OCMB
+ TARGETING::TargetHandle_t ocmb_target = nullptr;
+ TARGETING::TargetHandleList ocmb_target_list;
+ getAllChips(ocmb_target_list, TARGETING::TYPE_OCMB_CHIP);
+ if (ocmb_target_list.size() == 0)
+ {
+ TRACFCOMP(g_trac_fsiscom, "MmioTest::test_Valid> Target is NULL");
+ TS_INFO("MmioTest::test_Valid> Target is NULL");
+ return;
+ }
+ ocmb_target = ocmb_target_list[0];
+
+ // read
+ ++total;
+ l_err = MMIO::mmioPerformOp(
+ DeviceFW::READ,
+ ocmb_target,
+ &regdata,
+ op_size,
+ 0x0,
+ op_size);
+ if(l_err != nullptr)
+ {
+ TRACFCOMP(g_trac_mmio,
+ "MmioTest::test_Valid> Error for read, RC=0x%04X",
+ ERRL_GETRC_SAFE(l_err));
+ TS_FAIL("MmioTest::test_Valid> Error for read, RC=0x%04X",
+ ERRL_GETRC_SAFE(l_err));
+ ++fails;
+ errlCommit(l_err, MMIO_COMP_ID);
+ }
+
+ // write
+ ++total;
+ l_err = MMIO::mmioPerformOp(
+ DeviceFW::WRITE,
+ ocmb_target,
+ &regdata,
+ op_size,
+ 0x08,
+ op_size);
+ if(l_err != nullptr)
+ {
+ TRACFCOMP(g_trac_mmio,
+ "MmioTest::test_Valid> Error for write, RC=0x%04X",
+ ERRL_GETRC_SAFE(l_err));
+ TS_FAIL("MmioTest::test_Valid> Error for write, RC=0x%04X",
+ ERRL_GETRC_SAFE(l_err));
+ ++fails;
+ errlCommit(l_err, MMIO_COMP_ID);
+ }
+#endif
+
+ TRACFCOMP(g_trac_mmio, "Mmio::test_Valid> %d/%d fails", fails, total);
+ };
+};
OpenPOWER on IntegriCloud