📄 vl.c
字号:
#elif defined(__ia64)int64_t cpu_get_real_ticks(void){ int64_t val; asm volatile ("mov %0 = ar.itc" : "=r"(val) :: "memory"); return val;}#elif defined(__s390__)int64_t cpu_get_real_ticks(void){ int64_t val; asm volatile("stck 0(%1)" : "=m" (val) : "a" (&val) : "cc"); return val;}#else#error unsupported CPU#endifstatic int64_t cpu_ticks_prev;static int64_t cpu_ticks_offset;static int cpu_ticks_enabled;static inline int64_t cpu_get_ticks(void){ if (!cpu_ticks_enabled) { return cpu_ticks_offset; } else { int64_t ticks; ticks = cpu_get_real_ticks(); if (cpu_ticks_prev > ticks) { /* Note: non increasing ticks may happen if the host uses software suspend */ cpu_ticks_offset += cpu_ticks_prev - ticks; } cpu_ticks_prev = ticks; return ticks + cpu_ticks_offset; }}/* enable cpu_get_ticks() */void cpu_enable_ticks(void){ if (!cpu_ticks_enabled) { cpu_ticks_offset -= cpu_get_real_ticks(); cpu_ticks_enabled = 1; }}/* disable cpu_get_ticks() : the clock is stopped. You must not call cpu_get_ticks() after that. */void cpu_disable_ticks(void){ if (cpu_ticks_enabled) { cpu_ticks_offset = cpu_get_ticks(); cpu_ticks_enabled = 0; }}#ifdef _WIN32void cpu_calibrate_ticks(void){ LARGE_INTEGER freq; int ret; ret = QueryPerformanceFrequency(&freq); if (ret == 0) { fprintf(stderr, "Could not calibrate ticks\n"); exit(1); } ticks_per_sec = freq.QuadPart;}#elsestatic int64_t get_clock(void){ struct timeval tv; gettimeofday(&tv, NULL); return tv.tv_sec * 1000000LL + tv.tv_usec;}void cpu_calibrate_ticks(void){ int64_t usec, ticks; usec = get_clock(); ticks = cpu_get_real_ticks(); usleep(50 * 1000); usec = get_clock() - usec; ticks = cpu_get_real_ticks() - ticks; ticks_per_sec = (ticks * 1000000LL + (usec >> 1)) / usec;}#endif /* !_WIN32 *//* compute with 96 bit intermediate result: (a*b)/c */uint64_t muldiv64(uint64_t a, uint32_t b, uint32_t c){ union { uint64_t ll; struct {#ifdef WORDS_BIGENDIAN uint32_t high, low;#else uint32_t low, high;#endif } l; } u, res; uint64_t rl, rh; u.ll = a; rl = (uint64_t)u.l.low * (uint64_t)b; rh = (uint64_t)u.l.high * (uint64_t)b; rh += (rl >> 32); res.l.high = rh / c; res.l.low = (((rh % c) << 32) + (rl & 0xffffffff)) / c; return res.ll;}#define QEMU_TIMER_REALTIME 0#define QEMU_TIMER_VIRTUAL 1struct QEMUClock { int type; /* XXX: add frequency */};struct QEMUTimer { QEMUClock *clock; int64_t expire_time; QEMUTimerCB *cb; void *opaque; struct QEMUTimer *next;};QEMUClock *rt_clock;QEMUClock *vm_clock;static QEMUTimer *active_timers[2];#ifdef _WIN32static MMRESULT timerID;static HANDLE host_alarm = NULL;static unsigned int period = 1;#else/* frequency of the times() clock tick */static int timer_freq;#endifQEMUClock *qemu_new_clock(int type){ QEMUClock *clock; clock = qemu_mallocz(sizeof(QEMUClock)); if (!clock) return NULL; clock->type = type; return clock;}QEMUTimer *qemu_new_timer(QEMUClock *clock, QEMUTimerCB *cb, void *opaque){ QEMUTimer *ts; ts = qemu_mallocz(sizeof(QEMUTimer)); ts->clock = clock; ts->cb = cb; ts->opaque = opaque; return ts;}void qemu_free_timer(QEMUTimer *ts){ qemu_free(ts);}/* stop a timer, but do not dealloc it */void qemu_del_timer(QEMUTimer *ts){ QEMUTimer **pt, *t; /* NOTE: this code must be signal safe because qemu_timer_expired() can be called from a signal. */ pt = &active_timers[ts->clock->type]; for(;;) { t = *pt; if (!t) break; if (t == ts) { *pt = t->next; break; } pt = &t->next; }}/* modify the current timer so that it will be fired when current_time >= expire_time. The corresponding callback will be called. */void qemu_mod_timer(QEMUTimer *ts, int64_t expire_time){ QEMUTimer **pt, *t; qemu_del_timer(ts); /* add the timer in the sorted list */ /* NOTE: this code must be signal safe because qemu_timer_expired() can be called from a signal. */ pt = &active_timers[ts->clock->type]; for(;;) { t = *pt; if (!t) break; if (t->expire_time > expire_time) break; pt = &t->next; } ts->expire_time = expire_time; ts->next = *pt; *pt = ts;}int qemu_timer_pending(QEMUTimer *ts){ QEMUTimer *t; for(t = active_timers[ts->clock->type]; t != NULL; t = t->next) { if (t == ts) return 1; } return 0;}static inline int qemu_timer_expired(QEMUTimer *timer_head, int64_t current_time){ if (!timer_head) return 0; return (timer_head->expire_time <= current_time);}static void qemu_run_timers(QEMUTimer **ptimer_head, int64_t current_time){ QEMUTimer *ts; for(;;) { ts = *ptimer_head; if (!ts || ts->expire_time > current_time) break; /* remove timer from the list before calling the callback */ *ptimer_head = ts->next; ts->next = NULL; /* run the callback (the timer list can be modified) */ ts->cb(ts->opaque); }}int64_t qemu_get_clock(QEMUClock *clock){ switch(clock->type) { case QEMU_TIMER_REALTIME:#ifdef _WIN32 return GetTickCount();#else { struct tms tp; /* Note that using gettimeofday() is not a good solution for timers because its value change when the date is modified. */ if (timer_freq == 100) { return times(&tp) * 10; } else { return ((int64_t)times(&tp) * 1000) / timer_freq; } }#endif default: case QEMU_TIMER_VIRTUAL: return cpu_get_ticks(); }}/* save a timer */void qemu_put_timer(QEMUFile *f, QEMUTimer *ts){ uint64_t expire_time; if (qemu_timer_pending(ts)) { expire_time = ts->expire_time; } else { expire_time = -1; } qemu_put_be64(f, expire_time);}void qemu_get_timer(QEMUFile *f, QEMUTimer *ts){ uint64_t expire_time; expire_time = qemu_get_be64(f); if (expire_time != -1) { qemu_mod_timer(ts, expire_time); } else { qemu_del_timer(ts); }}static void timer_save(QEMUFile *f, void *opaque){ if (cpu_ticks_enabled) { hw_error("cannot save state if virtual timers are running"); } qemu_put_be64s(f, &cpu_ticks_offset); qemu_put_be64s(f, &ticks_per_sec);}static int timer_load(QEMUFile *f, void *opaque, int version_id){ if (version_id != 1) return -EINVAL; if (cpu_ticks_enabled) { return -EINVAL; } qemu_get_be64s(f, &cpu_ticks_offset); qemu_get_be64s(f, &ticks_per_sec); return 0;}#ifdef _WIN32void CALLBACK host_alarm_handler(UINT uTimerID, UINT uMsg, DWORD_PTR dwUser, DWORD_PTR dw1, DWORD_PTR dw2)#elsestatic void host_alarm_handler(int host_signum)#endif{#if 0#define DISP_FREQ 1000 { static int64_t delta_min = INT64_MAX; static int64_t delta_max, delta_cum, last_clock, delta, ti; static int count; ti = qemu_get_clock(vm_clock); if (last_clock != 0) { delta = ti - last_clock; if (delta < delta_min) delta_min = delta; if (delta > delta_max) delta_max = delta; delta_cum += delta; if (++count == DISP_FREQ) { printf("timer: min=%lld us max=%lld us avg=%lld us avg_freq=%0.3f Hz\n", muldiv64(delta_min, 1000000, ticks_per_sec), muldiv64(delta_max, 1000000, ticks_per_sec), muldiv64(delta_cum, 1000000 / DISP_FREQ, ticks_per_sec), (double)ticks_per_sec / ((double)delta_cum / DISP_FREQ)); count = 0; delta_min = INT64_MAX; delta_max = 0; delta_cum = 0; } } last_clock = ti; }#endif if (qemu_timer_expired(active_timers[QEMU_TIMER_VIRTUAL], qemu_get_clock(vm_clock)) || qemu_timer_expired(active_timers[QEMU_TIMER_REALTIME], qemu_get_clock(rt_clock))) {#ifdef _WIN32 SetEvent(host_alarm);#endif CPUState *env = cpu_single_env; if (env) { /* stop the currently executing cpu because a timer occured */ cpu_interrupt(env, CPU_INTERRUPT_EXIT);#ifdef USE_KQEMU if (env->kqemu_enabled) { kqemu_cpu_interrupt(env); }#endif } }}#ifndef _WIN32#if defined(__linux__)#define RTC_FREQ 1024static int rtc_fd;static int start_rtc_timer(void){ rtc_fd = open("/dev/rtc", O_RDONLY); if (rtc_fd < 0) return -1; if (ioctl(rtc_fd, RTC_IRQP_SET, RTC_FREQ) < 0) { fprintf(stderr, "Could not configure '/dev/rtc' to have a 1024 Hz timer. This is not a fatal\n" "error, but for better emulation accuracy either use a 2.6 host Linux kernel or\n" "type 'echo 1024 > /proc/sys/dev/rtc/max-user-freq' as root.\n"); goto fail; } if (ioctl(rtc_fd, RTC_PIE_ON, 0) < 0) { fail: close(rtc_fd); return -1; } pit_min_timer_count = PIT_FREQ / RTC_FREQ; return 0;}#elsestatic int start_rtc_timer(void){ return -1;}#endif /* !defined(__linux__) */#endif /* !defined(_WIN32) */static void init_timers(void){ rt_clock = qemu_new_clock(QEMU_TIMER_REALTIME); vm_clock = qemu_new_clock(QEMU_TIMER_VIRTUAL);#ifdef _WIN32 { int count=0; TIMECAPS tc; ZeroMemory(&tc, sizeof(TIMECAPS)); timeGetDevCaps(&tc, sizeof(TIMECAPS)); if (period < tc.wPeriodMin) period = tc.wPeriodMin; timeBeginPeriod(period); timerID = timeSetEvent(1, // interval (ms) period, // resolution host_alarm_handler, // function (DWORD)&count, // user parameter TIME_PERIODIC | TIME_CALLBACK_FUNCTION); if( !timerID ) { perror("failed timer alarm"); exit(1); } host_alarm = CreateEvent(NULL, FALSE, FALSE, NULL); if (!host_alarm) { perror("failed CreateEvent"); exit(1); } ResetEvent(host_alarm); } pit_min_timer_count = ((uint64_t)10000 * PIT_FREQ) / 1000000;#else { struct sigaction act; struct itimerval itv; /* get times() syscall frequency */ timer_freq = sysconf(_SC_CLK_TCK); /* timer signal */ sigfillset(&act.sa_mask); act.sa_flags = 0;#if defined (TARGET_I386) && defined(USE_CODE_COPY) act.sa_flags |= SA_ONSTACK;#endif act.sa_handler = host_alarm_handler; sigaction(SIGALRM, &act, NULL); itv.it_interval.tv_sec = 0; itv.it_interval.tv_usec = 999; /* for i386 kernel 2.6 to get 1 ms */ itv.it_value.tv_sec = 0; itv.it_value.tv_usec = 10 * 1000; setitimer(ITIMER_REAL, &itv, NULL); /* we probe the tick duration of the kernel to inform the user if the emulated kernel requested a too high timer frequency */ getitimer(ITIMER_REAL, &itv);#if defined(__linux__) /* XXX: force /dev/rtc usage because even 2.6 kernels may not have timers with 1 ms resolution. The correct solution will be to use the POSIX real time timers available in recent 2.6 kernels */ if (itv.it_interval.tv_usec > 1000 || 1) { /* try to use /dev/rtc to have a faster timer */ if (start_rtc_timer() < 0) goto use_itimer; /* disable itimer */ itv.it_interval.tv_sec = 0; itv.it_interval.tv_usec = 0; itv.it_value.tv_sec = 0; itv.it_value.tv_usec = 0; setitimer(ITIMER_REAL, &itv, NULL); /* use the RTC */ sigaction(SIGIO, &act, NULL); fcntl(rtc_fd, F_SETFL, O_ASYNC); fcntl(rtc_fd, F_SETOWN, getpid()); } else #endif /* defined(__linux__) */ { use_itimer: pit_min_timer_count = ((uint64_t)itv.it_interval.tv_usec * PIT_FREQ) / 1000000; } }#endif}void quit_timers(void){#ifdef _WIN32 timeKillEvent(timerID); timeEndPeriod(period); if (host_alarm) { CloseHandle(host_alarm); host_alarm = NULL; }#endif}/***********************************************************//* character device */int qemu_chr_write(CharDriverState *s, const uint8_t *buf, int len){ return s->chr_write(s, buf, len);}int qemu_chr_ioctl(CharDriverState *s, int cmd, void *arg){ if (!s->chr_ioctl) return -ENOTSUP; return s->chr_ioctl(s, cmd, arg);}void qemu_chr_printf(CharDriverState *s, const char *fmt, ...){ char buf[4096]; va_list ap; va_start(ap, fmt); vsnprintf(buf, sizeof(buf), fmt, ap);
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -