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
This commit is contained in:
		
							parent
							
								
									a6f816d697
								
							
						
					
					
						commit
						87858c89ca
					
				
							
								
								
									
										160
									
								
								vl.c
									
									
									
									
									
								
							
							
						
						
									
										160
									
								
								vl.c
									
									
									
									
									
								
							@ -745,17 +745,19 @@ void pic_init(void)
 | 
				
			|||||||
#define RW_STATE_LATCHED_WORD1 5
 | 
					#define RW_STATE_LATCHED_WORD1 5
 | 
				
			||||||
 | 
					
 | 
				
			||||||
typedef struct PITChannelState {
 | 
					typedef struct PITChannelState {
 | 
				
			||||||
    uint16_t count;
 | 
					    int count; /* can be 65536 */
 | 
				
			||||||
    uint16_t latched_count;
 | 
					    uint16_t latched_count;
 | 
				
			||||||
    uint8_t rw_state;
 | 
					    uint8_t rw_state;
 | 
				
			||||||
    uint8_t mode;
 | 
					    uint8_t mode;
 | 
				
			||||||
    uint8_t bcd; /* not supported */
 | 
					    uint8_t bcd; /* not supported */
 | 
				
			||||||
    uint8_t gate; /* timer start */
 | 
					    uint8_t gate; /* timer start */
 | 
				
			||||||
    int64_t count_load_time;
 | 
					    int64_t count_load_time;
 | 
				
			||||||
 | 
					    int64_t count_last_edge_check_time;
 | 
				
			||||||
} PITChannelState;
 | 
					} PITChannelState;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
PITChannelState pit_channels[3];
 | 
					PITChannelState pit_channels[3];
 | 
				
			||||||
int speaker_data_on;
 | 
					int speaker_data_on;
 | 
				
			||||||
 | 
					int pit_min_timer_count = 0;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
int64_t ticks_per_sec;
 | 
					int64_t ticks_per_sec;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@ -785,13 +787,36 @@ void cpu_calibrate_ticks(void)
 | 
				
			|||||||
    ticks_per_sec = (ticks * 1000000LL + (usec >> 1)) / usec;
 | 
					    ticks_per_sec = (ticks * 1000000LL + (usec >> 1)) / usec;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/* compute with 96 bit intermediate result: (a*b)/c */
 | 
				
			||||||
 | 
					static 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;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
static int pit_get_count(PITChannelState *s)
 | 
					static int pit_get_count(PITChannelState *s)
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
    int64_t d;
 | 
					    uint64_t d;
 | 
				
			||||||
    int counter;
 | 
					    int counter;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    d = ((cpu_get_ticks() - s->count_load_time) * PIT_FREQ) / 
 | 
					    d = muldiv64(cpu_get_ticks() - s->count_load_time, PIT_FREQ, ticks_per_sec);
 | 
				
			||||||
        ticks_per_sec;
 | 
					 | 
				
			||||||
    switch(s->mode) {
 | 
					    switch(s->mode) {
 | 
				
			||||||
    case 0:
 | 
					    case 0:
 | 
				
			||||||
    case 1:
 | 
					    case 1:
 | 
				
			||||||
@ -809,11 +834,10 @@ static int pit_get_count(PITChannelState *s)
 | 
				
			|||||||
/* get pit output bit */
 | 
					/* get pit output bit */
 | 
				
			||||||
static int pit_get_out(PITChannelState *s)
 | 
					static int pit_get_out(PITChannelState *s)
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
    int64_t d;
 | 
					    uint64_t d;
 | 
				
			||||||
    int out;
 | 
					    int out;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    d = ((cpu_get_ticks() - s->count_load_time) * PIT_FREQ) / 
 | 
					    d = muldiv64(cpu_get_ticks() - s->count_load_time, PIT_FREQ, ticks_per_sec);
 | 
				
			||||||
        ticks_per_sec;
 | 
					 | 
				
			||||||
    switch(s->mode) {
 | 
					    switch(s->mode) {
 | 
				
			||||||
    default:
 | 
					    default:
 | 
				
			||||||
    case 0:
 | 
					    case 0:
 | 
				
			||||||
@ -839,6 +863,69 @@ static int pit_get_out(PITChannelState *s)
 | 
				
			|||||||
    return out;
 | 
					    return out;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/* get the number of 0 to 1 transitions we had since we call this
 | 
				
			||||||
 | 
					   function */
 | 
				
			||||||
 | 
					/* XXX: maybe better to use ticks precision to avoid getting edges
 | 
				
			||||||
 | 
					   twice if checks are done at very small intervals */
 | 
				
			||||||
 | 
					static int pit_get_out_edges(PITChannelState *s)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
					    uint64_t d1, d2;
 | 
				
			||||||
 | 
					    int64_t ticks;
 | 
				
			||||||
 | 
					    int ret, v;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    ticks = cpu_get_ticks();
 | 
				
			||||||
 | 
					    d1 = muldiv64(s->count_last_edge_check_time - s->count_load_time, 
 | 
				
			||||||
 | 
					                 PIT_FREQ, ticks_per_sec);
 | 
				
			||||||
 | 
					    d2 = muldiv64(ticks - s->count_load_time, 
 | 
				
			||||||
 | 
					                  PIT_FREQ, ticks_per_sec);
 | 
				
			||||||
 | 
					    s->count_last_edge_check_time = ticks;
 | 
				
			||||||
 | 
					    switch(s->mode) {
 | 
				
			||||||
 | 
					    default:
 | 
				
			||||||
 | 
					    case 0:
 | 
				
			||||||
 | 
					        if (d1 < s->count && d2 >= s->count)
 | 
				
			||||||
 | 
					            ret = 1;
 | 
				
			||||||
 | 
					        else
 | 
				
			||||||
 | 
					            ret = 0;
 | 
				
			||||||
 | 
					        break;
 | 
				
			||||||
 | 
					    case 1:
 | 
				
			||||||
 | 
					        ret = 0;
 | 
				
			||||||
 | 
					        break;
 | 
				
			||||||
 | 
					    case 2:
 | 
				
			||||||
 | 
					        d1 /= s->count;
 | 
				
			||||||
 | 
					        d2 /= s->count;
 | 
				
			||||||
 | 
					        ret = d2 - d1;
 | 
				
			||||||
 | 
					        break;
 | 
				
			||||||
 | 
					    case 3:
 | 
				
			||||||
 | 
					        v = s->count - (s->count >> 1);
 | 
				
			||||||
 | 
					        d1 = (d1 + v) / s->count;
 | 
				
			||||||
 | 
					        d2 = (d2 + v) / s->count;
 | 
				
			||||||
 | 
					        ret = d2 - d1;
 | 
				
			||||||
 | 
					        break;
 | 
				
			||||||
 | 
					    case 4:
 | 
				
			||||||
 | 
					    case 5:
 | 
				
			||||||
 | 
					        if (d1 < s->count && d2 >= s->count)
 | 
				
			||||||
 | 
					            ret = 1;
 | 
				
			||||||
 | 
					        else
 | 
				
			||||||
 | 
					            ret = 0;
 | 
				
			||||||
 | 
					        break;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    return ret;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					static inline void pit_load_count(PITChannelState *s, int val)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
					    if (val == 0)
 | 
				
			||||||
 | 
					        val = 0x10000;
 | 
				
			||||||
 | 
					    s->count_load_time = cpu_get_ticks();
 | 
				
			||||||
 | 
					    s->count_last_edge_check_time = s->count_load_time;
 | 
				
			||||||
 | 
					    s->count = val;
 | 
				
			||||||
 | 
					    if (s == &pit_channels[0] && val <= pit_min_timer_count) {
 | 
				
			||||||
 | 
					        fprintf(stderr, 
 | 
				
			||||||
 | 
					                "\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", 
 | 
				
			||||||
 | 
					                PIT_FREQ / pit_min_timer_count);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
void pit_ioport_write(CPUX86State *env, uint32_t addr, uint32_t val)
 | 
					void pit_ioport_write(CPUX86State *env, uint32_t addr, uint32_t val)
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
    int channel, access;
 | 
					    int channel, access;
 | 
				
			||||||
@ -857,27 +944,24 @@ void pit_ioport_write(CPUX86State *env, uint32_t addr, uint32_t val)
 | 
				
			|||||||
            s->rw_state = RW_STATE_LATCHED_WORD0;
 | 
					            s->rw_state = RW_STATE_LATCHED_WORD0;
 | 
				
			||||||
            break;
 | 
					            break;
 | 
				
			||||||
        default:
 | 
					        default:
 | 
				
			||||||
 | 
					            s->mode = (val >> 1) & 7;
 | 
				
			||||||
 | 
					            s->bcd = val & 1;
 | 
				
			||||||
            s->rw_state = access - 1 +  RW_STATE_LSB;
 | 
					            s->rw_state = access - 1 +  RW_STATE_LSB;
 | 
				
			||||||
            break;
 | 
					            break;
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
        s->mode = (val >> 1) & 7;
 | 
					 | 
				
			||||||
        s->bcd = val & 1;
 | 
					 | 
				
			||||||
    } else {
 | 
					    } else {
 | 
				
			||||||
        s = &pit_channels[addr];
 | 
					        s = &pit_channels[addr];
 | 
				
			||||||
        switch(s->rw_state) {
 | 
					        switch(s->rw_state) {
 | 
				
			||||||
        case RW_STATE_LSB:
 | 
					        case RW_STATE_LSB:
 | 
				
			||||||
            s->count_load_time = cpu_get_ticks();
 | 
					            pit_load_count(s, val);
 | 
				
			||||||
            s->count = val;
 | 
					 | 
				
			||||||
            break;
 | 
					            break;
 | 
				
			||||||
        case RW_STATE_MSB:
 | 
					        case RW_STATE_MSB:
 | 
				
			||||||
            s->count_load_time = cpu_get_ticks();
 | 
					            pit_load_count(s, val << 8);
 | 
				
			||||||
            s->count = (val << 8);
 | 
					 | 
				
			||||||
            break;
 | 
					            break;
 | 
				
			||||||
        case RW_STATE_WORD0:
 | 
					        case RW_STATE_WORD0:
 | 
				
			||||||
        case RW_STATE_WORD1:
 | 
					        case RW_STATE_WORD1:
 | 
				
			||||||
            if (s->rw_state & 1) {
 | 
					            if (s->rw_state & 1) {
 | 
				
			||||||
                s->count_load_time = cpu_get_ticks();
 | 
					                pit_load_count(s, (s->latched_count & 0xff) | (val << 8));
 | 
				
			||||||
                s->count = (s->latched_count & 0xff) | (val << 8);
 | 
					 | 
				
			||||||
            } else {
 | 
					            } else {
 | 
				
			||||||
                s->latched_count = val;
 | 
					                s->latched_count = val;
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
@ -935,16 +1019,23 @@ uint32_t speaker_ioport_read(CPUX86State *env, uint32_t addr)
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
void pit_init(void)
 | 
					void pit_init(void)
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
    pit_channels[0].gate = 1;
 | 
					    PITChannelState *s;
 | 
				
			||||||
    pit_channels[1].gate = 1;
 | 
					    int i;
 | 
				
			||||||
    pit_channels[2].gate = 0;
 | 
					
 | 
				
			||||||
 | 
					    cpu_calibrate_ticks();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    for(i = 0;i < 3; i++) {
 | 
				
			||||||
 | 
					        s = &pit_channels[i];
 | 
				
			||||||
 | 
					        s->mode = 3;
 | 
				
			||||||
 | 
					        s->gate = (i != 2);
 | 
				
			||||||
 | 
					        pit_load_count(s, 0);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    register_ioport_writeb(0x40, 4, pit_ioport_write);
 | 
					    register_ioport_writeb(0x40, 4, pit_ioport_write);
 | 
				
			||||||
    register_ioport_readb(0x40, 3, pit_ioport_read);
 | 
					    register_ioport_readb(0x40, 3, pit_ioport_read);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    register_ioport_readb(0x61, 1, speaker_ioport_read);
 | 
					    register_ioport_readb(0x61, 1, speaker_ioport_read);
 | 
				
			||||||
    register_ioport_writeb(0x61, 1, speaker_ioport_write);
 | 
					    register_ioport_writeb(0x61, 1, speaker_ioport_write);
 | 
				
			||||||
    cpu_calibrate_ticks();
 | 
					 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
/***********************************************************/
 | 
					/***********************************************************/
 | 
				
			||||||
@ -1462,6 +1553,8 @@ void ne2000_ioport_write(CPUX86State *env, uint32_t addr, uint32_t val)
 | 
				
			|||||||
                s->rcnt == 0) {
 | 
					                s->rcnt == 0) {
 | 
				
			||||||
                s->isr |= ENISR_RDC;
 | 
					                s->isr |= ENISR_RDC;
 | 
				
			||||||
                ne2000_update_irq(s);
 | 
					                ne2000_update_irq(s);
 | 
				
			||||||
 | 
					                /* XXX: find a better solution for irqs */
 | 
				
			||||||
 | 
					                cpu_x86_interrupt(global_env);
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
            if (val & E8390_TRANS) {
 | 
					            if (val & E8390_TRANS) {
 | 
				
			||||||
                net_send_packet(s, s->mem + (s->tpsr << 8), s->tcnt);
 | 
					                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,
 | 
				
			|||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
static int timer_irq_pending;
 | 
					static int timer_irq_pending;
 | 
				
			||||||
 | 
					static int timer_irq_count;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
static void host_alarm_handler(int host_signum, siginfo_t *info, 
 | 
					static void host_alarm_handler(int host_signum, siginfo_t *info, 
 | 
				
			||||||
                               void *puc)
 | 
					                               void *puc)
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
 | 
					    /* NOTE: since usually the OS asks a 100 Hz clock, there can be
 | 
				
			||||||
 | 
					       some drift between cpu_get_ticks() and the interrupt time. So
 | 
				
			||||||
 | 
					       we queue some interrupts to avoid missing some */
 | 
				
			||||||
 | 
					    timer_irq_count += pit_get_out_edges(&pit_channels[0]);
 | 
				
			||||||
 | 
					    if (timer_irq_count) {
 | 
				
			||||||
 | 
					        if (timer_irq_count > 2)
 | 
				
			||||||
 | 
					            timer_irq_count = 2;
 | 
				
			||||||
 | 
					        timer_irq_count--;
 | 
				
			||||||
        /* just exit from the cpu to have a chance to handle timers */
 | 
					        /* just exit from the cpu to have a chance to handle timers */
 | 
				
			||||||
        cpu_x86_interrupt(global_env);
 | 
					        cpu_x86_interrupt(global_env);
 | 
				
			||||||
        timer_irq_pending = 1;
 | 
					        timer_irq_pending = 1;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
void help(void)
 | 
					void help(void)
 | 
				
			||||||
@ -1705,6 +1808,7 @@ int main(int argc, char **argv)
 | 
				
			|||||||
    struct sigaction act;
 | 
					    struct sigaction act;
 | 
				
			||||||
    struct itimerval itv;
 | 
					    struct itimerval itv;
 | 
				
			||||||
    CPUX86State *env;
 | 
					    CPUX86State *env;
 | 
				
			||||||
 | 
					    const char *tmpdir;
 | 
				
			||||||
    
 | 
					    
 | 
				
			||||||
    /* we never want that malloc() uses mmap() */
 | 
					    /* we never want that malloc() uses mmap() */
 | 
				
			||||||
    mallopt(M_MMAP_THRESHOLD, 4096 * 1024);
 | 
					    mallopt(M_MMAP_THRESHOLD, 4096 * 1024);
 | 
				
			||||||
@ -1749,14 +1853,19 @@ int main(int argc, char **argv)
 | 
				
			|||||||
    net_init();
 | 
					    net_init();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    /* init the memory */
 | 
					    /* init the memory */
 | 
				
			||||||
    strcpy(phys_ram_file, "/tmp/vlXXXXXX");
 | 
					    tmpdir = getenv("VLTMPDIR");
 | 
				
			||||||
 | 
					    if (!tmpdir)
 | 
				
			||||||
 | 
					        tmpdir = "/tmp";
 | 
				
			||||||
 | 
					    snprintf(phys_ram_file, sizeof(phys_ram_file), "%s/vlXXXXXX", tmpdir);
 | 
				
			||||||
    if (mkstemp(phys_ram_file) < 0) {
 | 
					    if (mkstemp(phys_ram_file) < 0) {
 | 
				
			||||||
        fprintf(stderr, "Could not create temporary memory file\n");
 | 
					        fprintf(stderr, "Could not create temporary memory file '%s'\n", 
 | 
				
			||||||
 | 
					                phys_ram_file);
 | 
				
			||||||
        exit(1);
 | 
					        exit(1);
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
    phys_ram_fd = open(phys_ram_file, O_CREAT | O_TRUNC | O_RDWR, 0600);
 | 
					    phys_ram_fd = open(phys_ram_file, O_CREAT | O_TRUNC | O_RDWR, 0600);
 | 
				
			||||||
    if (phys_ram_fd < 0) {
 | 
					    if (phys_ram_fd < 0) {
 | 
				
			||||||
        fprintf(stderr, "Could not open temporary memory file\n");
 | 
					        fprintf(stderr, "Could not open temporary memory file '%s'\n", 
 | 
				
			||||||
 | 
					                phys_ram_file);
 | 
				
			||||||
        exit(1);
 | 
					        exit(1);
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
    ftruncate(phys_ram_fd, phys_ram_size);
 | 
					    ftruncate(phys_ram_fd, phys_ram_size);
 | 
				
			||||||
@ -1856,10 +1965,15 @@ int main(int argc, char **argv)
 | 
				
			|||||||
    env->eflags = 0x2;
 | 
					    env->eflags = 0x2;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    itv.it_interval.tv_sec = 0;
 | 
					    itv.it_interval.tv_sec = 0;
 | 
				
			||||||
    itv.it_interval.tv_usec = 10 * 1000;
 | 
					    itv.it_interval.tv_usec = 1000;
 | 
				
			||||||
    itv.it_value.tv_sec = 0;
 | 
					    itv.it_value.tv_sec = 0;
 | 
				
			||||||
    itv.it_value.tv_usec = 10 * 1000;
 | 
					    itv.it_value.tv_usec = 10 * 1000;
 | 
				
			||||||
    setitimer(ITIMER_REAL, &itv, NULL);
 | 
					    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);
 | 
				
			||||||
 | 
					    pit_min_timer_count = ((uint64_t)itv.it_interval.tv_usec * PIT_FREQ) / 
 | 
				
			||||||
 | 
					        1000000;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    for(;;) {
 | 
					    for(;;) {
 | 
				
			||||||
        struct pollfd ufds[2], *pf, *serial_ufd, *net_ufd;
 | 
					        struct pollfd ufds[2], *pf, *serial_ufd, *net_ufd;
 | 
				
			||||||
 | 
				
			|||||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user