nmc-utils  0.1.1
nmctl.c
Go to the documentation of this file.
1 /*
2  * libEasyNMC DSP communication library.
3  * Copyright (C) 2014 RC "Module"
4  * Written by Andrew 'Necromant' Andrianov <andrew@ncrmnt.org>
5  *
6  * This library is free software; you can redistribute it and/or
7  * modify it under the terms of the GNU Lesser General Public
8  * License as published by the Free Software Foundation; either
9  * version 2.1 of the License, or (at your option) any later version.
10  *
11  * This library is distributed in the hope that it will be useful,
12  * ut WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14  * Lesser General Public License for more details.
15  *
16  * You should have received a copy of the GNU Lesser General Public
17  * License along with this library; if not, write to the Free Software
18  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
19  *
20  */
21 #include <stdio.h>
22 #include <getopt.h>
23 #include <sys/ioctl.h>
24 #include <unistd.h>
25 #include <stdio.h>
26 #include <stdlib.h>
27 #include <string.h>
28 #include <stdint.h>
29 #include <getopt.h>
30 #include <sys/epoll.h>
31 
32 
33 int g_debug = 1;
34 int g_force = 0;
35 int g_nostdio = 0;
36 static uint32_t entrypoint;
37 
38 #define dbg(fmt, ...) if (g_debug) { \
39  fprintf(stderr, "nmctl: " fmt, ##__VA_ARGS__); \
40  }
41 
42 #define err(fmt, ...) if (g_debug) { \
43  fprintf(stderr, "nmctl: " fmt, ##__VA_ARGS__); \
44  }
45 
46 
47 #include <easynmc.h>
48 
49 
50 int do_dump_core_info(struct easynmc_handle *h, void *udata)
51 {
52 
53  char name[64];
54  char type[64];
55  int ret;
56 
57  ret = ioctl(h->iofd, IOCTL_NMC3_GET_NAME, name);
58  if (ret != 0) {
59  perror("ioctl");
60  exit(1);
61  }
62 
63  ret = ioctl(h->iofd, IOCTL_NMC3_GET_TYPE, type);
64  if (ret != 0) {
65  perror("ioctl");
66  exit(1);
67  }
68 
69  struct nmc_core_stats stats;
70 
71  ret = ioctl(h->iofd, IOCTL_NMC3_GET_STATS, &stats);
72  if (ret != 0) {
73  perror("ioctl");
74  exit(1);
75  }
76 
77  /* Now, let's read some magic bytes */
78  const char *status = easynmc_state_name(easynmc_core_state(h));
79  const char *appid = easynmc_appid_get(h);
80 
81  printf("%d. name: %s type: %s (%s) appid: %s\n",
82  h->id, name, type, status, appid
83  );
84 
85  if (stats.started) {
86  uint32_t codever = h->imem32[NMC_REG_CODEVERSION];
87  printf(" Initcode version: %x, %s\n", codever,
88  easynmc_startupcode_is_compatible(codever) ? "compatible" : "incompatible");
89  }
90 
91  printf(" IRQs Recv: HP: %d LP: %d\n",
92  stats.irqs_recv[NMC_IRQ_HP],
93  stats.irqs_recv[NMC_IRQ_LP]
94  );
95 
96  printf(" IRQs Sent: NMI: %d HP: %d LP: %d\n",
97  stats.irqs_sent[NMC_IRQ_NMI],
98  stats.irqs_sent[NMC_IRQ_HP],
99  stats.irqs_sent[NMC_IRQ_LP]
100  );
101 
102  return 0;
103 }
104 
105 
106 int do_boot_core(struct easynmc_handle *h, void *optarg)
107 {
108  int ret;
109 
110  printf("Booting core %d with %s ipl\n", h->id, (optarg ? "debug" : "production"));
111 
112  ret = easynmc_boot_core(h, (optarg ? 1 : 0) );
113  if (ret) {
114  fprintf(stderr, "Failed to boot core #%d\n", h->id);
115  exit(1);
116  }
117  return 0;
118 }
119 
120 
121 int do_dump_ldr_info(struct easynmc_handle *h, void *optarg)
122 {
123 
124  printf("=== Init code registers dump ===\n");
125  printf(" CODEVER %x\n", h->imem32[NMC_REG_CODEVERSION]);
126  printf(" ISR_ON_START %x\n", h->imem32[NMC_REG_ISR_ON_START]);
127  printf(" STATUS %x\n", h->imem32[NMC_REG_CORE_STATUS]);
128  printf(" START %x\n", h->imem32[NMC_REG_CORE_START]);
129  printf(" ENTRY %x\n", h->imem32[NMC_REG_PROG_ENTRY]);
130  printf(" RETCODE %x\n", h->imem32[NMC_REG_PROG_RETURN]);
131  printf(" APPDATA %x\n", h->imem32[NMC_REG_APPDATA_SIZE]);
132  return 0;
133 }
134 
135 int do_reset_stats(struct easynmc_handle *h, void *optarg)
136 {
137  if (0!=easynmc_reset_stats(h))
138  exit(1);
139  return 0;
140 }
141 
142 int do_load_abs(struct easynmc_handle *h, void *arg)
143 {
144  int ret;
145  char *optarg = arg;
146  int flags = ABSLOAD_FLAG_DEFAULT;
147 
149  ret = easynmc_boot_core(h, 0);
150  if (ret)
151  exit(1);
152 
153  if (g_nostdio)
154  flags &= ~(ABSLOAD_FLAG_STDIO);
155 
156  if (g_force)
157  flags |= ABSLOAD_FLAG_FORCE;
158 
159  /* No args processing in nmctl */
160 
161  flags &= ~(ABSLOAD_FLAG_ARGS);
162 
163  ret = easynmc_load_abs(h, optarg, &entrypoint, flags);
164  if (ret == 0)
165  printf("ABS file %s loaded, ok\n", optarg);
166  else {
167  printf("Failed to load ABS file %s\n", optarg);
168  exit(1);
169  }
170 
171  return 0;
172 }
173 
174 int do_start_app(struct easynmc_handle *h, void *optarg)
175 {
176  int ret;
177  ret = easynmc_start_app(h, entrypoint);
178  if (ret == 0)
179  printf("NMC app now started!\n");
180  else
181  printf("Failed to start app!\n");
182 
184 
185  easynmc_close(h);
186 
187  return 0;
188 }
189 
190 int do_irq(struct easynmc_handle *h, void *optarg)
191 {
192  int ret = 1;
193  int irq = -1;
194 
195  if (strcmp(optarg,"nmi")==0)
196  irq = NMC_IRQ_NMI;
197  else if (strcmp(optarg,"lp")==0)
198  irq = NMC_IRQ_LP;
199  else if (strcmp(optarg,"hp")==0)
200  irq = NMC_IRQ_HP;
201 
202  if (irq!=-1)
203  ret = easynmc_send_irq(h,irq);
204 
205  easynmc_close(h);
206  return ret;
207 }
208 
209 int do_mon(struct easynmc_handle *h, void *optarg)
210 {
211  printf("Monitoring events, CTRL+C to terminate\n");
212  int evt;
213  struct easynmc_token *tok = easynmc_token_new(h, EASYNMC_EVT_ALL);
214  while (1) {
215  evt = easynmc_token_wait(tok, 50000);
216  if (evt != EASYNMC_EVT_TIMEOUT)
217  printf("Event: %s\n", easynmc_evt_name(evt));
218 
219  }
220  return 0;
221 }
222 
223 int do_kill(struct easynmc_handle *h, void *optarg)
224 {
225  int ret=0;
226 
227  if ((easynmc_core_state(h) == EASYNMC_CORE_RUNNING) && (!g_force)) {
228  fprintf(stderr, "Application is in state running (not killable)\n");
229  fprintf(stderr, "Killing it may cause userspace to misbehave\n");
230  fprintf(stderr, "Use --force to kill it anyway\n");
231  return 0;
232  }
233 
234  ret = easynmc_stop_app(h);
235  if (ret==0) {
236  printf("App on core %d terminated\n", h->id);
237  goto done;
238  }
239 
240  if ((easynmc_core_state(h) != EASYNMC_CORE_IDLE) &&
242  printf("Failed to terminate app on core %d\n", h->id);
243  printf("This will likely be only fixed by a reboot, sorry\n");
244  }
245 done:
246  return 0;
247 
248 }
249 
250 #define NUMEVENTS 16
251 int do_mon_epoll(struct easynmc_handle *h, void *optarg)
252 {
253  int ret = 1;
254  printf("Monitoring events on core %d (epoll), CTRL+C to terminate\n", h->id);
255 
256  if (0!=easynmc_pollmark(h))
257  goto errclose;
258 
259  struct epoll_event event;
260  struct epoll_event *events;
261  int efd = epoll_create(1);
262  if (efd == -1)
263  {
264  perror ("epoll_create");
265  ret = 1;
266  goto errclose;
267  }
268 
269  event.data.fd = h->memfd;
270  event.events = EPOLLNMI | EPOLLHP | EPOLLLP;
271  ret = epoll_ctl (efd, EPOLL_CTL_ADD, h->memfd, &event);
272  if (ret == -1)
273  {
274  perror ("epoll_ctl");
275  ret = 1;
276  goto errclose;
277  }
278 
279  events = calloc (NUMEVENTS, sizeof event);
280 
281  while (1) {
282  int n, i;
283  n = epoll_wait(efd, events, NUMEVENTS, -1);
284  for (i = 0; i < n; i++) {
285  if (events[i].events & EPOLLNMI)
286  printf("Event: NMI\n");
287  if (events[i].events & EPOLLLP)
288  printf("Event: LP\n");
289  if (events[i].events & EPOLLHP)
290  printf("Event: HP\n");
291  if (events[i].events & EPOLLERR)
292  printf("Event: ERROR\n");
293  }
294  }
295 errclose:
296  return ret;
297 }
298 
299 
300 static struct option long_options[] =
301 {
302  /* Generic stuff. */
303  {"list", no_argument, 0, 'l' },
304  {"help", no_argument, 0, 'h' },
305 
306  /* Options */
307  {"core", required_argument, 0, 'c' },
308  {"force", no_argument, &g_force, 1 },
309  {"nostdio", no_argument, &g_nostdio, 1 },
310 
311  /* Actual actions */
312  {"boot", optional_argument, 0, 'b' },
313  {"reset-stats", no_argument, 0, 'r' },
314  {"load", required_argument, 0, 'L' },
315  {"start", required_argument, 0, 's' },
316  {"irq", required_argument, 0, 'i' },
317  {"mon", no_argument, 0, 'm' },
318  {"mon-epoll", no_argument, 0, 'M' },
319  {"kill", no_argument, 0, 'k' },
320 
321 
322  /* Debugging hacks */
323  {"debug-lib", no_argument, &g_libeasynmc_debug, 1 },
324  {"debug", no_argument, &g_debug, 1 },
325  {"dump-ldr-regs", optional_argument, 0, 'D' },
326 
327  {0, 0, 0, 0}
328 };
329 
330 void usage(char *nm)
331 {
332  fprintf(stderr,
333  "nmctl - The EasyNMC control utility\n"
334  "(c) 2014 RC Module | Andrew 'Necromant' Andrianov <andrew@ncrmnt.org>\n"
335  "This is free software; see the source for copying conditions. There is NO\n"
336  "warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n"
337  "License: LGPLv2 \n"
338  "Usage: %s [options] [actions] - operate on core 0 (default)\n"
339  " %s --core=n [options] - operate on selected core\n"
340  " %s --core=all [options] - operate all cores\n"
341  "Valid options are: \n"
342  " --core=id - Select a core to operate on (--core=all selects all cores)\n"
343  " --list - list available nmc cores in this system and their state\n"
344  " --help - Show this help\n"
345  " --force - Disable internal seatbelts (DANGEROUS!)\n"
346  " --nostdio - Do not auto-attach stdio\n"
347  " --debug - print lots of debugging info (nmctl)\n"
348  " --debug-lib - print lots of debugging info (libeasynmc)\n"
349  "Valid actions are: \n"
350  " --boot - Load initcode and boot a core (all cores)\n"
351  " --reset-stats - Reset driver statistics for core (all cores)\n"
352  " --load=file.abs - Load abs file to core internal memory\n"
353  " --start=file.abs - Load abs file to core internal memory and start it\n"
354  " --irq=[nmi,lp,hp] - Send an interrupt to NMC\n"
355  " --kill - Abort nmc program execution\n"
356  " --mon - Monitor IRQs from NMC\n"
357  " --dump-ldr-regs - Dump init code memory registers\n\n"
358  "ProTIP(tm): You can supply init code file to use via NMC_STARTUPCODE env var\n"
359  " When no env is set nmctl will search a set of predefined paths\n"
360  ,nm, nm, nm
361 );
362 }
363 
364 static int for_each_core_optarg(int core, int (*cb)(struct easynmc_handle *h, void *optarg), char* optarg)
365 {
366  int ret = 0 ;
367  /* In case we operate on all cores */
368  if (core==-2) {
369  ret = easynmc_for_each_core(cb, 0, optarg);
370  if (!ret)
371  fprintf(stderr, "Iterated over 0 cores, kernel driver problem?\n");
372  return ret ? 0 : 1;
373  }
374  /* In case we operate on all cores */
375  struct easynmc_handle *h = easynmc_open_noboot(core, 0);
376  if (!h) {
377  fprintf(stderr, "easynmc_open_noboot() failed. Kernel driver problem or invalid core?\n");
378  return 1;
379  }
380  cb(h, optarg);
381  return 0;
382 }
383 
384 int main (int argc, char **argv)
385 {
386  int core = 0; /* Default - use first available core */
387 
388  if (argc < 2)
389  usage(argv[0]), exit(1);
390 
391  while (1)
392  {
393  int c;
394  int option_index = 0;
395  int ret;
396  c = getopt_long (argc, argv, "lhb:zr:",
397  long_options, &option_index);
398 
399  /* Detect the end of the options. */
400  if (c == -1)
401  break;
402 
403  switch (c)
404  {
405  case 'c':
406  if (strcmp(optarg, "all") == 0)
407  core = -1;
408  else
409  core = atoi(optarg);
410  break;
411  case 'M':
412  return for_each_core_optarg(core, do_mon, optarg);
413  case 'm':
414  return for_each_core_optarg(core, do_mon, optarg);
415  case 'r':
416  return for_each_core_optarg(core, do_reset_stats, NULL);
417  case 'k':
418  return for_each_core_optarg(core, do_kill, NULL);
419  case 'i':
420  return for_each_core_optarg(core, do_irq, optarg);
421  case 'D':
422  return for_each_core_optarg(core, do_dump_ldr_info, NULL);
423  case 'L':
424  case 's':
425  ret = for_each_core_optarg(core, do_load_abs, optarg);
426  /* start */
427  if (ret != 0) {
428  fprintf(stderr, "Failed to load abs file to nmc core(s)\n");
429  return ret;
430  }
431  if (c=='s')
432  ret = for_each_core_optarg(core, do_start_app, optarg);
433  return ret;
434  break;
435  case 'b':
436  return for_each_core_optarg(core, do_boot_core, optarg);
437  case 'l':
438  for_each_core_optarg(-2, do_dump_core_info, NULL);
439  exit(0);
440  break;
441  case 'h':
442  usage(argv[0]);
443  exit(1);
444  break;
445  }
446  }
447  return 0;
448 }