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