Coding to the SED API: Part 2

In my previous blog, I did a walkthrough of the source code for main() within the ltloop JTAG-based on-target diagnostic. This article covers main() in more detail, and provides insight into some of the operations of the utility functions and data structures.

Please read Coding to the SED API: Part 1 for the background on the BMC-based On-Target Diagnostics (OTDs) and on the ltloop OTD in particular. The source code is also available there. Let’s jump in.

First, let’s look at the parseArgs(argc, argv) function called initially within main() to see the overall parameters and operations supported by the ltloop OTD.

int parseArgs(int argc, char **argv)

{

    int c;

    int retval = 0;

    while ((c = getopt (argc, argv, "l:p:t:s:f:cdbo?h")) != -1)

    {

        switch(c)

        {

            case 'p':

                //printf("Option p: %s\n", optarg);

                m_port = atoi(optarg);

                if ((m_port < 0) || (m_port >= MAXPORT))

                {

                    printf("Invalid port value: %d \n", m_port);

                    retval = -1;

                    m_port = 0;

                }

                break;

            case 'l':

                m_loops = atoi(optarg);

                if (m_loops < 1)

                {

                    printf("Invalid number of loops, must be > 0.\n");

                    retval = -1;

                    m_loops = 1;

                }

                break;

            case 's':

                m_socket = atoi(optarg);

                //Will check socket # in main after we get system topology and learn # of CPUs

                break;

            case 't':

                m_type = atoi(optarg);

                if ((m_type != 1) && (m_type != 6))

                {

                    printf("Invalid test type, only type 1 and type 6 are supported.\n");

                    m_type = 1;

                    retval = -1;

                }

                break;

            case 'd':

                m_readDSC = true;

                break;

            case 'b':

                ai_mIOSFdebugPrint(true);

                printf("Debug printouts turned on\n");

                break;

            case 'o':

                m_overview = true;

                break;

            case 'f':

                m_forceSpeed = atoi(optarg);

                if ((m_forceSpeed > 4) || (m_forceSpeed < 1))

                {

                    printf("Invalid speed, must be between 1 and 4\n");

                    retval = 1;

                    m_forceSpeed = 0;

                }

                break;

            case 'c':

                m_pciScan = true;

                break;

            case '?':

            case 'h':

                retval = 1;     //Note: caller will treat as error and print usage()

                break;

        }  // switch...

    }  //while...

    //Further checks on params

    //Can't check downstream component on DMI (port 0)

    if ((m_port == 0) && (m_readDSC))

    {

        m_readDSC = false;

        printf("Unable to check DSC errors on DMI.\n");

        //We allow test to continue in this case, as it is probably what user would do if we issued an error.

    }

    return retval;

}  // parseArgs

You can see that the switch on the input parameters (i.e. we might invoke ltloop via “ltloop -s1 -p5 -t1 -l10000”) gives us the following options:

“s” : socket number

“p”: PCIe port number

“l” : the number of LTSSM loop tests to run; for example, how many retrains to do

“t” : Test type. Only 1 and 6 are supported in this routine. “1” is a simple link retrain test, and “6” is a speed change loop test (i.e. from Gen1 to Gen4 and back).

“d” : this provides the option to conduct testing on the downstream device (DSC – downstream component) as well as the upstream component. The default is “false” to optimize for time. But we can set it to “true” to have the test be more rigorous.

“b” : This allows for debug information to be sent from when we use the IOSF to access the PCI Express configuration registers. It’s a debug utility, mostly for use by ASSET or Intel personnel to do some troubleshooting if needed.

“o” : Another debug utility, just to do a subset equivalent of the “sysTopo” CScript. This overrides all other options.

“f” : Forces the port to a given speed; for example, Gen1, Gen2, Gen3, Gen4.

“c” : Does a scan and a print of all B/D/F.

Let’s now look at the rest of the main routine. Within main(), the initial work of initializing the platform and putting it into probe mode is done via the following sequence of functions:

ai_mOpen

ai_mSetTargetCPUType

ai_mConfig

ai_mGetITPScanChainTopology

ai_mSetActiveCPU

ai_mSetActiveCore

ai_mSetActiveThread

ai_mEnterDebugMode

The operation of these functions should be made fairly clear by looking at the source code. Here are some examples.

The invocation of ai_mOpen is in line 1255:

if ((iError = ai_mOpen(pdctarget, 1, &mHandle)) != AI_SUCCESS)

Since the third argument is &mHandle, the value of mHandle is updated within this function. mHandle is a unique identifier connection to a specific PdcNo – which in turns relates to the number of CPUs that the service processor JTAG Master is intended to drive. In this case, it’s left at the default of AI_PDC_0; the default of one chain or node.

The invocation of ai_mSetActiveCPU is in line 1286:

if ((iError = ai_mSetActiveCPU(mHandle, curCPU)) != AI_SUCCESS)

while iterating across the values of curCPU (normally two in a 2-socket system, four in a 4-socket system, etc.).

And ai_mEnterDebugMode is in line 1294:

if ((iError = ai_mEnterDebugMode(mHandle)) != AI_SUCCESS)

this function forces all connected CPU cores of the node identified by mHandle in to debug mode. Said another way, they enter probe mode, and are then available for other run-control operations. Debug mode or probe mode is a state in which the platform must be to execute many of the SED library functions; though this is not true in all cases, which I shall show when we discuss the IOSF related functions, such as invoked in ai_mIOSFTAPinit(mHandle) on line 1303. But, I’ll skip ahead of these calls for now.

After the platform probe mode initialization is complete, the flow of the program is as follows:

prepTarget(mHandle, 0, 0, 0);

getBusNumbers(mHandle, curCPU);

port2bdf(m_port, &bus, &dev, &fun); 

do_test(mHandle, numcpus, bus, dev, fun);

lt_loop(mHandle, bus, dev, fun);

Well, that was fun. Most of the heavy lifting is done within lt_loop(), which we’ll cover in detail soon. And we’ll also look at the operation of the other functions listed above as well, and the register definitions and methodology of JTAG/MMIO and the IOSF to read and write the applicable registers needed to perform our tests. Stay tuned!

Alan Sguigna