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