summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorStuart Bennett <sb476@cam.ac.uk>2007-11-23 03:10:28 +0000
committerStuart Bennett <sb476@cam.ac.uk>2007-11-23 03:10:28 +0000
commit3fbfcd93167e2148d949fab4cdcc14c862d47965 (patch)
treee4585d5c2f9f34766a8b09442393cc51a3d61d69
parent55ea919c1d178d940cf9da8861e08faf9765d90b (diff)
Initial support reading the BIOS from PROM on Nvidia cards
-rw-r--r--README3
-rw-r--r--makefile1
-rw-r--r--nvbiosemu.c170
-rw-r--r--thunk.c10
-rw-r--r--vbtracetool.c17
5 files changed, 193 insertions, 8 deletions
diff --git a/README b/README
index 57cd651..e6f07e9 100644
--- a/README
+++ b/README
@@ -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.
diff --git a/makefile b/makefile
index 9f8dce3..683b503 100644
--- a/makefile
+++ b/makefile
@@ -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;
+}
diff --git a/thunk.c b/thunk.c
index 72c0782..e9f4fb8 100644
--- a/thunk.c
+++ b/thunk.c
@@ -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());