adevs
adevs_qemu.h
1 #ifndef _adevs_qemu_h_
2 #define _adevs_qemu_h_
3 #include "adevs.h"
4 #include <vector>
5 #include <cstring>
6 #include <pthread.h>
7 #include <string>
8 #include <list>
9 #include <sys/un.h>
10 
11 namespace adevs
12 {
13 
20 {
21  public:
27  int num_bytes_to_read();
31  void read_bytes(void* buf);
37  void write_bytes(void* data, int num_bytes);
42  virtual void append_qemu_arguments(std::vector<std::string>& args) = 0;
43  virtual ~QemuDeviceModel();
45  // Called by the reading thread to execute the read loop
46  void read_loop();
47  // Called by the writing thread to execute the write loop
48  void write_loop();
49  void init_func();
50  volatile bool _is_done_with_init;
51  protected:
52  // Start the read and writing thread running on the file descriptor and return.
53  void start();
54  class io_buffer
55  {
56  public:
57  io_buffer(int size):
58  size(size),data(new char[size]){}
59  void* get_data() { return (void*)data; }
60  int get_size() { return size; }
61  void reset_size(int size) { this->size = size; }
62  ~io_buffer() { delete [] data; }
63  private:
64  int size;
65  char* data;
66  };
67  // Read a message, return NULL on error
68  virtual io_buffer* read() = 0;
69  // Write data to the device
70  virtual void write(void* data, int num_bytes) = 0;
71  // Perform any global initializations
72  virtual void initialize_io_structures() = 0;
73  private:
79  pthread_t read_thread;
80  pthread_mutex_t read_lock;
81  std::list<io_buffer*> read_q;
87  pthread_t write_thread;
88  pthread_mutex_t write_lock;
89  pthread_cond_t write_cond;
90  std::list<io_buffer*> write_q;
91 
95  pthread_attr_t io_sched_attr[2];
96  struct sched_param fifo_param[2];
97 
98  bool running;
99 };
100 
106 class QemuNic:
107  public QemuDeviceModel
108 {
109  public:
110  QemuNic(std::string mac_addr = "");
111  void append_qemu_arguments(std::vector<std::string>& args);
112  ~QemuNic();
113  protected:
114  void write(void* data, int num_bytes);
115  io_buffer* read();
116  void initialize_io_structures(){}
117  private:
118  int fd[2];
119  std::string mac_addr;
120 };
121 
128  public QemuDeviceModel
129 {
130  public:
131  QemuSerialPort();
132  void append_qemu_arguments(std::vector<std::string>& args);
133  ~QemuSerialPort();
134  protected:
135  void write(void* data, int num_bytes);
136  io_buffer* read();
137  void initialize_io_structures();
138  private:
139  char socket_file[100];
140  struct sockaddr_un address;
141  int fd;
142  static const int buf_size;
143 };
144 
151  public QemuDeviceModel
152 {
153  public:
154  uCsimSerialPort();
155  void append_qemu_arguments(std::vector<std::string>& args);
156  ~uCsimSerialPort();
157  protected:
158  void write(void* data, int num_bytes);
159  io_buffer* read();
160  void initialize_io_structures();
161  private:
162  char read_file[100];
163  char write_file[100];
164  int read_fd, write_fd;
165  volatile bool exit_read;
166  static const int buf_size;
167 };
168 
176 {
177  public:
178  virtual ~ComputerMemoryAccess(){}
179  virtual unsigned read_mem(unsigned addr) = 0;
180  virtual void write_mem(unsigned addr, unsigned dat) = 0;
181 };
182 
188 {
189  public:
190  CompSysEmulator(){}
191  // Shutdown the emulator
192  virtual ~CompSysEmulator(){}
193  // Returns microseconds actual elapsed in the last call to run
194  virtual int elapsed() = 0;
195  // Launch a thread that will fork the emulator and regulate its progress
196  static CompSysEmulator* launch_qemu(const char* exec_file, std::vector<std::string>& args);
197  // Launches the thread and returns a pointer to a ComputerMemoryAccess object,
198  // which should be freed by the caller when done. Presently only supports
199  // access to special function registers.
200  static CompSysEmulator* launch_ucsim(
201  const char* exec_file, std::vector<std::string>& args, ComputerMemoryAccess** obj);
202  // Returns true if the emulator is still running, false if it has exitted
203  virtual bool is_alive() = 0;
204  // Run the emulator through the set elapsed time
205  virtual void run(int us) = 0;
206  // Join on the last run call
207  virtual void join() = 0;
208 };
209 
210 template <typename X>
212  public Atomic<X>
213 {
214  public:
215  QemuComputer(double quantum_seconds);
216  void delta_int();
217  void delta_ext(double e, const Bag<X>& xb);
218  void delta_conf(const Bag<X>& xb);
219  double ta();
220  void output_func(Bag<X>& yb);
221  // Get the seconds that qemu was ahead (> 0) or behind (< 0) the simulation
222  // at the most recent synchronization point
223  double get_timing_error() const { return qemu_time-sim_time; }
224  double get_qemu_time() const { return qemu_time; }
225  double get_quantum_seconds() const { return quantum; }
226  double get_mean_timing_error() const { return acc_error/(double)(error_samples+1); }
227  double get_max_timing_error() const { return max_error; }
228  virtual ~QemuComputer();
229 
230  enum EmulatorMode
231  {
232  PRECISE, // Use icount
233  FAST, // kvm
234  };
235 
236  protected:
237  void create_x86(
238  std::vector<std::string>& qemu_args,
239  std::string disk_img,
240  int mb_ram = 2048,
241  EmulatorMode emulator_mode = PRECISE);
242  void create_x86(
243  std::vector<std::string>& qemu_args,
244  std::vector<std::string>& disks,
245  std::vector<std::string>& disk_formats,
246  std::string& cdrom,
247  bool boot_cdrom,
248  int mb_ram,
249  EmulatorMode emulator_mode);
250  void create_8052(
251  std::vector<std::string>& ucsim_args,
252  std::string flash_img,
253  ComputerMemoryAccess** obj = NULL);
254 
255  private:
256  const double quantum;
257  CompSysEmulator* emulator;
258  double ttg, qemu_time, sim_time, acc_error, max_error;
259  unsigned error_samples;
260  void inject_input(void* buf, unsigned size);
261  enum { CATCHUP, THREAD_RUNNING, IDLE } mode;
262 
263  void internal_and_confluent();
264 };
265 
266 template <typename X>
267 QemuComputer<X>::QemuComputer(double quantum_seconds):
268  Atomic<X>(),
269  quantum(quantum_seconds),
270  emulator(NULL),
271  ttg(0.0),
272  qemu_time(0.0),
273  sim_time(0.0),
274  acc_error(0.0),
275  max_error(0.0),
276  error_samples(0),
277  mode(IDLE)
278 {
279 }
280 
281 template <typename X>
283 {
284  if (emulator != NULL)
285  {
286  if (mode == THREAD_RUNNING)
287  emulator->join();
288  delete emulator;
289  }
290 }
291 
292 template <typename X>
294 {
295  // Wait for the quantum to complete before looking
296  // for output
297  if (mode == THREAD_RUNNING)
298  {
299  mode = IDLE;
300  emulator->join();
301  // Get the scaled time elapsed in the qemu thread
302  qemu_time += (emulator->elapsed()/1E6);
303  }
304 }
305 
306 template <typename X>
308 {
309  sim_time += ttg;
310  // If qemu is ahead of us then advance
311  // the clock without running qemu. A
312  // simple test of qemu_time > sim_time
313  // can become stuck if a floating point error
314  // and small ttg combine such that
315  // sim_time+ttg = sim_time.
316  if (mode != CATCHUP && qemu_time > sim_time)
317  {
318  mode = CATCHUP;
319  error_samples++;
320  acc_error += get_timing_error();
321  max_error = (max_error > get_timing_error()) ? max_error : get_timing_error();
322  ttg = qemu_time - sim_time;
323  }
324  // Run the computer for another quantum if it is still alive
325  else if (emulator->is_alive())
326  {
327  assert(mode != THREAD_RUNNING);
328  emulator->run(quantum*1E6);
329  mode = THREAD_RUNNING;
330  ttg = quantum;
331  }
332  else mode = IDLE;
333 }
334 
335 template <typename X>
337 {
338  internal_and_confluent();
339 }
340 
341 template <typename X>
342 void QemuComputer<X>::delta_ext(double e, const Bag<X>& xb)
343 {
344  sim_time += e;
345  ttg -= e;
346 }
347 
348 template <typename X>
350 {
351  internal_and_confluent();
352 }
353 
354 template <typename X>
356 {
357  if (emulator->is_alive())
358  return ttg;
359  return adevs_inf<double>();
360 }
361 
362 template <typename X>
364  std::vector<std::string>& args,
365  std::vector<std::string>& disks,
366  std::vector<std::string>& disk_formats,
367  std::string& cdrom,
368  bool boot_cdrom,
369  int mb_ram,
370  EmulatorMode emulator_mode)
371 {
372  char arg_buf[1000];
373  args.push_back("-vga");
374  args.push_back("std");
375  args.push_back("-m");
376  sprintf(arg_buf,"%d",mb_ram);
377  args.push_back(arg_buf);
378  // No monitor
379  args.push_back("-monitor");
380  args.push_back("none");
381  // Simulated computer will report virtual time and not attempt to track
382  // the real system clock
383  args.push_back("-rtc");
384  args.push_back("clock=vm");
385  // Time will track the instruction count
386  if (emulator_mode == PRECISE)
387  {
388  sprintf(arg_buf,"1,sleep=off");
389  args.push_back("-icount");
390  args.push_back(arg_buf);
391  }
392  else if (emulator_mode == FAST)
393  {
394  args.push_back("-cpu");
395  args.push_back("kvm64,-kvmclock,-tsc");
396  args.push_back("-machine");
397  args.push_back("smm=off");
398  args.push_back("-enable-kvm");
399  }
400  // Attach our disk images
401  for (unsigned idx = 0; idx < disks.size(); idx++)
402  {
403  sprintf(arg_buf,"file=%s,index=%u,media=disk,format=%s",disks[idx].c_str(),idx,disk_formats[idx].c_str());
404  args.push_back("-drive");
405  args.push_back(arg_buf);
406  }
407  if (cdrom != "")
408  {
409  args.push_back("-cdrom");
410  args.push_back(cdrom);
411  if (boot_cdrom)
412  {
413  args.push_back("-boot");
414  args.push_back("d");
415  }
416  }
417  // Start the machine
418  emulator = CompSysEmulator::launch_qemu("qemu-system-i386",args);
419  assert(emulator->is_alive());
420 }
421 
422 template <typename X>
424  std::vector<std::string>& args,
425  std::string disk_image,
426  int mb_ram,
427  QemuComputer<X>::EmulatorMode emulator_mode)
428 {
429  std::string cdrom = "";
430  std::vector<std::string> disks, disk_formats;
431  disks.push_back(disk_image);
432  disk_formats.push_back("raw");
433  create_x86(args,disks,disk_formats,cdrom,false,mb_ram,emulator_mode);
434 }
435 
436 template <typename X>
438  std::vector<std::string>& args,
439  std::string flash_image,
440  ComputerMemoryAccess** obj)
441 {
442  args.push_back("-t");
443  args.push_back("8052");
444  args.push_back(flash_image);
445  emulator = CompSysEmulator::launch_ucsim("s51",args,obj);
446  assert(emulator->is_alive());
447 }
448 
449 }
450 
451 #endif
double ta()
Definition: adevs_qemu.h:355
Definition: adevs_qemu.h:19
void output_func(Bag< X > &yb)
Definition: adevs_qemu.h:293
Definition: adevs_qemu.h:150
Definition: adevs_qemu.h:211
void read_bytes(void *buf)
Definition: adevs_qemu.h:187
Definition: adevs_qemu.h:106
void write_bytes(void *data, int num_bytes)
Definition: adevs_fmi.h:56
void delta_conf(const Bag< X > &xb)
Definition: adevs_qemu.h:349
Definition: adevs_qemu.h:54
Definition: adevs_qemu.h:175
void delta_int()
Internal transition function.
Definition: adevs_qemu.h:336
virtual void append_qemu_arguments(std::vector< std::string > &args)=0
void delta_ext(double e, const Bag< X > &xb)
Definition: adevs_qemu.h:342
Definition: adevs_qemu.h:127
Definition: adevs_models.h:47