Commit | Line | Data |
---|---|---|
c906108c SS |
1 | /* Simulator hardware option handling. |
2 | Copyright (C) 1998 Free Software Foundation, Inc. | |
3 | Contributed by Cygnus Support and Andrew Cagney. | |
4 | ||
5 | This file is part of GDB, the GNU debugger. | |
6 | ||
7 | This program is free software; you can redistribute it and/or modify | |
8 | it under the terms of the GNU General Public License as published by | |
9 | the Free Software Foundation; either version 2, or (at your option) | |
10 | any later version. | |
11 | ||
12 | This program is distributed in the hope that it will be useful, | |
13 | but WITHOUT ANY WARRANTY; without even the implied warranty of | |
14 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |
15 | GNU General Public License for more details. | |
16 | ||
17 | You should have received a copy of the GNU General Public License along | |
18 | with this program; if not, write to the Free Software Foundation, Inc., | |
19 | 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ | |
20 | ||
21 | #include "sim-main.h" | |
22 | #include "sim-assert.h" | |
23 | #include "sim-options.h" | |
24 | ||
25 | #include "sim-hw.h" | |
26 | ||
27 | #include "hw-tree.h" | |
28 | #include "hw-device.h" | |
29 | #include "hw-main.h" | |
30 | #include "hw-base.h" | |
31 | ||
32 | ||
33 | #ifdef HAVE_STRING_H | |
34 | #include <string.h> | |
35 | #else | |
36 | #ifdef HAVE_STRINGS_H | |
37 | #include <strings.h> | |
38 | #endif | |
39 | #endif | |
40 | #ifdef HAVE_STDLIB_H | |
41 | #include <stdlib.h> | |
42 | #endif | |
43 | #include <ctype.h> | |
44 | #include <sys/errno.h> | |
45 | ||
46 | ||
47 | struct sim_hw { | |
48 | struct hw *tree; | |
49 | int trace_p; | |
50 | int info_p; | |
51 | /* if called from a processor */ | |
52 | sim_cpu *cpu; | |
53 | sim_cia cia; | |
54 | }; | |
55 | ||
56 | ||
57 | struct hw * | |
58 | sim_hw_parse (struct sim_state *sd, | |
59 | const char *fmt, | |
60 | ...) | |
61 | { | |
62 | struct hw *current; | |
63 | va_list ap; | |
64 | va_start (ap, fmt); | |
65 | current = hw_tree_vparse (STATE_HW (sd)->tree, fmt, ap); | |
66 | va_end (ap); | |
67 | return current; | |
68 | } | |
69 | ||
70 | struct printer { | |
71 | struct sim_state *file; | |
72 | void (*print) (struct sim_state *, const char *, va_list ap); | |
73 | }; | |
74 | ||
75 | static void | |
76 | do_print (void *file, const char *fmt, ...) | |
77 | { | |
78 | struct printer *p = file; | |
79 | va_list ap; | |
80 | va_start (ap, fmt); | |
81 | p->print (p->file, fmt, ap); | |
82 | va_end (ap); | |
83 | } | |
84 | ||
85 | void | |
86 | sim_hw_print (struct sim_state *sd, | |
87 | void (*print) (struct sim_state *, const char *, va_list ap)) | |
88 | { | |
89 | struct printer p; | |
90 | p.file = sd; | |
91 | p.print = print; | |
92 | hw_tree_print (STATE_HW (sd)->tree, do_print, &p); | |
93 | } | |
94 | ||
95 | ||
96 | ||
97 | ||
98 | /* command line options. */ | |
99 | ||
100 | enum { | |
101 | OPTION_HW_INFO = OPTION_START, | |
102 | OPTION_HW_TRACE, | |
103 | OPTION_HW_DEVICE, | |
104 | OPTION_HW_FILE, | |
105 | }; | |
106 | ||
107 | static DECLARE_OPTION_HANDLER (hw_option_handler); | |
108 | ||
109 | static const OPTION hw_options[] = | |
110 | { | |
111 | { {"hw-info", no_argument, NULL, OPTION_HW_INFO }, | |
112 | '\0', NULL, "List configurable hw regions", | |
113 | hw_option_handler }, | |
114 | { {"info-hw", no_argument, NULL, OPTION_HW_INFO }, | |
115 | '\0', NULL, NULL, | |
116 | hw_option_handler }, | |
117 | ||
118 | { {"hw-trace", optional_argument, NULL, OPTION_HW_TRACE }, | |
119 | '\0', "on|off", "Trace all hardware devices", | |
120 | hw_option_handler }, | |
121 | { {"trace-hw", optional_argument, NULL, OPTION_HW_TRACE }, | |
122 | '\0', NULL, NULL, | |
123 | hw_option_handler }, | |
124 | ||
125 | { {"hw-device", required_argument, NULL, OPTION_HW_DEVICE }, | |
126 | '\0', "DEVICE", "Add the specified device", | |
127 | hw_option_handler }, | |
128 | ||
129 | { {"hw-file", required_argument, NULL, OPTION_HW_FILE }, | |
130 | '\0', "FILE", "Add the devices listed in the file", | |
131 | hw_option_handler }, | |
132 | ||
133 | { {NULL, no_argument, NULL, 0}, '\0', NULL, NULL, NULL } | |
134 | }; | |
135 | ||
136 | ||
137 | ||
138 | /* Copied from ../ppc/psim.c:psim_merge_device_file() */ | |
139 | ||
140 | static SIM_RC | |
141 | merge_device_file (struct sim_state *sd, | |
142 | const char *file_name) | |
143 | { | |
144 | FILE *description; | |
145 | struct hw *current = STATE_HW (sd)->tree; | |
146 | int line_nr; | |
147 | char device_path[1000]; | |
148 | ||
149 | /* try opening the file */ | |
150 | description = fopen (file_name, "r"); | |
151 | if (description == NULL) | |
152 | { | |
153 | perror (file_name); | |
154 | return SIM_RC_FAIL; | |
155 | } | |
156 | ||
157 | line_nr = 0; | |
158 | while (fgets (device_path, sizeof(device_path), description)) | |
159 | { | |
160 | char *device; | |
161 | /* check that a complete line was read */ | |
162 | if (strchr (device_path, '\n') == NULL) | |
163 | { | |
164 | fclose (description); | |
165 | sim_io_eprintf (sd, "%s:%d: line to long", file_name, line_nr); | |
166 | return SIM_RC_FAIL; | |
167 | } | |
168 | *strchr (device_path, '\n') = '\0'; | |
169 | line_nr++; | |
170 | /* skip comments ("#" or ";") and blank lines lines */ | |
171 | for (device = device_path; | |
172 | *device != '\0' && isspace (*device); | |
173 | device++); | |
174 | if (device[0] == '#' | |
175 | || device[0] == ';' | |
176 | || device[0] == '\0') | |
177 | continue; | |
178 | /* merge any appended lines */ | |
179 | while (device_path[strlen (device_path) - 1] == '\\') | |
180 | { | |
181 | int curlen = strlen (device_path) - 1; | |
182 | /* zap the `\' at the end of the line */ | |
183 | device_path[curlen] = '\0'; | |
184 | /* append the next line */ | |
185 | if (!fgets (device_path + curlen, | |
186 | sizeof (device_path) - curlen, | |
187 | description)) | |
188 | { | |
189 | fclose (description); | |
190 | sim_io_eprintf (sd, "%s:%d: unexpected eof", file_name, line_nr); | |
191 | return SIM_RC_FAIL; | |
192 | } | |
193 | if (strchr(device_path, '\n') == NULL) | |
194 | { | |
195 | fclose(description); | |
196 | sim_io_eprintf (sd, "%s:%d: line to long", file_name, line_nr); | |
197 | return SIM_RC_FAIL; | |
198 | } | |
199 | *strchr(device_path, '\n') = '\0'; | |
200 | line_nr++; | |
201 | } | |
202 | /* parse this line */ | |
203 | current = hw_tree_parse (current, "%s", device); | |
204 | } | |
205 | fclose (description); | |
206 | return SIM_RC_OK; | |
207 | } | |
208 | ||
209 | ||
210 | static SIM_RC | |
211 | hw_option_handler (struct sim_state *sd, sim_cpu *cpu, int opt, | |
212 | char *arg, int is_command) | |
213 | { | |
214 | switch (opt) | |
215 | { | |
216 | ||
217 | case OPTION_HW_INFO: | |
218 | { | |
219 | /* delay info until after the tree is finished */ | |
220 | STATE_HW (sd)->info_p = 1; | |
221 | return SIM_RC_OK; | |
222 | break; | |
223 | } | |
224 | ||
225 | case OPTION_HW_TRACE: | |
226 | { | |
227 | if (arg == NULL) | |
228 | { | |
229 | STATE_HW (sd)->trace_p = 1; | |
230 | } | |
231 | else if (strcmp (arg, "yes") == 0 | |
232 | || strcmp (arg, "on") == 0) | |
233 | { | |
234 | STATE_HW (sd)->trace_p = 1; | |
235 | } | |
236 | else if (strcmp (arg, "no") == 0 | |
237 | || strcmp (arg, "off") == 0) | |
238 | { | |
239 | STATE_HW (sd)->trace_p = 0; | |
240 | } | |
241 | else | |
242 | { | |
243 | sim_io_eprintf (sd, "Option --hw-trace ignored\n"); | |
244 | /* set tracing on all devices */ | |
245 | return SIM_RC_FAIL; | |
246 | } | |
247 | /* FIXME: Not very nice - see also hw-base.c */ | |
248 | if (STATE_HW (sd)->trace_p) | |
249 | hw_tree_parse (STATE_HW (sd)->tree, "/global-trace? true"); | |
250 | return SIM_RC_OK; | |
251 | break; | |
252 | } | |
253 | ||
254 | case OPTION_HW_DEVICE: | |
255 | { | |
256 | hw_tree_parse (STATE_HW (sd)->tree, arg); | |
257 | return SIM_RC_OK; | |
258 | } | |
259 | ||
260 | case OPTION_HW_FILE: | |
261 | { | |
262 | return merge_device_file (sd, arg); | |
263 | } | |
264 | ||
265 | default: | |
266 | sim_io_eprintf (sd, "Unknown hw option %d\n", opt); | |
267 | return SIM_RC_FAIL; | |
268 | ||
269 | } | |
270 | ||
271 | return SIM_RC_FAIL; | |
272 | } | |
273 | ||
274 | ||
275 | /* "hw" module install handler. | |
276 | ||
277 | This is called via sim_module_install to install the "hw" subsystem | |
278 | into the simulator. */ | |
279 | ||
280 | static MODULE_INIT_FN sim_hw_init; | |
281 | static MODULE_UNINSTALL_FN sim_hw_uninstall; | |
282 | ||
283 | SIM_RC | |
284 | sim_hw_install (struct sim_state *sd) | |
285 | { | |
286 | SIM_ASSERT (STATE_MAGIC (sd) == SIM_MAGIC_NUMBER); | |
287 | sim_add_option_table (sd, NULL, hw_options); | |
288 | sim_module_add_uninstall_fn (sd, sim_hw_uninstall); | |
289 | sim_module_add_init_fn (sd, sim_hw_init); | |
290 | STATE_HW (sd) = ZALLOC (struct sim_hw); | |
291 | STATE_HW (sd)->tree = hw_tree_create (sd, "core"); | |
292 | return SIM_RC_OK; | |
293 | } | |
294 | ||
295 | ||
296 | static SIM_RC | |
297 | sim_hw_init (struct sim_state *sd) | |
298 | { | |
299 | /* FIXME: anything needed? */ | |
300 | hw_tree_finish (STATE_HW (sd)->tree); | |
301 | if (STATE_HW (sd)->info_p) | |
302 | sim_hw_print (sd, sim_io_vprintf); | |
303 | return SIM_RC_OK; | |
304 | } | |
305 | ||
306 | /* Uninstall the "hw" subsystem from the simulator. */ | |
307 | ||
308 | static void | |
309 | sim_hw_uninstall (struct sim_state *sd) | |
310 | { | |
311 | /* hw_tree_delete (STATE_HW (sd)->tree); */ | |
312 | zfree (STATE_HW (sd)); | |
313 | STATE_HW (sd) = NULL; | |
314 | } | |
315 | ||
316 | ||
317 | \f | |
318 | /* Data transfers to/from the hardware device tree. There are several | |
319 | cases. */ | |
320 | ||
321 | ||
322 | /* CPU: The simulation is running and the current CPU/CIA | |
323 | initiates a data transfer. */ | |
324 | ||
325 | void | |
326 | sim_cpu_hw_io_read_buffer (sim_cpu *cpu, | |
327 | sim_cia cia, | |
328 | struct hw *hw, | |
329 | void *dest, | |
330 | int space, | |
331 | unsigned_word addr, | |
332 | unsigned nr_bytes) | |
333 | { | |
334 | SIM_DESC sd = CPU_STATE (cpu); | |
335 | STATE_HW (sd)->cpu = cpu; | |
336 | STATE_HW (sd)->cia = cia; | |
337 | if (hw_io_read_buffer (hw, dest, space, addr, nr_bytes) != nr_bytes) | |
338 | sim_engine_abort (sd, cpu, cia, "broken CPU read"); | |
339 | } | |
340 | ||
341 | void | |
342 | sim_cpu_hw_io_write_buffer (sim_cpu *cpu, | |
343 | sim_cia cia, | |
344 | struct hw *hw, | |
345 | const void *source, | |
346 | int space, | |
347 | unsigned_word addr, | |
348 | unsigned nr_bytes) | |
349 | { | |
350 | SIM_DESC sd = CPU_STATE (cpu); | |
351 | STATE_HW (sd)->cpu = cpu; | |
352 | STATE_HW (sd)->cia = cia; | |
353 | if (hw_io_write_buffer (hw, source, space, addr, nr_bytes) != nr_bytes) | |
354 | sim_engine_abort (sd, cpu, cia, "broken CPU write"); | |
355 | } | |
356 | ||
357 | ||
358 | ||
359 | ||
360 | /* SYSTEM: A data transfer is being initiated by the system. */ | |
361 | ||
362 | unsigned | |
363 | sim_hw_io_read_buffer (struct sim_state *sd, | |
364 | struct hw *hw, | |
365 | void *dest, | |
366 | int space, | |
367 | unsigned_word addr, | |
368 | unsigned nr_bytes) | |
369 | { | |
370 | STATE_HW (sd)->cpu = NULL; | |
371 | return hw_io_read_buffer (hw, dest, space, addr, nr_bytes); | |
372 | } | |
373 | ||
374 | unsigned | |
375 | sim_hw_io_write_buffer (struct sim_state *sd, | |
376 | struct hw *hw, | |
377 | const void *source, | |
378 | int space, | |
379 | unsigned_word addr, | |
380 | unsigned nr_bytes) | |
381 | { | |
382 | STATE_HW (sd)->cpu = NULL; | |
383 | return hw_io_write_buffer (hw, source, space, addr, nr_bytes); | |
384 | } | |
385 | ||
386 | ||
387 | \f | |
388 | /* Abort the simulation specifying HW as the reason */ | |
389 | ||
390 | void | |
391 | hw_vabort (struct hw *me, | |
392 | const char *fmt, | |
393 | va_list ap) | |
394 | { | |
395 | const char *name; | |
396 | char *msg; | |
397 | /* find an identity */ | |
398 | if (me != NULL && hw_path (me) != NULL && hw_path (me) [0] != '\0') | |
399 | name = hw_path (me); | |
400 | else if (me != NULL && hw_name (me) != NULL && hw_name (me)[0] != '\0') | |
401 | name = hw_name (me); | |
402 | else if (me != NULL && hw_family (me) != NULL && hw_family (me)[0] != '\0') | |
403 | name = hw_family (me); | |
404 | else | |
405 | name = "device"; | |
406 | /* construct an updated format string */ | |
407 | msg = alloca (strlen (name) + strlen (": ") + strlen (fmt) + 1); | |
408 | strcpy (msg, name); | |
409 | strcat (msg, ": "); | |
410 | strcat (msg, fmt); | |
411 | /* report the problem */ | |
412 | sim_engine_vabort (hw_system (me), | |
413 | STATE_HW (hw_system (me))->cpu, | |
414 | STATE_HW (hw_system (me))->cia, | |
415 | msg, ap); | |
416 | } | |
417 | ||
418 | void | |
419 | hw_abort (struct hw *me, | |
420 | const char *fmt, | |
421 | ...) | |
422 | { | |
423 | va_list ap; | |
424 | /* report the problem */ | |
425 | va_start (ap, fmt); | |
426 | hw_vabort (me, fmt, ap); | |
427 | va_end (ap); | |
428 | } | |
429 | ||
430 | void | |
431 | sim_hw_abort (struct sim_state *sd, | |
432 | struct hw *me, | |
433 | const char *fmt, | |
434 | ...) | |
435 | { | |
436 | va_list ap; | |
437 | va_start (ap, fmt); | |
438 | if (me == NULL) | |
439 | sim_engine_vabort (sd, NULL, NULL_CIA, fmt, ap); | |
440 | else | |
441 | hw_vabort (me, fmt, ap); | |
442 | va_end (ap); | |
443 | } | |
444 | ||
445 | ||
446 | /* MISC routines to tie HW into the rest of the system */ | |
447 | ||
448 | void | |
449 | hw_halt (struct hw *me, | |
450 | int reason, | |
451 | int status) | |
452 | { | |
453 | struct sim_state *sd = hw_system (me); | |
454 | struct sim_hw *sim = STATE_HW (sd); | |
455 | sim_engine_halt (sd, sim->cpu, NULL, sim->cia, reason, status); | |
456 | } | |
457 | ||
458 | struct _sim_cpu * | |
459 | hw_system_cpu (struct hw *me) | |
460 | { | |
461 | return STATE_HW (hw_system (me))->cpu; | |
462 | } | |
463 | ||
464 | void | |
465 | hw_trace (struct hw *me, | |
466 | const char *fmt, | |
467 | ...) | |
468 | { | |
469 | if (hw_trace_p (me)) /* to be sure, to be sure */ | |
470 | { | |
471 | va_list ap; | |
472 | va_start (ap, fmt); | |
473 | sim_io_eprintf (hw_system (me), "%s: ", hw_path (me)); | |
474 | sim_io_evprintf (hw_system (me), fmt, ap); | |
475 | sim_io_eprintf (hw_system (me), "\n"); | |
476 | va_end (ap); | |
477 | } | |
478 | } | |
479 | ||
480 | ||
481 | /* Based on gdb-4.17/sim/ppc/main.c:sim_io_read_stdin() */ | |
482 | ||
483 | int | |
484 | do_hw_poll_read (struct hw *me, | |
485 | do_hw_poll_read_method *read, | |
486 | int sim_io_fd, | |
487 | void *buf, | |
488 | unsigned sizeof_buf) | |
489 | { | |
490 | int status = read (hw_system (me), sim_io_fd, buf, sizeof_buf); | |
491 | if (status > 0) | |
492 | return status; | |
493 | else if (status == 0 && sizeof_buf == 0) | |
494 | return 0; | |
495 | else if (status == 0) | |
496 | return HW_IO_EOF; | |
497 | else /* status < 0 */ | |
498 | { | |
499 | #ifdef EAGAIN | |
500 | if (STATE_CALLBACK (hw_system (me))->last_errno == EAGAIN) | |
501 | return HW_IO_NOT_READY; | |
502 | else | |
503 | return HW_IO_EOF; | |
504 | #else | |
505 | return HW_IO_EOF; | |
506 | #endif | |
507 | } | |
508 | } |