device.cc Source File

Back to the index.

device.cc
Go to the documentation of this file.
1 /*
2  * Copyright (C) 2005-2013 Anders Gavare. All rights reserved.
3  *
4  * Redistribution and use in source and binary forms, with or without
5  * modification, are permitted provided that the following conditions are met:
6  *
7  * 1. Redistributions of source code must retain the above copyright
8  * notice, this list of conditions and the following disclaimer.
9  * 2. Redistributions in binary form must reproduce the above copyright
10  * notice, this list of conditions and the following disclaimer in the
11  * documentation and/or other materials provided with the distribution.
12  * 3. The name of the author may not be used to endorse or promote products
13  * derived from this software without specific prior written permission.
14  *
15  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
16  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
17  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
18  * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
19  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
20  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
21  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
22  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
23  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
24  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
25  * SUCH DAMAGE.
26  *
27  *
28  * COMMENT: Device registry framework
29  */
30 
31 #include <stdio.h>
32 #include <stdlib.h>
33 #include <string.h>
34 
35 #include "device.h"
36 #include "machine.h"
37 #include "memory.h"
38 #include "misc.h"
39 
40 
41 static struct device_entry *device_entries = NULL;
42 static int device_entries_sorted = 0;
43 static int n_device_entries = 0;
44 static int device_exit_on_error = 1;
45 
46 static struct pci_entry *pci_entries = NULL;
47 static int n_pci_entries = 0;
48 
49 
50 /*
51  * device_entry_compar():
52  *
53  * Internal function, used by sort_entries().
54  */
55 static int device_entry_compar(const void *a, const void *b)
56 {
57  const struct device_entry *pa = (const struct device_entry *) a;
58  const struct device_entry *pb = (const struct device_entry *) b;
59 
60  return strcmp(pa->name, pb->name);
61 }
62 
63 
64 /*
65  * sort_entries():
66  *
67  * Internal function. Sorts the device_entries array in alphabetic order.
68  */
69 static void sort_entries(void)
70 {
71  qsort(device_entries, n_device_entries, sizeof(struct device_entry),
72  device_entry_compar);
73 
74  device_entries_sorted = 1;
75 }
76 
77 
78 /*
79  * device_register():
80  *
81  * Registers a device. The device is added to the end of the device_entries
82  * array, and the sorted flag is set to zero.
83  *
84  * NOTE: It would be a bad thing if two devices had the same name. However,
85  * that isn't checked here, it is up to the caller!
86  *
87  * Return value is 1 if the device was registered, 0 otherwise.
88  */
89 int device_register(const char *name, int (*initf)(struct devinit *))
90 {
91  CHECK_ALLOCATION(device_entries = (struct device_entry *) realloc(device_entries,
92  sizeof(struct device_entry) * (n_device_entries + 1)));
93 
94  memset(&device_entries[n_device_entries], 0,
95  sizeof(struct device_entry));
96 
97  CHECK_ALLOCATION(device_entries[n_device_entries].name = strdup(name));
98  device_entries[n_device_entries].initf = initf;
99 
100  device_entries_sorted = 0;
101  n_device_entries ++;
102  return 1;
103 }
104 
105 
106 /*
107  * pci_register():
108  *
109  * Registers a pci device. The pci device is added to the pci_entries array.
110  *
111  * Return value is 1 if the pci device was registered. If it was not
112  * added, this function does not return.
113  */
114 int pci_register(const char *name, void (*initf)(struct machine *, struct memory *,
115  struct pci_device *))
116 {
117  CHECK_ALLOCATION(pci_entries = (struct pci_entry *) realloc(pci_entries,
118  sizeof(struct pci_entry) * (n_pci_entries + 1)));
119 
120  memset(&pci_entries[n_pci_entries], 0, sizeof(struct pci_entry));
121 
122  CHECK_ALLOCATION(pci_entries[n_pci_entries].name = strdup(name));
123  pci_entries[n_pci_entries].initf = initf;
124  n_pci_entries ++;
125  return 1;
126 }
127 
128 
129 /*
130  * pci_lookup_initf():
131  *
132  * Find a pci device init function by scanning the pci_entries array.
133  *
134  * Return value is a function pointer, or NULL if the name was not found.
135  */
136 void (*pci_lookup_initf(const char *name))(struct machine *machine,
137  struct memory *mem, struct pci_device *pd)
138 {
139  int i;
140 
141  if (name == NULL) {
142  fprintf(stderr, "pci_lookup_initf(): name = NULL\n");
143  exit(1);
144  }
145 
146  for (i=0; i<n_pci_entries; i++)
147  if (strcmp(name, pci_entries[i].name) == 0)
148  return pci_entries[i].initf;
149  return NULL;
150 }
151 
152 
153 /*
154  * device_lookup():
155  *
156  * Lookup a device name by scanning the device_entries array (as a binary
157  * search tree).
158  *
159  * Return value is a pointer to the device_entry on success, or a NULL pointer
160  * if there was no such device.
161  */
163 {
164  int hi, lo;
165 
166  if (name == NULL) {
167  fprintf(stderr, "device_lookup(): NULL ptr\n");
168  exit(1);
169  }
170 
171  if (!device_entries_sorted)
172  sort_entries();
173 
174  if (n_device_entries == 0)
175  return NULL;
176 
177  lo = 0; hi = n_device_entries - 1;
178 
179  while (lo <= hi) {
180  int r, i = (lo + hi) / 2;
181 
182  /* printf("device_lookup(): i=%i (lo=%i hi=%i)\n", i, lo, hi);
183  printf(" name='%s', '%s'\n", name,
184  device_entries[i].name); */
185 
186  r = strcmp(name, device_entries[i].name);
187  if (r == 0) {
188  /* Found it! */
189  return &device_entries[i];
190  }
191 
192  /* Try left or right half: */
193  if (r < 0)
194  hi = i - 1;
195  if (r > 0)
196  lo = i + 1;
197  }
198 
199  return NULL;
200 }
201 
202 
203 /*
204  * device_unregister():
205  *
206  * Unregisters a device.
207  *
208  * Return value is 1 if a device was unregistered, 0 otherwise.
209  */
211 {
212  ssize_t i;
213  struct device_entry *p = device_lookup(name);
214 
215  if (p == NULL) {
216  fatal("device_unregister(): no such device (\"%s\")\n", name);
217  return 0;
218  }
219 
220  i = (size_t)p - (size_t)device_entries;
221  i /= sizeof(struct device_entry);
222 
223  free(device_entries[i].name);
224  device_entries[i].name = NULL;
225 
226  if (i == n_device_entries-1) {
227  /* Do nothing if we're removing the last array element. */
228  } else {
229  /* Remove array element i by copying the last element
230  to i's position: */
231  device_entries[i] = device_entries[n_device_entries-1];
232 
233  /* The array is not sorted anymore: */
234  device_entries_sorted = 0;
235  }
236 
237  n_device_entries --;
238 
239  /* TODO: realloc? */
240  return 1;
241 }
242 
243 
244 /*
245  * device_add():
246  *
247  * Add a device to a machine. For example: "kn210 addr=0x12340000" adds a
248  * device called "kn210" at a specific address.
249  *
250  * TODO: This function is quite ugly, and should be cleaned up.
251  */
252 void *device_add(struct machine *machine, const char *name_and_params)
253 {
254  struct device_entry *p;
255  struct devinit devinit;
256  const char *s2;
257  const char *s3;
258  size_t len, interrupt_path_len = strlen(machine->path) + 100;
259  int quoted;
260 
261  memset(&devinit, 0, sizeof(struct devinit));
263 
264  /* Default values: */
265  devinit.addr_mult = 1;
266  devinit.in_use = 1;
267 
268  /* Get the device name first: */
269  s2 = name_and_params;
270  while (s2[0] != ',' && s2[0] != ' ' && s2[0] != '\0')
271  s2 ++;
272 
273  len = (size_t)s2 - (size_t)name_and_params;
274  CHECK_ALLOCATION(devinit.name = (char *) malloc(len + 1));
275  memcpy(devinit.name, name_and_params, len);
276  devinit.name[len] = '\0';
277 
278  /* Allocate space for the default interrupt name: */
280  malloc(interrupt_path_len + 1));
281  snprintf(devinit.interrupt_path, interrupt_path_len,
282  "%s.cpu[%i]", machine->path, machine->bootstrap_cpu);
283 
285  if (p == NULL) {
286  fatal("no such device (\"%s\")\n", devinit.name);
287  if (device_exit_on_error)
288  exit(1);
289  else
290  goto return_fail;
291  }
292 
293  /* Get params from name_and_params: */
294  while (*s2 != '\0') {
295  /* Skip spaces, commas, and semicolons: */
296  while (*s2 == ' ' || *s2 == ',' || *s2 == ';')
297  s2 ++;
298 
299  if (*s2 == '\0')
300  break;
301 
302  /* s2 now points to the next param. eg "addr=1234" */
303 
304  /* Get a word (until there is a '=' sign): */
305  s3 = s2;
306  while (*s3 != '=' && *s3 != '\0')
307  s3 ++;
308  if (s3 == s2) {
309  fatal("weird param: %s\n", s2);
310  if (device_exit_on_error)
311  exit(1);
312  else
313  goto return_fail;
314  }
315  s3 ++;
316  /* s3 now points to the parameter value ("1234") */
317 
318  if (strncmp(s2, "addr=", 5) == 0) {
319  devinit.addr = mystrtoull(s3, NULL, 0);
320  } else if (strncmp(s2, "addr2=", 6) == 0) {
321  devinit.addr2 = mystrtoull(s3, NULL, 0);
322  } else if (strncmp(s2, "len=", 4) == 0) {
323  devinit.len = mystrtoull(s3, NULL, 0);
324  } else if (strncmp(s2, "addr_mult=", 10) == 0) {
325  devinit.addr_mult = mystrtoull(s3, NULL, 0);
326  } else if (strncmp(s2, "pci_little_endian=", 18) == 0) {
327  devinit.pci_little_endian = mystrtoull(s3, NULL, 0);
328  switch (devinit.pci_little_endian) {
329  case 0: break;
330  case 1: devinit.pci_little_endian =
332  break;
333  default:fatal("Bad pci_little_endian value.\n");
334  exit(1);
335  }
336  } else if (strncmp(s2, "irq=", 4) == 0) {
337  snprintf(devinit.interrupt_path, interrupt_path_len, "%s", s3);
338  if (strchr(devinit.interrupt_path, ' ') != NULL)
339  *strchr(devinit.interrupt_path, ' ') = '\0';
340  } else if (strncmp(s2, "in_use=", 7) == 0) {
341  devinit.in_use = mystrtoull(s3, NULL, 0);
342  } else if (strncmp(s2, "name2=", 6) == 0) {
343  const char *h = s2 + 6;
344  size_t len2 = 0;
345  quoted = 0;
346  while (*h) {
347  if (*h == '\'')
348  quoted = !quoted;
349  h++, len2++;
350  if (!quoted && *h == ' ')
351  break;
352  }
353  CHECK_ALLOCATION(devinit.name2 = (char *) malloc(len2 + 1));
354  h = s2 + 6;
355  if (*h == '\'')
356  len2 -= 2, h++;
357  snprintf(devinit.name2, len2 + 1, "%s", h);
358  } else {
359  fatal("unknown param: %s\n", s2);
360  if (device_exit_on_error)
361  exit(1);
362  else
363  goto return_fail;
364  }
365 
366  /* skip to the next param: */
367  s2 = s3;
368  quoted = 0;
369  while (*s2 != '\0' && (*s2 != ' ' || quoted) &&
370  *s2 != ',' && *s2 != ';') {
371  if (*s2 == '\'')
372  quoted = !quoted;
373  s2 ++;
374  }
375  }
376 
377 
378  /*
379  * Call the init function for this device:
380  */
381 
382  devinit.return_ptr = NULL;
383 
384  if (!p->initf(&devinit)) {
385  fatal("error adding device (\"%s\")\n", name_and_params);
386  if (device_exit_on_error)
387  exit(1);
388  else
389  goto return_fail;
390  }
391 
392  free(devinit.interrupt_path);
393  free(devinit.name);
394  return devinit.return_ptr;
395 
396 return_fail:
397  free(devinit.name);
398  return NULL;
399 }
400 
401 
402 /*
403  * device_dumplist():
404  *
405  * Dump a list of all registered devices. (If the list is not sorted when
406  * this function is called, it is implicitly sorted.)
407  */
408 void device_dumplist(void)
409 {
410  int i;
411 
412  if (!device_entries_sorted)
413  sort_entries();
414 
415  for (i=0; i<n_device_entries; i++) {
416  debug(" %s", device_entries[i].name);
417 
418  /* TODO: flags? */
419 
420  debug("\n");
421  }
422 }
423 
424 
425 /*
426  * device_set_exit_on_error():
427  *
428  * This function selects the behaviour of the emulator when a device is not
429  * found. During startup, it is nicest to abort the whole emulator session,
430  * but if a device addition is attempted from within the debugger, then it is
431  * nicer to just print a warning and continue.
432  */
433 void device_set_exit_on_error(int exit_on_error)
434 {
435  device_exit_on_error = exit_on_error;
436 }
437 
438 
439 /*
440  * device_init():
441  *
442  * Initialize the device registry, and call autodev_init() to automatically
443  * add all normal devices (from the src/devices/ directory).
444  *
445  * This function should be called before any other device_*() function is used.
446  */
447 void device_init(void)
448 {
449  device_entries = NULL;
450  device_entries_sorted = 0;
451  n_device_entries = 0;
452 
453  autodev_init();
454 }
455 
machine::bootstrap_cpu
int bootstrap_cpu
Definition: machine.h:136
autodev_init
void autodev_init(void)
Definition: autodev.cc:150
memory
Definition: memory.h:75
debug
#define debug
Definition: dev_adb.cc:57
device_init
void device_init(void)
Definition: device.cc:447
pci_entry::initf
void(* initf)(struct machine *, struct memory *, struct pci_device *)
Definition: device.h:66
device_set_exit_on_error
void device_set_exit_on_error(int exit_on_error)
Definition: device.cc:433
devinit::addr
uint64_t addr
Definition: device.h:46
devinit::machine
struct machine * machine
Definition: device.h:41
devinit::len
uint64_t len
Definition: device.h:48
device.h
pci_register
int pci_register(const char *name, void(*initf)(struct machine *, struct memory *, struct pci_device *))
Definition: device.cc:114
devinit::addr2
uint64_t addr2
Definition: device.h:47
devinit::interrupt_path
char * interrupt_path
Definition: device.h:50
strlen
void COMBINE() strlen(struct cpu *cpu, struct arm_instr_call *ic, int low_addr)
Definition: cpu_arm_instr.cc:2333
fatal
void fatal(const char *fmt,...)
Definition: main.cc:152
device_lookup
struct device_entry * device_lookup(char *name)
Definition: device.cc:162
device_dumplist
void device_dumplist(void)
Definition: device.cc:408
pci_lookup_initf
void(*)(struct machine *machine, struct memory *mem, struct pci_device *pd) pci_lookup_initf(const char *name)
Definition: device.cc:136
misc.h
device_add
void * device_add(struct machine *machine, const char *name_and_params)
Definition: device.cc:252
machine.h
machine
Definition: machine.h:97
devinit::name
char * name
Definition: device.h:43
devinit::pci_little_endian
int pci_little_endian
Definition: device.h:54
devinit
Definition: device.h:40
machine::path
char * path
Definition: machine.h:108
device_entry
Definition: device.h:59
devinit::return_ptr
void * return_ptr
Definition: device.h:56
device_entry::name
char * name
Definition: device.h:60
mystrtoull
unsigned long long mystrtoull(const char *s, char **endp, int base)
Definition: misc.cc:46
MEM_PCI_LITTLE_ENDIAN
#define MEM_PCI_LITTLE_ENDIAN
Definition: memory.h:97
device_unregister
int device_unregister(char *name)
Definition: device.cc:210
devinit::name2
char * name2
Definition: device.h:44
device_register
int device_register(const char *name, int(*initf)(struct devinit *))
Definition: device.cc:89
device_entry::initf
int(* initf)(struct devinit *)
Definition: device.h:61
pci_entry
Definition: device.h:64
devinit::in_use
int in_use
Definition: device.h:52
devinit::addr_mult
int addr_mult
Definition: device.h:53
memory.h
CHECK_ALLOCATION
#define CHECK_ALLOCATION(ptr)
Definition: misc.h:239

Generated on Tue Mar 24 2020 14:04:48 for GXemul by doxygen 1.8.17