Commit 87858c89ca89bd1bb2044de0c8a9406bd6367ab7
1 parent
a6f816d6
more precise timer emulation - fixed NE2000 probe problems - added VLTMPDIR support
git-svn-id: svn://svn.savannah.nongnu.org/qemu/trunk@286 c046a42c-6fe2-441c-8c8c-71466251a162
Showing
1 changed file
with
143 additions
and
29 deletions
vl.c
| @@ -745,17 +745,19 @@ void pic_init(void) | @@ -745,17 +745,19 @@ void pic_init(void) | ||
| 745 | #define RW_STATE_LATCHED_WORD1 5 | 745 | #define RW_STATE_LATCHED_WORD1 5 |
| 746 | 746 | ||
| 747 | typedef struct PITChannelState { | 747 | typedef struct PITChannelState { |
| 748 | - uint16_t count; | 748 | + int count; /* can be 65536 */ |
| 749 | uint16_t latched_count; | 749 | uint16_t latched_count; |
| 750 | uint8_t rw_state; | 750 | uint8_t rw_state; |
| 751 | uint8_t mode; | 751 | uint8_t mode; |
| 752 | uint8_t bcd; /* not supported */ | 752 | uint8_t bcd; /* not supported */ |
| 753 | uint8_t gate; /* timer start */ | 753 | uint8_t gate; /* timer start */ |
| 754 | int64_t count_load_time; | 754 | int64_t count_load_time; |
| 755 | + int64_t count_last_edge_check_time; | ||
| 755 | } PITChannelState; | 756 | } PITChannelState; |
| 756 | 757 | ||
| 757 | PITChannelState pit_channels[3]; | 758 | PITChannelState pit_channels[3]; |
| 758 | int speaker_data_on; | 759 | int speaker_data_on; |
| 760 | +int pit_min_timer_count = 0; | ||
| 759 | 761 | ||
| 760 | int64_t ticks_per_sec; | 762 | int64_t ticks_per_sec; |
| 761 | 763 | ||
| @@ -785,13 +787,36 @@ void cpu_calibrate_ticks(void) | @@ -785,13 +787,36 @@ void cpu_calibrate_ticks(void) | ||
| 785 | ticks_per_sec = (ticks * 1000000LL + (usec >> 1)) / usec; | 787 | ticks_per_sec = (ticks * 1000000LL + (usec >> 1)) / usec; |
| 786 | } | 788 | } |
| 787 | 789 | ||
| 790 | +/* compute with 96 bit intermediate result: (a*b)/c */ | ||
| 791 | +static uint64_t muldiv64(uint64_t a, uint32_t b, uint32_t c) | ||
| 792 | +{ | ||
| 793 | + union { | ||
| 794 | + uint64_t ll; | ||
| 795 | + struct { | ||
| 796 | +#ifdef WORDS_BIGENDIAN | ||
| 797 | + uint32_t high, low; | ||
| 798 | +#else | ||
| 799 | + uint32_t low, high; | ||
| 800 | +#endif | ||
| 801 | + } l; | ||
| 802 | + } u, res; | ||
| 803 | + uint64_t rl, rh; | ||
| 804 | + | ||
| 805 | + u.ll = a; | ||
| 806 | + rl = (uint64_t)u.l.low * (uint64_t)b; | ||
| 807 | + rh = (uint64_t)u.l.high * (uint64_t)b; | ||
| 808 | + rh += (rl >> 32); | ||
| 809 | + res.l.high = rh / c; | ||
| 810 | + res.l.low = (((rh % c) << 32) + (rl & 0xffffffff)) / c; | ||
| 811 | + return res.ll; | ||
| 812 | +} | ||
| 813 | + | ||
| 788 | static int pit_get_count(PITChannelState *s) | 814 | static int pit_get_count(PITChannelState *s) |
| 789 | { | 815 | { |
| 790 | - int64_t d; | 816 | + uint64_t d; |
| 791 | int counter; | 817 | int counter; |
| 792 | 818 | ||
| 793 | - d = ((cpu_get_ticks() - s->count_load_time) * PIT_FREQ) / | ||
| 794 | - ticks_per_sec; | 819 | + d = muldiv64(cpu_get_ticks() - s->count_load_time, PIT_FREQ, ticks_per_sec); |
| 795 | switch(s->mode) { | 820 | switch(s->mode) { |
| 796 | case 0: | 821 | case 0: |
| 797 | case 1: | 822 | case 1: |
| @@ -809,11 +834,10 @@ static int pit_get_count(PITChannelState *s) | @@ -809,11 +834,10 @@ static int pit_get_count(PITChannelState *s) | ||
| 809 | /* get pit output bit */ | 834 | /* get pit output bit */ |
| 810 | static int pit_get_out(PITChannelState *s) | 835 | static int pit_get_out(PITChannelState *s) |
| 811 | { | 836 | { |
| 812 | - int64_t d; | 837 | + uint64_t d; |
| 813 | int out; | 838 | int out; |
| 814 | 839 | ||
| 815 | - d = ((cpu_get_ticks() - s->count_load_time) * PIT_FREQ) / | ||
| 816 | - ticks_per_sec; | 840 | + d = muldiv64(cpu_get_ticks() - s->count_load_time, PIT_FREQ, ticks_per_sec); |
| 817 | switch(s->mode) { | 841 | switch(s->mode) { |
| 818 | default: | 842 | default: |
| 819 | case 0: | 843 | case 0: |
| @@ -839,11 +863,74 @@ static int pit_get_out(PITChannelState *s) | @@ -839,11 +863,74 @@ static int pit_get_out(PITChannelState *s) | ||
| 839 | return out; | 863 | return out; |
| 840 | } | 864 | } |
| 841 | 865 | ||
| 866 | +/* get the number of 0 to 1 transitions we had since we call this | ||
| 867 | + function */ | ||
| 868 | +/* XXX: maybe better to use ticks precision to avoid getting edges | ||
| 869 | + twice if checks are done at very small intervals */ | ||
| 870 | +static int pit_get_out_edges(PITChannelState *s) | ||
| 871 | +{ | ||
| 872 | + uint64_t d1, d2; | ||
| 873 | + int64_t ticks; | ||
| 874 | + int ret, v; | ||
| 875 | + | ||
| 876 | + ticks = cpu_get_ticks(); | ||
| 877 | + d1 = muldiv64(s->count_last_edge_check_time - s->count_load_time, | ||
| 878 | + PIT_FREQ, ticks_per_sec); | ||
| 879 | + d2 = muldiv64(ticks - s->count_load_time, | ||
| 880 | + PIT_FREQ, ticks_per_sec); | ||
| 881 | + s->count_last_edge_check_time = ticks; | ||
| 882 | + switch(s->mode) { | ||
| 883 | + default: | ||
| 884 | + case 0: | ||
| 885 | + if (d1 < s->count && d2 >= s->count) | ||
| 886 | + ret = 1; | ||
| 887 | + else | ||
| 888 | + ret = 0; | ||
| 889 | + break; | ||
| 890 | + case 1: | ||
| 891 | + ret = 0; | ||
| 892 | + break; | ||
| 893 | + case 2: | ||
| 894 | + d1 /= s->count; | ||
| 895 | + d2 /= s->count; | ||
| 896 | + ret = d2 - d1; | ||
| 897 | + break; | ||
| 898 | + case 3: | ||
| 899 | + v = s->count - (s->count >> 1); | ||
| 900 | + d1 = (d1 + v) / s->count; | ||
| 901 | + d2 = (d2 + v) / s->count; | ||
| 902 | + ret = d2 - d1; | ||
| 903 | + break; | ||
| 904 | + case 4: | ||
| 905 | + case 5: | ||
| 906 | + if (d1 < s->count && d2 >= s->count) | ||
| 907 | + ret = 1; | ||
| 908 | + else | ||
| 909 | + ret = 0; | ||
| 910 | + break; | ||
| 911 | + } | ||
| 912 | + return ret; | ||
| 913 | +} | ||
| 914 | + | ||
| 915 | +static inline void pit_load_count(PITChannelState *s, int val) | ||
| 916 | +{ | ||
| 917 | + if (val == 0) | ||
| 918 | + val = 0x10000; | ||
| 919 | + s->count_load_time = cpu_get_ticks(); | ||
| 920 | + s->count_last_edge_check_time = s->count_load_time; | ||
| 921 | + s->count = val; | ||
| 922 | + if (s == &pit_channels[0] && val <= pit_min_timer_count) { | ||
| 923 | + fprintf(stderr, | ||
| 924 | + "\nWARNING: vl: on your system, accurate timer emulation is impossible if its frequency is more than %d Hz. If using a 2.5.xx Linux kernel, you must patch asm/param.h to change HZ from 1000 to 100.\n\n", | ||
| 925 | + PIT_FREQ / pit_min_timer_count); | ||
| 926 | + } | ||
| 927 | +} | ||
| 928 | + | ||
| 842 | void pit_ioport_write(CPUX86State *env, uint32_t addr, uint32_t val) | 929 | void pit_ioport_write(CPUX86State *env, uint32_t addr, uint32_t val) |
| 843 | { | 930 | { |
| 844 | int channel, access; | 931 | int channel, access; |
| 845 | PITChannelState *s; | 932 | PITChannelState *s; |
| 846 | - | 933 | + |
| 847 | addr &= 3; | 934 | addr &= 3; |
| 848 | if (addr == 3) { | 935 | if (addr == 3) { |
| 849 | channel = val >> 6; | 936 | channel = val >> 6; |
| @@ -857,27 +944,24 @@ void pit_ioport_write(CPUX86State *env, uint32_t addr, uint32_t val) | @@ -857,27 +944,24 @@ void pit_ioport_write(CPUX86State *env, uint32_t addr, uint32_t val) | ||
| 857 | s->rw_state = RW_STATE_LATCHED_WORD0; | 944 | s->rw_state = RW_STATE_LATCHED_WORD0; |
| 858 | break; | 945 | break; |
| 859 | default: | 946 | default: |
| 947 | + s->mode = (val >> 1) & 7; | ||
| 948 | + s->bcd = val & 1; | ||
| 860 | s->rw_state = access - 1 + RW_STATE_LSB; | 949 | s->rw_state = access - 1 + RW_STATE_LSB; |
| 861 | break; | 950 | break; |
| 862 | } | 951 | } |
| 863 | - s->mode = (val >> 1) & 7; | ||
| 864 | - s->bcd = val & 1; | ||
| 865 | } else { | 952 | } else { |
| 866 | s = &pit_channels[addr]; | 953 | s = &pit_channels[addr]; |
| 867 | switch(s->rw_state) { | 954 | switch(s->rw_state) { |
| 868 | case RW_STATE_LSB: | 955 | case RW_STATE_LSB: |
| 869 | - s->count_load_time = cpu_get_ticks(); | ||
| 870 | - s->count = val; | 956 | + pit_load_count(s, val); |
| 871 | break; | 957 | break; |
| 872 | case RW_STATE_MSB: | 958 | case RW_STATE_MSB: |
| 873 | - s->count_load_time = cpu_get_ticks(); | ||
| 874 | - s->count = (val << 8); | 959 | + pit_load_count(s, val << 8); |
| 875 | break; | 960 | break; |
| 876 | case RW_STATE_WORD0: | 961 | case RW_STATE_WORD0: |
| 877 | case RW_STATE_WORD1: | 962 | case RW_STATE_WORD1: |
| 878 | if (s->rw_state & 1) { | 963 | if (s->rw_state & 1) { |
| 879 | - s->count_load_time = cpu_get_ticks(); | ||
| 880 | - s->count = (s->latched_count & 0xff) | (val << 8); | 964 | + pit_load_count(s, (s->latched_count & 0xff) | (val << 8)); |
| 881 | } else { | 965 | } else { |
| 882 | s->latched_count = val; | 966 | s->latched_count = val; |
| 883 | } | 967 | } |
| @@ -935,16 +1019,23 @@ uint32_t speaker_ioport_read(CPUX86State *env, uint32_t addr) | @@ -935,16 +1019,23 @@ uint32_t speaker_ioport_read(CPUX86State *env, uint32_t addr) | ||
| 935 | 1019 | ||
| 936 | void pit_init(void) | 1020 | void pit_init(void) |
| 937 | { | 1021 | { |
| 938 | - pit_channels[0].gate = 1; | ||
| 939 | - pit_channels[1].gate = 1; | ||
| 940 | - pit_channels[2].gate = 0; | ||
| 941 | - | 1022 | + PITChannelState *s; |
| 1023 | + int i; | ||
| 1024 | + | ||
| 1025 | + cpu_calibrate_ticks(); | ||
| 1026 | + | ||
| 1027 | + for(i = 0;i < 3; i++) { | ||
| 1028 | + s = &pit_channels[i]; | ||
| 1029 | + s->mode = 3; | ||
| 1030 | + s->gate = (i != 2); | ||
| 1031 | + pit_load_count(s, 0); | ||
| 1032 | + } | ||
| 1033 | + | ||
| 942 | register_ioport_writeb(0x40, 4, pit_ioport_write); | 1034 | register_ioport_writeb(0x40, 4, pit_ioport_write); |
| 943 | register_ioport_readb(0x40, 3, pit_ioport_read); | 1035 | register_ioport_readb(0x40, 3, pit_ioport_read); |
| 944 | 1036 | ||
| 945 | register_ioport_readb(0x61, 1, speaker_ioport_read); | 1037 | register_ioport_readb(0x61, 1, speaker_ioport_read); |
| 946 | register_ioport_writeb(0x61, 1, speaker_ioport_write); | 1038 | register_ioport_writeb(0x61, 1, speaker_ioport_write); |
| 947 | - cpu_calibrate_ticks(); | ||
| 948 | } | 1039 | } |
| 949 | 1040 | ||
| 950 | /***********************************************************/ | 1041 | /***********************************************************/ |
| @@ -1462,6 +1553,8 @@ void ne2000_ioport_write(CPUX86State *env, uint32_t addr, uint32_t val) | @@ -1462,6 +1553,8 @@ void ne2000_ioport_write(CPUX86State *env, uint32_t addr, uint32_t val) | ||
| 1462 | s->rcnt == 0) { | 1553 | s->rcnt == 0) { |
| 1463 | s->isr |= ENISR_RDC; | 1554 | s->isr |= ENISR_RDC; |
| 1464 | ne2000_update_irq(s); | 1555 | ne2000_update_irq(s); |
| 1556 | + /* XXX: find a better solution for irqs */ | ||
| 1557 | + cpu_x86_interrupt(global_env); | ||
| 1465 | } | 1558 | } |
| 1466 | if (val & E8390_TRANS) { | 1559 | if (val & E8390_TRANS) { |
| 1467 | net_send_packet(s, s->mem + (s->tpsr << 8), s->tcnt); | 1560 | net_send_packet(s, s->mem + (s->tpsr << 8), s->tcnt); |
| @@ -1671,13 +1764,23 @@ static void host_segv_handler(int host_signum, siginfo_t *info, | @@ -1671,13 +1764,23 @@ static void host_segv_handler(int host_signum, siginfo_t *info, | ||
| 1671 | } | 1764 | } |
| 1672 | 1765 | ||
| 1673 | static int timer_irq_pending; | 1766 | static int timer_irq_pending; |
| 1767 | +static int timer_irq_count; | ||
| 1674 | 1768 | ||
| 1675 | static void host_alarm_handler(int host_signum, siginfo_t *info, | 1769 | static void host_alarm_handler(int host_signum, siginfo_t *info, |
| 1676 | void *puc) | 1770 | void *puc) |
| 1677 | { | 1771 | { |
| 1678 | - /* just exit from the cpu to have a chance to handle timers */ | ||
| 1679 | - cpu_x86_interrupt(global_env); | ||
| 1680 | - timer_irq_pending = 1; | 1772 | + /* NOTE: since usually the OS asks a 100 Hz clock, there can be |
| 1773 | + some drift between cpu_get_ticks() and the interrupt time. So | ||
| 1774 | + we queue some interrupts to avoid missing some */ | ||
| 1775 | + timer_irq_count += pit_get_out_edges(&pit_channels[0]); | ||
| 1776 | + if (timer_irq_count) { | ||
| 1777 | + if (timer_irq_count > 2) | ||
| 1778 | + timer_irq_count = 2; | ||
| 1779 | + timer_irq_count--; | ||
| 1780 | + /* just exit from the cpu to have a chance to handle timers */ | ||
| 1781 | + cpu_x86_interrupt(global_env); | ||
| 1782 | + timer_irq_pending = 1; | ||
| 1783 | + } | ||
| 1681 | } | 1784 | } |
| 1682 | 1785 | ||
| 1683 | void help(void) | 1786 | void help(void) |
| @@ -1705,7 +1808,8 @@ int main(int argc, char **argv) | @@ -1705,7 +1808,8 @@ int main(int argc, char **argv) | ||
| 1705 | struct sigaction act; | 1808 | struct sigaction act; |
| 1706 | struct itimerval itv; | 1809 | struct itimerval itv; |
| 1707 | CPUX86State *env; | 1810 | CPUX86State *env; |
| 1708 | - | 1811 | + const char *tmpdir; |
| 1812 | + | ||
| 1709 | /* we never want that malloc() uses mmap() */ | 1813 | /* we never want that malloc() uses mmap() */ |
| 1710 | mallopt(M_MMAP_THRESHOLD, 4096 * 1024); | 1814 | mallopt(M_MMAP_THRESHOLD, 4096 * 1024); |
| 1711 | 1815 | ||
| @@ -1749,14 +1853,19 @@ int main(int argc, char **argv) | @@ -1749,14 +1853,19 @@ int main(int argc, char **argv) | ||
| 1749 | net_init(); | 1853 | net_init(); |
| 1750 | 1854 | ||
| 1751 | /* init the memory */ | 1855 | /* init the memory */ |
| 1752 | - strcpy(phys_ram_file, "/tmp/vlXXXXXX"); | 1856 | + tmpdir = getenv("VLTMPDIR"); |
| 1857 | + if (!tmpdir) | ||
| 1858 | + tmpdir = "/tmp"; | ||
| 1859 | + snprintf(phys_ram_file, sizeof(phys_ram_file), "%s/vlXXXXXX", tmpdir); | ||
| 1753 | if (mkstemp(phys_ram_file) < 0) { | 1860 | if (mkstemp(phys_ram_file) < 0) { |
| 1754 | - fprintf(stderr, "Could not create temporary memory file\n"); | 1861 | + fprintf(stderr, "Could not create temporary memory file '%s'\n", |
| 1862 | + phys_ram_file); | ||
| 1755 | exit(1); | 1863 | exit(1); |
| 1756 | } | 1864 | } |
| 1757 | phys_ram_fd = open(phys_ram_file, O_CREAT | O_TRUNC | O_RDWR, 0600); | 1865 | phys_ram_fd = open(phys_ram_file, O_CREAT | O_TRUNC | O_RDWR, 0600); |
| 1758 | if (phys_ram_fd < 0) { | 1866 | if (phys_ram_fd < 0) { |
| 1759 | - fprintf(stderr, "Could not open temporary memory file\n"); | 1867 | + fprintf(stderr, "Could not open temporary memory file '%s'\n", |
| 1868 | + phys_ram_file); | ||
| 1760 | exit(1); | 1869 | exit(1); |
| 1761 | } | 1870 | } |
| 1762 | ftruncate(phys_ram_fd, phys_ram_size); | 1871 | ftruncate(phys_ram_fd, phys_ram_size); |
| @@ -1856,10 +1965,15 @@ int main(int argc, char **argv) | @@ -1856,10 +1965,15 @@ int main(int argc, char **argv) | ||
| 1856 | env->eflags = 0x2; | 1965 | env->eflags = 0x2; |
| 1857 | 1966 | ||
| 1858 | itv.it_interval.tv_sec = 0; | 1967 | itv.it_interval.tv_sec = 0; |
| 1859 | - itv.it_interval.tv_usec = 10 * 1000; | 1968 | + itv.it_interval.tv_usec = 1000; |
| 1860 | itv.it_value.tv_sec = 0; | 1969 | itv.it_value.tv_sec = 0; |
| 1861 | itv.it_value.tv_usec = 10 * 1000; | 1970 | itv.it_value.tv_usec = 10 * 1000; |
| 1862 | setitimer(ITIMER_REAL, &itv, NULL); | 1971 | setitimer(ITIMER_REAL, &itv, NULL); |
| 1972 | + /* we probe the tick duration of the kernel to inform the user if | ||
| 1973 | + the emulated kernel requested a too high timer frequency */ | ||
| 1974 | + getitimer(ITIMER_REAL, &itv); | ||
| 1975 | + pit_min_timer_count = ((uint64_t)itv.it_interval.tv_usec * PIT_FREQ) / | ||
| 1976 | + 1000000; | ||
| 1863 | 1977 | ||
| 1864 | for(;;) { | 1978 | for(;;) { |
| 1865 | struct pollfd ufds[2], *pf, *serial_ufd, *net_ufd; | 1979 | struct pollfd ufds[2], *pf, *serial_ufd, *net_ufd; |