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 = 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;
}
Page updated:
