Commit 87858c89ca89bd1bb2044de0c8a9406bd6367ab7

Authored by bellard
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
... ... @@ -745,17 +745,19 @@ void pic_init(void)
745 745 #define RW_STATE_LATCHED_WORD1 5
746 746  
747 747 typedef struct PITChannelState {
748   - uint16_t count;
  748 + int count; /* can be 65536 */
749 749 uint16_t latched_count;
750 750 uint8_t rw_state;
751 751 uint8_t mode;
752 752 uint8_t bcd; /* not supported */
753 753 uint8_t gate; /* timer start */
754 754 int64_t count_load_time;
  755 + int64_t count_last_edge_check_time;
755 756 } PITChannelState;
756 757  
757 758 PITChannelState pit_channels[3];
758 759 int speaker_data_on;
  760 +int pit_min_timer_count = 0;
759 761  
760 762 int64_t ticks_per_sec;
761 763  
... ... @@ -785,13 +787,36 @@ void cpu_calibrate_ticks(void)
785 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 814 static int pit_get_count(PITChannelState *s)
789 815 {
790   - int64_t d;
  816 + uint64_t d;
791 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 820 switch(s->mode) {
796 821 case 0:
797 822 case 1:
... ... @@ -809,11 +834,10 @@ static int pit_get_count(PITChannelState *s)
809 834 /* get pit output bit */
810 835 static int pit_get_out(PITChannelState *s)
811 836 {
812   - int64_t d;
  837 + uint64_t d;
813 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 841 switch(s->mode) {
818 842 default:
819 843 case 0:
... ... @@ -839,11 +863,74 @@ static int pit_get_out(PITChannelState *s)
839 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 929 void pit_ioport_write(CPUX86State *env, uint32_t addr, uint32_t val)
843 930 {
844 931 int channel, access;
845 932 PITChannelState *s;
846   -
  933 +
847 934 addr &= 3;
848 935 if (addr == 3) {
849 936 channel = val >> 6;
... ... @@ -857,27 +944,24 @@ void pit_ioport_write(CPUX86State *env, uint32_t addr, uint32_t val)
857 944 s->rw_state = RW_STATE_LATCHED_WORD0;
858 945 break;
859 946 default:
  947 + s->mode = (val >> 1) & 7;
  948 + s->bcd = val & 1;
860 949 s->rw_state = access - 1 + RW_STATE_LSB;
861 950 break;
862 951 }
863   - s->mode = (val >> 1) & 7;
864   - s->bcd = val & 1;
865 952 } else {
866 953 s = &pit_channels[addr];
867 954 switch(s->rw_state) {
868 955 case RW_STATE_LSB:
869   - s->count_load_time = cpu_get_ticks();
870   - s->count = val;
  956 + pit_load_count(s, val);
871 957 break;
872 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 960 break;
876 961 case RW_STATE_WORD0:
877 962 case RW_STATE_WORD1:
878 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 965 } else {
882 966 s->latched_count = val;
883 967 }
... ... @@ -935,16 +1019,23 @@ uint32_t speaker_ioport_read(CPUX86State *env, uint32_t addr)
935 1019  
936 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 1034 register_ioport_writeb(0x40, 4, pit_ioport_write);
943 1035 register_ioport_readb(0x40, 3, pit_ioport_read);
944 1036  
945 1037 register_ioport_readb(0x61, 1, speaker_ioport_read);
946 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 1553 s->rcnt == 0) {
1463 1554 s->isr |= ENISR_RDC;
1464 1555 ne2000_update_irq(s);
  1556 + /* XXX: find a better solution for irqs */
  1557 + cpu_x86_interrupt(global_env);
1465 1558 }
1466 1559 if (val & E8390_TRANS) {
1467 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 1764 }
1672 1765  
1673 1766 static int timer_irq_pending;
  1767 +static int timer_irq_count;
1674 1768  
1675 1769 static void host_alarm_handler(int host_signum, siginfo_t *info,
1676 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 1786 void help(void)
... ... @@ -1705,7 +1808,8 @@ int main(int argc, char **argv)
1705 1808 struct sigaction act;
1706 1809 struct itimerval itv;
1707 1810 CPUX86State *env;
1708   -
  1811 + const char *tmpdir;
  1812 +
1709 1813 /* we never want that malloc() uses mmap() */
1710 1814 mallopt(M_MMAP_THRESHOLD, 4096 * 1024);
1711 1815  
... ... @@ -1749,14 +1853,19 @@ int main(int argc, char **argv)
1749 1853 net_init();
1750 1854  
1751 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 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 1863 exit(1);
1756 1864 }
1757 1865 phys_ram_fd = open(phys_ram_file, O_CREAT | O_TRUNC | O_RDWR, 0600);
1758 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 1869 exit(1);
1761 1870 }
1762 1871 ftruncate(phys_ram_fd, phys_ram_size);
... ... @@ -1856,10 +1965,15 @@ int main(int argc, char **argv)
1856 1965 env->eflags = 0x2;
1857 1966  
1858 1967 itv.it_interval.tv_sec = 0;
1859   - itv.it_interval.tv_usec = 10 * 1000;
  1968 + itv.it_interval.tv_usec = 1000;
1860 1969 itv.it_value.tv_sec = 0;
1861 1970 itv.it_value.tv_usec = 10 * 1000;
1862 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 1978 for(;;) {
1865 1979 struct pollfd ufds[2], *pf, *serial_ufd, *net_ufd;
... ...