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