/* reloads an nvidia card's 0xc0000 bios with a valid copy for execution * (all done in a copy of system memory, no bios overwriting involved) * * portions of code below taken from Nouveau's nv_bios.c and nvclock * and are copyright their respective authors */ #include #include #include #include #include #include #include #include #include "x86emu.h" #define Bool bool #define TRUE true #define FALSE false #define ScrnInfoPtr void * #define VBIOS_ROM 0xc0000 #define NV_PROM_SIZE 0x10000 static uint32_t *PMC; static uint32_t *PRAMIN; static uint8_t *PROM; static uint32_t *PNV50; static void *map_dev_mem (int fd, unsigned long Base, unsigned long Size) { void *base; int mapflags = MAP_SHARED; unsigned long realBase, alignOff; realBase = Base & ~(getpagesize() - 1); alignOff = Base - realBase; base = mmap((caddr_t)0, Size + alignOff, PROT_READ|PROT_WRITE, mapflags, fd, (off_t)realBase); return (void *) ((char *)base + alignOff); } static void unmap_dev_mem (unsigned long Base, unsigned long Size) { unsigned long alignOff = Base - (Base & ~(getpagesize() - 1)); munmap((caddr_t)(Base - alignOff), (Size + alignOff)); } static Bool nv_cksum(const uint8_t *data, unsigned int length) { /* there's a few checksums in the BIOS, so here's a generic checking function */ int i; uint8_t sum = 0; for (i = 0; i < length; i++) sum += data[i]; if (sum) return TRUE; return FALSE; } static Bool NVValidVBIOS(ScrnInfoPtr pScrn, const uint8_t *data) { /* check for BIOS signature */ if (!(data[0] == 0x55 && data[1] == 0xAA)) { printf("... BIOS signature not found\n"); return FALSE; } if (nv_cksum(data, data[2] * 512)) { printf("... BIOS checksum invalid\n"); return FALSE; } else printf( "... appears to be valid\n"); return TRUE; } static void NVShadowVBIOS_PROM(ScrnInfoPtr pScrn, uint8_t *data) { int i; printf("Attempting to locate BIOS image in PROM"); /* enable ROM access */ PMC[0x1850 / 4] = 0x0; for (i = 0; i < NV_PROM_SIZE; i++) { /* according to nvclock, we need that to work around a 6600GT/6800LE bug */ data[i] = PROM[i]; data[i] = PROM[i]; data[i] = PROM[i]; data[i] = PROM[i]; data[i] = PROM[i]; } /* disable ROM access */ PMC[0x1850 / 4] = 0x1; } static void NVShadowVBIOS_PRAMIN(ScrnInfoPtr pScrn, uint32_t *data) { const uint32_t *pramin = PRAMIN; int arch = PMC[0] >> 20 & 0x1ff; uint32_t old_bar0_pramin = 0; printf("Attempting to locate BIOS image in PRAMIN"); if (arch > 0x50) { uint32_t vbios_vram; vbios_vram = (PNV50[0x9f04/4] & ~0xff) << 8; if (!vbios_vram) { vbios_vram = PMC[0x1700/4] << 16; vbios_vram += 0xf0000; } old_bar0_pramin = PMC[0x1700/4]; PMC[0x1700/4] = vbios_vram >> 16; } memcpy(data, pramin, NV_PROM_SIZE); if (arch > 0x50) PMC[0x1700/4] = old_bar0_pramin; } static Bool NVShadowVBIOS(ScrnInfoPtr pScrn, uint32_t *data) { NVShadowVBIOS_PROM(pScrn, (uint8_t *)data); if (NVValidVBIOS(pScrn, (uint8_t *)data)) return TRUE; NVShadowVBIOS_PRAMIN(pScrn, data); if (NVValidVBIOS(pScrn, (uint8_t *)data)) return TRUE; return FALSE; } int mmap_in_bios(uint8_t *bios) { int zfd, i; if ((zfd = open("/dev/zero", O_RDWR)) == -1) { printf("Can't open /dev/zero\n"); return 1; } mmap((void *)VBIOS_ROM, NV_PROM_SIZE, PROT_READ | PROT_WRITE | PROT_EXEC, MAP_FIXED | MAP_SHARED, zfd, 0); close(zfd); for (i = 0; i < NV_PROM_SIZE; i++) *((uint8_t *)VBIOS_ROM + i) = bios[i]; return 0; } int reload_nv_bios(uintptr_t pcimemaddr) { int fd; uint8_t bios[NV_PROM_SIZE]; int ret = 0; if ((fd = open("/dev/mem", O_RDWR)) == -1) { printf("Can't open /dev/mem\n"); return 1; } printf("Using card memory region at %p\n", (void *)pcimemaddr); PMC = map_dev_mem(fd, pcimemaddr + 0x000000, 0x2000); PRAMIN = map_dev_mem(fd, pcimemaddr + 0x700000, NV_PROM_SIZE); PROM = map_dev_mem(fd, pcimemaddr + 0x300000, NV_PROM_SIZE); PNV50 = map_dev_mem(fd, pcimemaddr + 0x610000, 0xffff); close(fd); if (NVShadowVBIOS(NULL, (uint32_t *)bios)) { if (mmap_in_bios(bios)) ret = 1; } else ret = 1; unmap_dev_mem((unsigned long)PMC, 0x2000); unmap_dev_mem((unsigned long)PRAMIN, NV_PROM_SIZE); unmap_dev_mem((unsigned long)PROM, NV_PROM_SIZE); unmap_dev_mem((unsigned long)PNV50, 0xffff); return ret; } int reload_bios_from_file(char *romfile) { uint8_t bios[NV_PROM_SIZE]; FILE *romf; if (!(romf = fopen(romfile, "r"))) { printf("File open failed\n"); return 1; } fread(bios, NV_PROM_SIZE, 1, romf); fclose(romf); if (!NVValidVBIOS(NULL, bios)) return 1; printf("Loading contents of %s as bios image to be used, unless ^C is hit in next 5s\n", romfile); sleep(5); if (mmap_in_bios(bios)) return 1; return 0; }