/* * isa_bench.c * * Quick test of access speed to ISA port I/O and ISA mem I/O. * Will reveal shadowed ROMs and which side of the ISA bridge * is handling the various ISA mem regions from 640k -> 1MB. * * Repeated port I/O on 0x80 varies from 0.5MiB/s to 1MiB/s. * ISA memory I/O varies from 3MiB/s to 5MiB/s. In both cases * older machines with the ISA bus clocked significantly above * 8MHz will give values closer to the high end. * * Memory I/O significantly higher than 5MiB/s is an indication * of a shadowed ROM and/or a mem region that is not passed * through the ISA bridge on a PCI machine. * * You need to be root to do I/O to 0x80 (the unused POST port) * and to mmap(/dev/mem) for reading. Some old shared mem ISA * devices may not like having their mem read so beware. * * This code is released under GPL v2, and comes with NO WARRANTY. * If it breaks, you get to keep both pieces... * * Compile: gcc -s -Wall -O2 isa_speed.c -o isa_speed * * Paul Gortmaker. */ #define VERSION 1.0 #include #include #include #include #include #include #include #include #include #include #define DUMMY_PORT 0x80 #define WAITTIME 3 #define ISA_WINSIZE 0x1000 #define ISA_START 0xA0000 volatile int done = 0; inline void isa_memspeed(unsigned long); inline void memspeed(); inline void rdtsc_io_test(); inline unsigned long outb_test(); inline unsigned long inb_test(); void print_stats(unsigned long, char *); void alarm_intr() { done = 1; } int main() { unsigned long here, count = 0; if (ioperm(DUMMY_PORT, 1, 1)) { perror("ioperm"); return errno; } printf("\n\tISA Memory I/O Speed Test\n"); printf("\t=========================\n"); printf("\n\t\twith repeated %d byte reads for %d seconds\n\n", ISA_WINSIZE, WAITTIME); memspeed(); for (here=ISA_START; here < 0xFFFFF ; here+=0x8000) isa_memspeed(here); printf("\n\tISA Port I/O Speed Test\n"); printf("\t=======================\n"); signal(SIGALRM, alarm_intr); alarm(WAITTIME); count = inb_test(); print_stats(count, "inb"); signal(SIGALRM, alarm_intr); alarm(WAITTIME); count = outb_test(); print_stats(count, "outb"); printf("\nPort I/O speed measured with rdtsc:\n"); rdtsc_io_test(); return 0; } /* rdtsc test code hacked from one of Linus' e-mails... */ #define rdtsc(low) \ __asm__ __volatile__("rdtsc" : "=a" (low) : : "edx") #define TIME(x,y) \ min = 100000; \ for (i = 0; i < 1000; i++) { \ unsigned long start,end; \ rdtsc(start); \ x; \ rdtsc(end); \ end -= start; \ if (end < min) \ min = end; \ } \ printf("\t\t" y ": %lu cycles\n", min); #define OUT asm volatile("outb %al,$0x80") #define IN asm volatile("inb $0x80": : :"ax") #define CPUID asm volatile("cpuid": : :"ax", "dx", "cx", "bx") void ill_intr() { printf("This CPU does not support rdtsc - skipping rdtsc tests.\n"); exit(-1); } void rdtsc_io_test() { unsigned long min; unsigned int i; signal(SIGILL, ill_intr); TIME(/* */, "nothing"); TIME(CPUID, "cpuid"); TIME(OUT, "outb"); TIME(IN, "inb"); } inline unsigned long outb_test() { unsigned long count = 0; done = 0; while (!done) { outb(0x00, DUMMY_PORT); outb(0x01, DUMMY_PORT); outb(0x02, DUMMY_PORT); outb(0x04, DUMMY_PORT); outb(0x08, DUMMY_PORT); outb(0x10, DUMMY_PORT); outb(0x20, DUMMY_PORT); outb(0x40, DUMMY_PORT); outb(0x80, DUMMY_PORT); outb(0xFF, DUMMY_PORT); count+=10; } return count; } inline unsigned long inb_test() { unsigned long count = 0; done = 0; while (!done) { inb(DUMMY_PORT); inb(DUMMY_PORT); inb(DUMMY_PORT); inb(DUMMY_PORT); inb(DUMMY_PORT); inb(DUMMY_PORT); inb(DUMMY_PORT); inb(DUMMY_PORT); inb(DUMMY_PORT); inb(DUMMY_PORT); count+=10; } return count; } /* Assume 3 ISA cycles per I/O (e.g. BALE, -IOW or -IOR etc.) */ void guess_ISA_bus(unsigned long count){ int io_recovery = 0; float speed; printf("I/O Recovery\t\t\tISA Speed (MHz)\n"); do { speed = (float)count/1e6*((3.0+io_recovery)/WAITTIME); if (speed > 5) printf(" %d\t\t\t\t%.2f\n", io_recovery, speed); io_recovery++; } while (speed < 20 && io_recovery < 25); } void print_stats(unsigned long count, char *insn) { printf("%ld %s in %d seconds (%.2fMiB/s), ", count, insn, WAITTIME, (float)count/(WAITTIME*1024*1024)); printf("time per i/o = %.2fus.\n", WAITTIME*1e6/count); #ifdef GUESS_ISA_MHZ guess_ISA_bus(count); #endif } void isa_memspeed(unsigned long here) { int fd; unsigned char *isa_mem; unsigned long count; unsigned char *buf; buf = malloc(ISA_WINSIZE); if (buf == NULL) exit(ENOMEM); fd = open("/dev/mem", O_RDONLY); if (fd == -1) { perror("open /dev/mem"); exit(-1); } isa_mem = mmap(0, ISA_WINSIZE, PROT_READ, MAP_SHARED, fd, here); if ((long)isa_mem == -1) { perror("memmap /dev/mem"); exit(-1); } printf("At %#lx - 0x%.2x, 0x%.2x, ... (", here, isa_mem[0], isa_mem[1]); if (isa_mem[0] == 0xff && isa_mem[1] == 0xff) printf("unused addr."); else if (here >= 0xa0000 && here < 0xaffff) printf("SVGA RAM"); else if (here >= 0xb0000 && here < 0xbffff) printf("video RAM"); else if (here >= 0xc0000 && here < 0xc7fff) printf("SVGA ROM"); else if (here >= 0xf0000 && here < 0xfffff) printf("BIOS ROM"); else if (isa_mem[0] == 0x55 && isa_mem[1] == 0xaa) printf("misc. ROM"); else printf("unknown dev."); printf(")"); count = done = 0; signal(SIGALRM, alarm_intr); alarm(WAITTIME); while (!done) { memcpy(buf, isa_mem, ISA_WINSIZE); count++; } munmap((void*)here, ISA_WINSIZE); printf(":\t%lu copies (%.2f MiB/s)\n", count, (float)count*ISA_WINSIZE/(1024*1024)); } void memspeed(void) { unsigned char *buf1, *buf2; unsigned long count; buf1 = malloc(ISA_WINSIZE); buf2 = malloc(ISA_WINSIZE); if (buf1 == NULL || buf2 == NULL) exit(ENOMEM); buf1[ISA_WINSIZE/2] = 0x12; /* dirty the pages */ buf2[ISA_WINSIZE/2] = 0x34; count = done = 0; signal(SIGALRM, alarm_intr); alarm(WAITTIME); while (!done) { memcpy(buf2, buf1, ISA_WINSIZE); memcpy(buf1, buf2, ISA_WINSIZE); count+=2; } printf("Cached memcpy on this machine is approx. %lu MiB/s.\n\n", count*ISA_WINSIZE/(1024*1024)); }