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