/* NAME pflip-vga - test hardware page flipping SYNOPSIS pflip-vga DESCRIPTION pflip-vga is used to test hardware page flipping on graphic cards supporting legacy standard VGA registers while running an X Server. If the legacy VGA registers are not available they are enabled by writing to the PCI configuration space. To obtain meaningful results, a virtual screen must be active with an horizontal resolution set to the double of the viewport horizontal resolution. For example, a 1024x768 viewport with a 2048x768 virtual screen. The application is controlled using these interactive commands: q quit the application 1 set the CRTC Start Address Register to 0 2 set the CRTC Start Address Register to OPTIONS Set the offset value as a number of pixel columns that should correspond to the width of the current viewport. For example 1920 for a 3840x1080 virtual screen. If you don't want to set up a virtual screen you can also specify a lower value than your current horizontal width, ie. 512 for a 1024x768 screen. AUTHOR Frédéric Lopez 26 May 2012 */ #include #include #include #include #include #include #include // Define the Address Register for CRTC access #define CRTC_ADDR_REG 0x3D4 // Define the Data Register for CRTC access #define CRTC_DATA_REG 0x3D5 #define PCI_GET_BUS(devbusfn) ((devbusfn >> 8) & 0xff) #define PCI_GET_DEVICE(devbusfn) ((devbusfn & 0xff) >> 3) #define PCI_GET_FUNCTION(devbusfn) (devbusfn & 0x7) // Set the CRTC Start Adress Register static void set_vga_start(int offset) { // Set the CRTC Start Address Low Register outb(0x0D, CRTC_ADDR_REG); outb(offset & 0xFF, CRTC_DATA_REG); // Set the CRTC Start Address High Register outb(0x0C, CRTC_ADDR_REG); outb(offset >> 8, CRTC_DATA_REG); } int32_t pciReadLong(unsigned short devbusfn, long offset) { char file[25]; FILE *device; short bus = PCI_GET_BUS(devbusfn); short dev = PCI_GET_DEVICE(devbusfn); short function = PCI_GET_FUNCTION(devbusfn); snprintf(file, sizeof(file), "/proc/bus/pci/%02x/%02x.%x", bus, dev, function); if ((device = fopen(file, "r")) != NULL) { int32_t buffer; fseek(device, offset, SEEK_SET); fread(&buffer, sizeof(int32_t), 1, device); fclose(device); return buffer; } return -1; } /* Check if the device is a videocard */ int is_video_card(unsigned short devbusfn) { int32_t pci_class = pciReadLong(devbusfn, 0x9); /* The device is a VGA card */ if (((htonl(pci_class) >> 8) & 0xf) == 0x03) return 1; else return 0; } /* Get the register offset */ int get_reg_addr() { int dev, dum, reg_addr, i = 0; unsigned short devbusfn; char buf[256]; FILE *proc; proc = fopen("/proc/bus/pci/devices", "r"); if (!proc) { perror("Can't open /proc/bus/pci/devices !"); return -1; } while (fgets(buf, sizeof(buf) - 1, proc)) { if (sscanf(buf,"%hx %x %x %x",&devbusfn, &dev, &dum, ®_addr) != 4) continue; /* The card contains an Nvidia chipset */ if ((dev >> 16) == 0x10de) { if (!is_video_card(devbusfn)) continue; i++; } } fclose(proc); if (i == 0) return -1; else return reg_addr; } int main(int argc, char *argv[]) { int key, offset; char *p; int fd; uint64_t res; volatile uint16_t *ppci; // Test the number of command-line arguments if (argc != 2) { printf("Usage: pageflip \n"); exit(EXIT_FAILURE); } // Test if the argument is an integer errno = 0; offset = strtol(argv[1], &p, 10); if (errno != 0 || *p != 0 || p == argv[1]) { printf("Error: offset value must be an integer!\n"); exit(EXIT_FAILURE); } // Change the I/O privilege level to allow access to VGA registers if (iopl(3) != 0) { printf("Warning: unable to modify the I/O privilege level!\n"); } // Show interactive commands help printf("Quit with 'q'.\n"); printf("Page 1 with '1'.\n"); printf("Page 2 with '2'.\n"); // Find video card register offset res = get_reg_addr(); if (res == -1) { printf("Error: no NVIDIA video card found!\n"); exit(EXIT_FAILURE); } // Open /dev/mem to map NVIDIA registers if ((fd = open("/dev/mem", O_RDWR)) < 0) { perror("Fail open :("); return -1; } // Map PCI configuration space (NV40-NVC0) ppci = mmap(0, 0x1000, PROT_READ|PROT_WRITE, MAP_SHARED, fd, res + 0x088000); if (ppci == MAP_FAILED) { perror("FAIL mmap PPCI (PCI config space access) :("); return -1; } printf("VGA legacy IO/memory ranges decoding enable : %x\n", ppci[0x054/2]); ppci[0x054/2] = 0; printf("VGA legacy IO/memory ranges decoding enable : %x\n", ppci[0x054/2]); ppci[0x054/2] = 1; printf("VGA legacy IO/memory ranges decoding enable : %x\n", ppci[0x054/2]); while ((key = getchar()) != 'q') { switch (key) { case '1' : // Set Start Address Register to 0 set_vga_start(0); printf("Page 1.\n"); break; case '2' : // Set Start Address Register to set_vga_start(offset); printf("Page 2.\n"); break; } } // Restore the default I/O privilege level if (iopl(0) != 0) { printf("Warning: unable to restore the default I/O privilege level!\n"); } printf("Done.\n"); return EXIT_SUCCESS; }