Updated: April 19, 2023 |
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 = MsgRegisterEvent(&event,-1); if (result!=0) { smmumon_slogf( _SLOG_ERROR, verbosity_level, 0, "wait_for_violations: MsgRegisteredEvent 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-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 = smmu_xfer_notify(&event); if (result!=0) { process_all_violations(); smmumon_slogf( _SLOG_ERROR, verbosity_level, 0, "wait_for_violations: smmu_xfer_notify-2 failed , errno=%d\n", errno); 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; } } 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; }