1 /* Main driver program for VMIPS.
2 Copyright 2001, 2003 Brian R. Gaeke.
3 Copyright 2002, 2003 Paul Twohey.
4
5 This file is part of VMIPS.
6
7 VMIPS is free software; you can redistribute it and/or modify it
8 under the terms of the GNU General Public License as published by the
9 Free Software Foundation; either version 2 of the License, or (at your
10 option) any later version.
11
12 VMIPS is distributed in the hope that it will be useful, but
13 WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
14 or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
15 for more details.
16
17 You should have received a copy of the GNU General Public License along
18 with VMIPS; if not, write to the Free Software Foundation, Inc.,
19 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */
20
21 #include "clock.h"
22 #include "clockdev.h"
23 #include "clockreg.h"
24 #include "cpzeroreg.h"
25 #include "debug.h"
26 #include "error.h"
27 #include "endiantest.h"
28 #include "haltreg.h"
29 #include "haltdev.h"
30 #include "intctrl.h"
31 #include "range.h"
32 #include "spimconsole.h"
33 #include "mapper.h"
34 #include "memorymodule.h"
35 #include "cpu.h"
36 #include "cpzero.h"
37 #include "spimconsreg.h"
38 #include "vmips.h"
39 #include "options.h"
40 #include "decrtc.h"
41 #include "decrtcreg.h"
42 #include "deccsr.h"
43 #include "deccsrreg.h"
44 #include "decstat.h"
45 #include "decserial.h"
46 #include "testdev.h"
47 #include "stub-dis.h"
48 #include "rommodule.h"
49 #include "interactor.h"
50 #include <fcntl.h>
51 #include <cerrno>
52 #include <csignal>
53 #include <cstdarg>
54 #include <cstring>
55 #include <string>
56 #include <exception>
57
58 vmips *machine;
59
60 void
refresh_options(void)61 vmips::refresh_options(void)
62 {
63 /* Extract important flags and things. */
64 opt_bootmsg = opt->option("bootmsg")->flag;
65 opt_clockdevice = opt->option("clockdevice")->flag;
66 opt_debug = opt->option("debug")->flag;
67 opt_dumpcpu = opt->option("dumpcpu")->flag;
68 opt_dumpcp0 = opt->option("dumpcp0")->flag;
69 opt_haltdevice = opt->option("haltdevice")->flag;
70 opt_haltdumpcpu = opt->option("haltdumpcpu")->flag;
71 opt_haltdumpcp0 = opt->option("haltdumpcp0")->flag;
72 opt_instcounts = opt->option("instcounts")->flag;
73 opt_memdump = opt->option("memdump")->flag;
74 opt_realtime = opt->option("realtime")->flag;
75
76 opt_clockspeed = opt->option("clockspeed")->num;
77 clock_nanos = 1000000000/opt_clockspeed;
78
79 opt_clockintr = opt->option("clockintr")->num;
80 opt_clockdeviceirq = opt->option("clockdeviceirq")->num;
81 opt_loadaddr = opt->option("loadaddr")->num;
82 opt_memsize = opt->option("memsize")->num;
83 opt_timeratio = opt->option("timeratio")->num;
84
85 opt_memdumpfile = opt->option("memdumpfile")->str;
86 opt_image = opt->option("romfile")->str;
87 opt_execname = opt->option("execname")->str;
88 opt_ttydev = opt->option("ttydev")->str;
89 opt_ttydev2 = opt->option("ttydev2")->str;
90
91 opt_decrtc = opt->option("decrtc")->flag;
92 opt_deccsr = opt->option("deccsr")->flag;
93 opt_decstat = opt->option("decstat")->flag;
94 opt_decserial = opt->option("decserial")->flag;
95 opt_spimconsole = opt->option("spimconsole")->flag;
96 opt_testdev = opt->option("testdev")->flag;
97 }
98
99 /* Set up some machine globals, and process command line arguments,
100 * configuration files, etc.
101 */
vmips(int argc,char * argv[])102 vmips::vmips(int argc, char *argv[])
103 : opt(new Options), state(HALT),
104 clock(0), clock_device(0), halt_device(0), spim_console(0),
105 num_instrs(0), interactor(0)
106 {
107 opt->process_options (argc, argv);
108 refresh_options();
109 }
110
~vmips()111 vmips::~vmips()
112 {
113 if (disasm) delete disasm;
114 if (opt_debug && dbgr) delete dbgr;
115 if (cpu) delete cpu;
116 if (physmem) delete physmem;
117 //if (clock) delete clock; // crash in this dtor - double free?
118 if (intc) delete intc;
119 if (opt) delete opt;
120 }
121
122 void
setup_machine(void)123 vmips::setup_machine(void)
124 {
125 /* Construct the various vmips components. */
126 intc = new IntCtrl;
127 physmem = new Mapper;
128 cpu = new CPU (*physmem, *intc);
129
130 /* Set up the debugger interface, if applicable. */
131 if (opt_debug)
132 dbgr = new Debug (*cpu, *physmem);
133
134 /* Direct the libopcodes disassembler output to stderr. */
135 disasm = new Disassembler (host_bigendian, stderr);
136 }
137
138 /* Connect the file or device named NAME to line number L of
139 * console device C, or do nothing if NAME is "off".
140 * If NAME is "stdout", then the device will be connected to stdout.
141 */
setup_console_line(int l,char * name,TerminalController * c,const char * c_name)142 void vmips::setup_console_line(int l, char *name, TerminalController *c, const
143 char *c_name)
144 {
145 /* If they said to turn off the tty line, do nothing. */
146 if (strcmp(name, "off") == 0)
147 return;
148
149 int ttyfd;
150 if (strcmp(name, "stdout") == 0) {
151 /* If they asked for stdout, give them stdout. */
152 ttyfd = fileno(stdout);
153 } else {
154 /* Open the file or device in question. */
155 ttyfd = open(name, O_RDWR | O_NONBLOCK);
156 if (ttyfd == -1) {
157 /* If we can't open it, warn and use stdout instead. */
158 error("Opening %s (terminal %d): %s", name, l,
159 strerror(errno));
160 warning("using stdout, input disabled\n");
161 ttyfd = fileno(stdout);
162 }
163 }
164
165 /* Connect it to the SPIM-compatible console device. */
166 c->connect_terminal(ttyfd, l);
167 boot_msg("Connected fd %d to %s line %d.\n", ttyfd, c_name, l);
168 }
169
setup_spimconsole()170 bool vmips::setup_spimconsole()
171 {
172 /* FIXME: It would be helpful to restore tty modes on a SIGINT or
173 other abortive exit or when vmips has been foregrounded after
174 being in the background. The restoration mechanism should use
175 TerminalController::reinitialze_terminals() */
176
177 if (!opt_spimconsole)
178 return true;
179
180 spim_console = new SpimConsoleDevice( clock );
181 physmem->map_at_physical_address( spim_console, SPIM_BASE );
182 boot_msg("Mapping %s to physical address 0x%08x\n",
183 spim_console->descriptor_str(), SPIM_BASE);
184
185 intc->connectLine(IRQ2, spim_console);
186 intc->connectLine(IRQ3, spim_console);
187 intc->connectLine(IRQ4, spim_console);
188 intc->connectLine(IRQ5, spim_console);
189 intc->connectLine(IRQ6, spim_console);
190 boot_msg("Connected IRQ2-IRQ6 to %s\n",spim_console->descriptor_str());
191
192 setup_console_line(0, opt_ttydev, spim_console,
193 spim_console->descriptor_str ());
194 setup_console_line(1, opt_ttydev2, spim_console,
195 spim_console->descriptor_str ());
196 return true;
197 }
198
setup_clockdevice()199 bool vmips::setup_clockdevice()
200 {
201 if( !opt_clockdevice )
202 return true;
203
204 uint32 clock_irq;
205 if( !(clock_irq = DeviceInt::num2irq( opt_clockdeviceirq )) ) {
206 error( "invalid clockdeviceirq (%u), irq numbers must be 2-7.",
207 opt_clockdeviceirq );
208 return false;
209 }
210
211 /* Microsecond Clock at base physaddr CLOCK_BASE */
212 clock_device = new ClockDevice( clock, clock_irq, opt_clockintr );
213 physmem->map_at_physical_address( clock_device, CLOCK_BASE );
214 boot_msg( "Mapping %s to physical address 0x%08x\n",
215 clock_device->descriptor_str(), CLOCK_BASE );
216
217 intc->connectLine( clock_irq, clock_device );
218 boot_msg( "Connected %s to the %s\n", DeviceInt::strlineno(clock_irq),
219 clock_device->descriptor_str() );
220
221 return true;
222 }
223
setup_decrtc()224 bool vmips::setup_decrtc()
225 {
226 if (!opt_decrtc)
227 return true;
228
229 /* Always use IRQ3 ("hw interrupt level 1") for RTC. */
230 uint32 decrtc_irq = DeviceInt::num2irq (3);
231
232 /* DECstation 5000/200 DS1287-based RTC at base physaddr DECRTC_BASE */
233 decrtc_device = new DECRTCDevice( clock, decrtc_irq );
234 physmem->map_at_physical_address( decrtc_device, DECRTC_BASE );
235 boot_msg( "Mapping %s to physical address 0x%08x\n",
236 decrtc_device->descriptor_str(), DECRTC_BASE );
237
238 intc->connectLine( decrtc_irq, decrtc_device );
239 boot_msg( "Connected %s to the %s\n", DeviceInt::strlineno(decrtc_irq),
240 decrtc_device->descriptor_str() );
241
242 return true;
243 }
244
setup_deccsr()245 bool vmips::setup_deccsr()
246 {
247 if (!opt_deccsr)
248 return true;
249
250 /* DECstation 5000/200 Control/Status Reg at base physaddr DECCSR_BASE */
251 /* Connected to IRQ2 */
252 static const uint32 DECCSR_MIPS_IRQ = DeviceInt::num2irq (2);
253 deccsr_device = new DECCSRDevice (DECCSR_MIPS_IRQ);
254 physmem->map_at_physical_address (deccsr_device, DECCSR_BASE);
255 boot_msg ("Mapping %s to physical address 0x%08x\n",
256 deccsr_device->descriptor_str(), DECCSR_BASE);
257
258 intc->connectLine (DECCSR_MIPS_IRQ, deccsr_device);
259 boot_msg("Connected %s to the %s\n", DeviceInt::strlineno(DECCSR_MIPS_IRQ),
260 deccsr_device->descriptor_str());
261
262 return true;
263 }
264
setup_decstat()265 bool vmips::setup_decstat()
266 {
267 if (!opt_decstat)
268 return true;
269
270 /* DECstation 5000/200 CHKSYN + ERRADR at base physaddr DECSTAT_BASE */
271 decstat_device = new DECStatDevice( );
272 physmem->map_at_physical_address( decstat_device, DECSTAT_BASE );
273 boot_msg( "Mapping %s to physical address 0x%08x\n",
274 decstat_device->descriptor_str(), DECSTAT_BASE );
275
276 return true;
277 }
278
setup_decserial()279 bool vmips::setup_decserial()
280 {
281 if (!opt_decserial)
282 return true;
283
284 /* DECstation 5000/200 DZ11 serial at base physaddr DECSERIAL_BASE */
285 /* Uses CSR interrupt SystemInterfaceCSRInt */
286 decserial_device = new DECSerialDevice (clock, SystemInterfaceCSRInt);
287 physmem->map_at_physical_address (decserial_device, DECSERIAL_BASE );
288 boot_msg ("Mapping %s to physical address 0x%08x\n",
289 decserial_device->descriptor_str (), DECSERIAL_BASE );
290
291 // Use printer line for console.
292 setup_console_line (3, opt_ttydev, decserial_device,
293 decserial_device->descriptor_str ());
294 return true;
295 }
296
setup_testdev()297 bool vmips::setup_testdev()
298 {
299 if (!opt_testdev)
300 return true;
301
302 test_device = new TestDev();
303 physmem->map_at_physical_address(test_device, TEST_BASE);
304 boot_msg("Mapping %s to physical address 0x%08x\n",
305 test_device->descriptor_str(), TEST_BASE);
306 return true;
307 }
308
setup_haltdevice()309 bool vmips::setup_haltdevice()
310 {
311 if( !opt_haltdevice )
312 return true;
313
314 halt_device = new HaltDevice( this );
315 physmem->map_at_physical_address( halt_device, HALT_BASE );
316 boot_msg( "Mapping %s to physical address 0x%08x\n",
317 halt_device->descriptor_str(), HALT_BASE );
318
319 return true;
320 }
321
boot_msg(const char * msg,...)322 void vmips::boot_msg( const char *msg, ... )
323 {
324 if( !opt_bootmsg )
325 return;
326
327 va_list ap;
328 va_start( ap, msg );
329 vfprintf( stderr, msg, ap );
330 va_end( ap );
331
332 fflush( stderr );
333 }
334
335 int
host_endian_selftest(void)336 vmips::host_endian_selftest(void)
337 {
338 try {
339 EndianSelfTester est;
340 machine->host_bigendian = est.host_is_big_endian();
341 if (!machine->host_bigendian) {
342 boot_msg ("Little-Endian host processor detected.\n");
343 } else {
344 boot_msg ("Big-Endian host processor detected.\n");
345 }
346 return 0;
347 } catch (std::string &err) {
348 boot_msg (err.c_str ());
349 return -1;
350 }
351 }
352
353 void
halt(void)354 vmips::halt(void)
355 {
356 state = HALT;
357 }
358
359 void
attn_key(void)360 vmips::attn_key(void)
361 {
362 state = INTERACT;
363 }
364
dump_cpu_info(bool dumpcpu,bool dumpcp0)365 void vmips::dump_cpu_info(bool dumpcpu, bool dumpcp0) {
366 if (dumpcpu) {
367 cpu->dump_regs (stderr);
368 cpu->dump_stack (stderr);
369 }
370 if (dumpcp0)
371 cpu->cpzero_dump_regs_and_tlb (stderr);
372 }
373
374 void
step(void)375 vmips::step(void)
376 {
377 /* Process instructions. */
378 cpu->step();
379
380 /* Keep track of time passing. Each instruction either takes
381 * clock_nanos nanoseconds, or we use pass_realtime() to check the
382 * system clock.
383 */
384 if( !opt_realtime )
385 clock->increment_time(clock_nanos);
386 else
387 clock->pass_realtime(opt_timeratio);
388
389 /* If user requested it, dump registers from CPU and/or CP0. */
390 dump_cpu_info (opt_dumpcpu, opt_dumpcp0);
391
392 num_instrs++;
393 }
394
395 long
timediff(struct timeval * after,struct timeval * before)396 timediff(struct timeval *after, struct timeval *before)
397 {
398 return (after->tv_sec * 1000000 + after->tv_usec) -
399 (before->tv_sec * 1000000 + before->tv_usec);
400 }
401
402 bool
setup_rom()403 vmips::setup_rom ()
404 {
405 // Open ROM image.
406 FILE *rom = fopen (opt_image, "rb");
407 if (!rom) {
408 error ("Could not open ROM `%s': %s", opt_image, strerror (errno));
409 return false;
410 }
411 // Translate loadaddr to physical address.
412 opt_loadaddr -= KSEG1_CONST_TRANSLATION;
413 ROMModule *rm;
414 try {
415 rm = new ROMModule (rom);
416 } catch (int errcode) {
417 error ("mmap failed for %s: %s", opt_image, strerror (errcode));
418 return false;
419 }
420 // Map the ROM image to the virtual physical memory.
421 physmem->map_at_physical_address (rm, opt_loadaddr);
422 boot_msg ("Mapping ROM image (%s, %u words) to physical address 0x%08x\n",
423 opt_image, rm->getExtent () / 4, rm->getBase ());
424 // Point debugger at wherever the user thinks the ROM is.
425 if (opt_debug)
426 if (dbgr->setup (opt_loadaddr, rm->getExtent () / 4) < 0)
427 return false; // Error in setting up debugger.
428 return true;
429 }
430
431 bool
setup_ram()432 vmips::setup_ram ()
433 {
434 // Make a new RAM module and install it at base physical address 0.
435 memmod = new MemoryModule(opt_memsize);
436 physmem->map_at_physical_address(memmod, 0);
437 boot_msg( "Mapping RAM module (host=%p, %uKB) to physical address 0x%x\n",
438 memmod->getAddress (), memmod->getExtent () / 1024, memmod->getBase ());
439 return true;
440 }
441
442 bool
setup_clock()443 vmips::setup_clock ()
444 {
445 /* Set up the clock with the current time. */
446 timeval start;
447 gettimeofday(&start, NULL);
448 timespec start_ts;
449 TIMEVAL_TO_TIMESPEC( &start, &start_ts );
450 clock = new Clock( start_ts );
451 return true;
452 }
453
454 static void
halt_machine_by_signal(int sig)455 halt_machine_by_signal (int sig)
456 {
457 machine->halt();
458 }
459
460 /// Interact with user. Returns true if we should continue, false otherwise.
461 ///
462 bool
interact()463 vmips::interact ()
464 {
465 TerminalController *c;
466 if (opt_spimconsole) c = spim_console;
467 else if (opt_decserial) c = decserial_device;
468 else c = 0;
469 if (c) c->suspend();
470 bool should_continue = true;
471 printf ("\n");
472 if (!interactor) interactor = create_interactor ();
473 interactor->interact ();
474 if (state == INTERACT) state = RUN;
475 if (state == HALT) should_continue = false;
476 if (c) c->reinitialize_terminals ();
477 return should_continue;
478 }
479
480 int
run()481 vmips::run()
482 {
483 /* Check host processor endianness. */
484 if (host_endian_selftest () != 0) {
485 error( "Could not determine host processor endianness." );
486 return 1;
487 }
488
489 /* Set up the rest of the machine components. */
490 setup_machine();
491
492 if (!setup_rom ())
493 return 1;
494
495 if (!setup_ram ())
496 return 1;
497
498 if (!setup_haltdevice ())
499 return 1;
500
501 if (!setup_clock ())
502 return 1;
503
504 if (!setup_clockdevice ())
505 return 1;
506
507 if (!setup_decrtc ())
508 return 1;
509
510 if (!setup_deccsr ())
511 return 1;
512
513 if (!setup_decstat ())
514 return 1;
515
516 if (!setup_decserial ())
517 return 1;
518
519 if (!setup_spimconsole ())
520 return 1;
521
522 if (!setup_testdev ())
523 return 1;
524
525 signal (SIGQUIT, halt_machine_by_signal);
526
527 boot_msg( "Hit Ctrl-\\ to halt machine, Ctrl-_ for a debug prompt.\n" );
528
529 /* Reset the CPU. */
530 boot_msg( "\n*************RESET*************\n\n" );
531 cpu->reset();
532
533 if (!setup_exe ())
534 return 1;
535
536 timeval start;
537 if (opt_instcounts)
538 gettimeofday(&start, NULL);
539
540 state = (opt_debug ? DEBUG : RUN);
541 while (state != HALT) {
542 switch (state) {
543 case RUN:
544 while (state == RUN) { step (); }
545 break;
546 case DEBUG:
547 while (state == DEBUG) { dbgr->serverloop(); }
548 break;
549 case INTERACT:
550 while (state == INTERACT) { interact(); }
551 break;
552 }
553 }
554
555 timeval end;
556 if (opt_instcounts)
557 gettimeofday(&end, NULL);
558
559 /* Halt! */
560 boot_msg( "\n*************HALT*************\n\n" );
561
562 /* If we're tracing, dump the trace. */
563 cpu->flush_trace ();
564
565 /* If user requested it, dump registers from CPU and/or CP0. */
566 if (opt_haltdumpcpu || opt_haltdumpcp0) {
567 fprintf(stderr,"Dumping:\n");
568 dump_cpu_info (opt_haltdumpcpu, opt_haltdumpcp0);
569 }
570
571 if (opt_instcounts) {
572 double elapsed = (double) timediff(&end, &start) / 1000000.0;
573 fprintf(stderr, "%u instructions in %.5f seconds (%.3f "
574 "instructions per second)\n", num_instrs, elapsed,
575 ((double) num_instrs) / elapsed);
576 }
577
578 if (opt_memdump) {
579 fprintf(stderr,"Dumping RAM to %s...", opt_memdumpfile);
580 if (FILE *ramdump = fopen (opt_memdumpfile, "wb")) {
581 fwrite (memmod->getAddress (), memmod->getExtent (), 1, ramdump);
582 fclose(ramdump);
583 fprintf(stderr,"succeeded.\n");
584 } else {
585 error( "\nRAM dump failed: %s", strerror(errno) );
586 }
587 }
588
589 /* We're done. */
590 boot_msg( "Goodbye.\n" );
591 return 0;
592 }
593
vmips_unexpected()594 static void vmips_unexpected() {
595 fatal_error ("unexpected exception");
596 }
597
vmips_terminate()598 static void vmips_terminate() {
599 fatal_error ("uncaught exception");
600 }
601
602 int
main(int argc,char ** argv)603 main(int argc, char **argv)
604 try {
605 std::set_unexpected(vmips_unexpected);
606 std::set_terminate(vmips_terminate);
607
608 machine = new vmips(argc, argv);
609 int rc = machine->run();
610 delete machine; /* No disassemble Number Five!! */
611 return rc;
612 }
613 catch( std::bad_alloc &b ) {
614 fatal_error( "unable to allocate memory" );
615 }
616
617