/* NAME pflip - test hardware page flipping SYNOPSIS pflip DESCRIPTION pflip is used to test hardware page flipping on graphic cards supporting standard VGA registers while running an X Server. 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. AUTHOR Frédéric Lopez 21 May 2012 */ #include #include #include #include #include #include // Define the "address" and "data" registers for VGA CRTC access #define VGA_CRTC_ADDR_REG 0x3D4 #define VGA_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(volatile uint8_t *mem, int offset) { // Set the VGA CRTC Start Address Low Register mem[VGA_CRTC_ADDR_REG] = 0x0D; mem[VGA_CRTC_DATA_REG] = offset & 0xFF; // Set the VGA CRTC Start Address High Register mem[VGA_CRTC_ADDR_REG] = 0x0C; mem[VGA_CRTC_DATA_REG] = offset >> 8; } 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; void *map; uint64_t res; char buf[32]; volatile uint8_t *m; int resfd; // Test the number of command-line arguments if (argc != 2) { printf("Usage: pflip \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); } // 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 NVIDIA VGA CRTC registers m = map = mmap(0, 0x2000, PROT_READ|PROT_WRITE, MAP_SHARED, fd, res + 0x601000); if (map == MAP_FAILED) { perror("FAIL mmap :("); return -1; } // Main loop while ((key = getchar()) != 'q') { switch (key) { case '1' : set_vga_start(m, 0); printf("Page 1.\n"); break; case '2' : set_vga_start(m, offset); printf("Page 2.\n"); break; } } printf("Done.\n"); return EXIT_SUCCESS; }