Example program

Example of a program that connects to the smmuman service and monitors IOMMU/SMMU transgressions reported by the service.

The following program is taken from a system that maps all DMA devices in the smmuman startup configuration (see Startup mappings). It monitors transgressions reported by the smmuman service, but doesn't create SMMU objects, or memory mappings:

#ifdef __USAGE
%C - SMMU violations monitor

Syntax:
%C

Options:
    -v verbosity
#endif

#include <stdio.h>
#include <stdlib.h>
#include <stdarg.h>
#include <string.h>
#include <unistd.h>
#include <errno.h>
#include <smmu.h>
#include <inttypes.h>
#include <sys/neutrino.h>
#include <sys/siginfo.h>
#include <sys/slogcodes.h>
#include <sys/procmgr.h>

#define PULSE_CODE_SMMU_VIOLATION 0
#define _SLOGC_SMMUMON (_SLOGC_PRIVATE_END - 6)

static int verbosity_level = 0;

static int smmumon_slogf(int severity, int verbosity, int vlevel, const char *fmt, ...)
{
    int     ret;
    va_list arglist;

    ret = 0;

    if (severity <= _SLOG_ERROR || verbosity > 5) {
        va_start(arglist, fmt);
        vfprintf(stderr, fmt, arglist);
        va_end(arglist);
        fprintf(stderr, "\n");
    }
    if (vlevel <= 4 || verbosity >= vlevel) {
        va_start(arglist, fmt);
        ret = vslogf(_SLOGC_SMMUMON, severity, fmt, arglist);
        va_end(arglist);
    }
    return (ret);
}

static void options(int argc, char **argv)
{
    int     opt;
    while (optind < argc)
    {
        while ((opt = getopt(argc, argv, "v")) != -1)
        {
            switch (opt)
            {
                case 'v':
                    if (verbosity_level < 20) {
                        verbosity_level++;
                    }
                    break;
                default:
                    smmumon_slogf(
                        _SLOG_ERROR, 1, 0,
                        "options: unexpected parameter\n");
                    exit(-1);
            }
        }
    }
}

static int process_all_violations(void)
{
    int result;
    for(;;) {
        struct smmu_status status;
        result = smmu_xfer_status(&status);
        if (result == -1) {
            smmumon_slogf(
                _SLOG_ERROR, verbosity_level, 0,
                "process_all_violations: smmu_xfer_status failed, errno=%d\n",
                errno);
            return -1;
        }

        if (result == 0)
        {
            return 0;
        }

        if (result != 1) {
            smmumon_slogf(
                _SLOG_ERROR, verbosity_level, 0,
                "process_all_violations: unexpected result from smmu_xfer_status,"
                " result=%d\n", result);
            return -1;
        }

        switch(status.devid.type) {
            case SDT_PCI: {
                smmumon_slogf(
                    _SLOG_CRITICAL, verbosity_level, 0,
                    "pci device violation detected : "
                    "fault_addr=0x%" PRIx64 " "
                    "hw_specific=0x%" PRIx64 " "
                    "flags=0x%x "
                    "pci bdf=%d:%d:%d"
                    "\n",
                    status.fault_addr,
                    status.hw_specific,
                    status.flags,
                    status.devid.pci.bus,
                    status.devid.pci.dev,
                    status.devid.pci.func);
                break;
            }
            case SDT_MMIO: {
                smmumon_slogf(
                    _SLOG_CRITICAL, verbosity_level, 0,
                    "mmio device violation detected : "
                    "fault_addr=0x%" PRIx64 " "
                    "hw_specific=0x%" PRIx64 " "
                    "flags=0x%x "
                    "mmio.phys=0x%" PRIx64 " "
                    "mmio.length=0x%x"
                    "\n",
                    status.fault_addr,
                    status.hw_specific,
                    status.flags,
                    status.devid.mmio.phys,
                    status.devid.mmio.length);
                break;
            }
            default:
            {
                smmumon_slogf(
                    _SLOG_CRITICAL, verbosity_level, 0,
                    "unknown device type violation detected, "
                    "type=%d : "
                    "fault_addr=0x%" PRIx64 " "
                    "hw_specific=0x%" PRIx64 " "
                    "flags=0x%x "
                    "\n",
                    status.devid.type,
                    status.fault_addr,
                    status.hw_specific,
                    status.flags);
                break;
            }
        }
    }
    return -1;
}

static int wait_for_violations(int channel, int coid)
{
    int result;

    struct sigevent event;
    SIGEV_PULSE_INIT(&event, coid, -1, PULSE_CODE_SMMU_VIOLATION, NULL);

    result = smmu_xfer_notify(&event);
    if (result!=0) {
        smmumon_slogf(
            _SLOG_ERROR, verbosity_level, 0,
            "wait_for_violations: smmu_xfer_notify-1 failed , errno=%d\n",
            errno);
        return -1;
    }

    for(;;) {
        struct _pulse pulse;
        result = MsgReceivePulse(channel, &pulse, sizeof(pulse), NULL);
        if (result != 0) {
            smmumon_slogf(
                _SLOG_ERROR, verbosity_level, 0,
                "wait_for_violations: MsgReceivePulse failed, errno=%d\n",
                errno);
            return -1;
        }

        if (pulse.code != PULSE_CODE_SMMU_VIOLATION) {
            smmumon_slogf(
                _SLOG_ERROR, verbosity_level, 0,
                "wait_for_violations: unexpected pulse.code=%d\n",
                pulse.code);
            return -1;
        }

        result = process_all_violations();
        if (result != 0) {
            smmumon_slogf(
                _SLOG_ERROR, verbosity_level, 0,
                "wait_for_violations: process_all_violations failed, errno=%d\n",
                errno);
            return -1;
        }

        result = smmu_xfer_notify(&event);
        if (result!=0) {
            smmumon_slogf(
                _SLOG_ERROR, verbosity_level, 0,
                "wait_for_violations: smmu_xfer_notify-2 failed , errno=%d\n",
                errno);
            return -1;
        }
    }

    return -1;
}

static int connect_attach_channel_and_process(int channel)
{
    int coid;
    coid = ConnectAttach_r(0, 0, channel, _NTO_SIDE_CHANNEL, 0);
    if(coid < 0) {
        smmumon_slogf(
            _SLOG_ERROR, verbosity_level, 0,
            "connect_attach_channel_and_process : ConnectAttach_r %d\n",
            -coid);
        return -1;
    }

    int result;
    result = wait_for_violations(channel,coid);

    ConnectDetach_r(coid);

    return result;
}

static int open_channel_and_process(void)
{
    int channel;
    channel = ChannelCreate_r(0);
    if(channel < 0) {
        smmumon_slogf(
            _SLOG_ERROR, verbosity_level, 0,
            "open_channel_and_process: ChannelCreate_r failed: %d\n",
            -channel);
        return -1;
    }

    int result;
    result = connect_attach_channel_and_process(channel);

    ChannelDestroy_r(channel);

    return result;
}

int main(int argc, char *argv[])
{
    int result;

    options(argc,argv);

    result = procmgr_daemon( 0,
        PROCMGR_DAEMON_NODEVNULL | PROCMGR_DAEMON_NOCLOSE);

    if (result < 0) {
        smmumon_slogf(
            _SLOG_ERROR, verbosity_level, 0,
            "main: procmgr_daemon failed : %s\n", strerror(errno));
        return -1;
    }

    result = smmu_init(0);
    if (result != 0) {
        smmumon_slogf(
            _SLOG_ERROR, verbosity_level, 0,
            "main: smmu_init failed, errno=%d\n",errno);
        return -1;
    }

    result = open_channel_and_process();

    smmu_fini();

    return result;
}