diff options
author | Stuart Bennett <sb476@cam.ac.uk> | 2007-11-23 03:10:28 +0000 |
---|---|---|
committer | Stuart Bennett <sb476@cam.ac.uk> | 2007-11-23 03:10:28 +0000 |
commit | 3fbfcd93167e2148d949fab4cdcc14c862d47965 (patch) | |
tree | e4585d5c2f9f34766a8b09442393cc51a3d61d69 | |
parent | 55ea919c1d178d940cf9da8861e08faf9765d90b (diff) |
Initial support reading the BIOS from PROM on Nvidia cards
-rw-r--r-- | README | 3 | ||||
-rw-r--r-- | makefile | 1 | ||||
-rw-r--r-- | nvbiosemu.c | 170 | ||||
-rw-r--r-- | thunk.c | 10 | ||||
-rw-r--r-- | vbtracetool.c | 17 |
5 files changed, 193 insertions, 8 deletions
@@ -8,9 +8,10 @@ Building: Run make Running: -./vbtracetool [-d] [-g | -p | -s MODENUMBER] +./vbtracetool [-d] [-n] [-g | -p | -s MODENUMBER] -d gives trace output *to stderr*. you'll want to redirect that. +-n attempts to read and execute the bios from an Nvidia card's PROM region -g gets the current display mode number -p posts the card. could induce several seconds of pants cacking with -d. @@ -44,6 +44,7 @@ prim_ops.o \ sys.o VBTTOBJS=\ +nvbiosemu.o \ vbtracetool.o \ thunk.o \ x86-common.o diff --git a/nvbiosemu.c b/nvbiosemu.c new file mode 100644 index 0000000..3a201e2 --- /dev/null +++ b/nvbiosemu.c @@ -0,0 +1,170 @@ +/* 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 <fcntl.h> +#include <stdbool.h> +#include <stdint.h> +#include <stdio.h> +#include <string.h> +#include <sys/mman.h> +#include <sys/types.h> +#include <unistd.h> + +#include "x86emu.h" + +#define Bool bool +#define TRUE true +#define FALSE false +#define ScrnInfoPtr void * +#define NV_PROM_SIZE 0x10000 + +static uint32_t *PMC; +static uint32_t *PRAMIN; +static uint8_t *PROM; + +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 offset, 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[offset + 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, 0, 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\n"); + + /* 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 Bool NVShadowVBIOS(ScrnInfoPtr pScrn, uint8_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 fake_bios(int fd, uint8_t *bios) +{ + uint8_t *emumem, *sysmem; + int i; + + /* this does not get free'd until exit */ + if (!(emumem = malloc(1024 * 1024))) { + printf("Couldn't allocate fake system memory\n"); + return 1; + } + + sysmem = map_dev_mem(fd, 0, 1024*1024); + + for (i = 0; i < 1024*1024; i++) + emumem[i] = sysmem[i]; + for (i = 0; i < NV_PROM_SIZE; i++) + emumem[0xc0000 + i] = bios[i]; + + unmap_dev_mem((unsigned long)sysmem, 1024*1024); + + M.mem_base = (int)emumem; + + return 0; +} + +int reload_nv_bios() +{ + int fd; + uint8_t bios[NV_PROM_SIZE]; + + if( (fd = open("/dev/mem", O_RDWR)) == -1 ) { + printf("Can't open /dev/mem\n"); + return 0; + } + + unsigned int reg_address = 0xe5000000; // FIXME + PMC = map_dev_mem(fd, reg_address + 0x000000, NV_PROM_SIZE); + PRAMIN = map_dev_mem(fd, reg_address + 0x700000, NV_PROM_SIZE); + PROM = map_dev_mem(fd, reg_address + 0x300000, NV_PROM_SIZE); + + + if (NVShadowVBIOS(NULL, bios)) { + if (fake_bios(fd, bios)) + return 1; + } else + return 1; + + close(fd); + + unmap_dev_mem((unsigned long)PMC, NV_PROM_SIZE); + unmap_dev_mem((unsigned long)PRAMIN, NV_PROM_SIZE); + unmap_dev_mem((unsigned long)PROM, NV_PROM_SIZE); + + return 0; +} @@ -161,20 +161,20 @@ int LRMI_init() { X86_EFLAGS = X86_IF_MASK | X86_IOPL_MASK; + M.mem_base = 0; + M.mem_size = 1024*1024; + /* * Allocate a 64k stack. */ stack = LRMI_alloc_real(64 * 1024); X86_SS = (u32) stack >> 4; X86_ESP = 0xFFF9; - memset (stack, 0, 64*1024); + memset (M.mem_base + stack, 0, 64*1024); *((char *)0) = 0x4f; /* Make sure that we end up jumping back to a halt instruction */ - M.mem_base = 0; - M.mem_size = 1024*1024; - return 1; } @@ -210,7 +210,7 @@ int real_call(struct LRMI_regs *registers) { X86_ESP = 0xFFF9; } - memset (stack, 0, 64*1024); + memset (M.mem_base + stack, 0, 64*1024); X86EMU_exec(); diff --git a/vbtracetool.c b/vbtracetool.c index e93cb85..1c93639 100644 --- a/vbtracetool.c +++ b/vbtracetool.c @@ -19,6 +19,8 @@ #include "lrmi.h" #include "x86emu.h" +extern int reload_nv_bios(); + int do_set_mode(unsigned mode) { struct LRMI_regs r; @@ -91,9 +93,9 @@ int main(int argc, char *argv[]) struct pci_dev *p; unsigned int c; unsigned int pci_id; - int opt, debug = 0, mode, op = 0, opset = 0; + int opt, debug = 0, mode, op = 0, opset = 0, nvreload = 0; - while ((opt = getopt(argc, argv, "dgps:")) != -1) { + while ((opt = getopt(argc, argv, "dgnps:")) != -1) { switch (opt) { case 'd': debug = 1; @@ -101,6 +103,9 @@ int main(int argc, char *argv[]) case 'g': opset++; break; + case 'n': + nvreload = 1; + break; case 'p': op = 2; opset++; @@ -133,6 +138,14 @@ int main(int argc, char *argv[]) else M.x86.debug = 0; + if (nvreload) + if (reload_nv_bios()) + exit(EXIT_FAILURE); + + /* brace yourselves */ + sync(); + sync(); + switch (op) { case 0: return (do_get_mode()); |