forked from Imagelibrary/rtems
Merged of mcp750 and mvme2307 BSP by Eric Valette <valette@crf.canon.fr>.
As part of this effort, the mpc750 libcpu code is now shared with the ppc6xx.
This commit is contained in:
580
c/src/lib/libbsp/powerpc/shared/bootloader/em86.c
Normal file
580
c/src/lib/libbsp/powerpc/shared/bootloader/em86.c
Normal file
@@ -0,0 +1,580 @@
|
||||
/*
|
||||
* em86.c -- Include file for bootloader.
|
||||
*
|
||||
* Copyright (C) 1998, 1999 Gabriel Paubert, paubert@iram.es
|
||||
*
|
||||
* Modified to compile in RTEMS development environment
|
||||
* by Eric Valette
|
||||
*
|
||||
* Copyright (C) 1999 Eric Valette. valette@crf.canon.fr
|
||||
*
|
||||
* The license and distribution terms for this file may be
|
||||
* found in found in the file LICENSE in this distribution or at
|
||||
* http://www.OARcorp.com/rtems/license.html.
|
||||
*
|
||||
* $Id$
|
||||
*/
|
||||
|
||||
/*****************************************************************************
|
||||
*
|
||||
* Code to interpret Video BIOS ROM routines.
|
||||
*
|
||||
*
|
||||
******************************************************************************/
|
||||
|
||||
/* These include are for the development version only */
|
||||
#include <sys/types.h>
|
||||
#include "pci.h"
|
||||
#include <libcpu/byteorder.h>
|
||||
#ifdef __BOOT__
|
||||
#include "bootldr.h"
|
||||
#include <limits.h>
|
||||
#endif
|
||||
|
||||
/* Code options, put them on the compiler command line */
|
||||
/* #define EIP_STATS */ /* EIP based profiling */
|
||||
/* #undef EIP_STATS */
|
||||
|
||||
typedef union _reg_type1 {
|
||||
unsigned e;
|
||||
unsigned short x;
|
||||
struct {
|
||||
unsigned char l, h;
|
||||
} lh;
|
||||
} reg_type1;
|
||||
|
||||
typedef union _reg_type2 {
|
||||
unsigned e;
|
||||
unsigned short x;
|
||||
} reg_type2;
|
||||
|
||||
typedef struct _x86 {
|
||||
reg_type1
|
||||
_eax, _ecx, _edx, _ebx;
|
||||
reg_type2
|
||||
_esp, _ebp, _esi, _edi;
|
||||
unsigned
|
||||
es, cs, ss, ds, fs, gs, eip, eflags;
|
||||
unsigned char
|
||||
*esbase, *csbase, *ssbase, *dsbase, *fsbase, *gsbase;
|
||||
volatile unsigned char *iobase;
|
||||
unsigned char *ioperm;
|
||||
unsigned
|
||||
reason, nexteip, parm1, parm2, opcode, base;
|
||||
unsigned *optable, opreg; /* no more used! */
|
||||
unsigned char* vbase;
|
||||
unsigned instructions;
|
||||
#ifdef __BOOT__
|
||||
u_char * ram;
|
||||
u_char * rom;
|
||||
struct pci_dev * dev;
|
||||
#else
|
||||
unsigned filler[14]; /* Skip to next 64 byte boundary */
|
||||
unsigned eipstats[32768][2];
|
||||
#endif
|
||||
} x86;
|
||||
|
||||
x86 v86_private __attribute__((aligned(32)));
|
||||
|
||||
|
||||
/* Emulator is in another source file */
|
||||
extern
|
||||
void em86_enter(x86 * p);
|
||||
|
||||
#define EAX (p->_eax.e)
|
||||
#define ECX (p->_ecx.e)
|
||||
#define EDX (p->_edx.e)
|
||||
#define EBX (p->_ebx.e)
|
||||
#define ESP (p->_esp.e)
|
||||
#define EBP (p->_ebp.e)
|
||||
#define ESI (p->_esi.e)
|
||||
#define EDI (p->_edi.e)
|
||||
#define AX (p->_eax.x)
|
||||
#define CX (p->_ecx.x)
|
||||
#define DX (p->_edx.x)
|
||||
#define BX (p->_ebx.x)
|
||||
#define SP (p->_esp.x)
|
||||
#define BP (p->_ebp.x)
|
||||
#define SI (p->_esi.x)
|
||||
#define DI (p->_edi.x)
|
||||
#define AL (p->_eax.lh.l)
|
||||
#define CL (p->_ecx.lh.l)
|
||||
#define DL (p->_edx.lh.l)
|
||||
#define BL (p->_ebx.lh.l)
|
||||
#define AH (p->_eax.lh.h)
|
||||
#define CH (p->_ecx.lh.h)
|
||||
#define DH (p->_edx.lh.h)
|
||||
#define BH (p->_ebx.lh.h)
|
||||
|
||||
/* Function used to debug */
|
||||
#ifdef __BOOT__
|
||||
#define printf printk
|
||||
#endif
|
||||
#ifdef DEBUG
|
||||
static void dump86(x86 * p){
|
||||
unsigned char *s = p->csbase + p->eip;
|
||||
printf("cs:eip=%04x:%08x, eax=%08x, ecx=%08x, edx=%08x, ebx=%08x\n",
|
||||
p->cs, p->eip, ld_le32(&EAX),
|
||||
ld_le32(&ECX), ld_le32(&EDX), ld_le32(&EBX));
|
||||
printf("ss:esp=%04x:%08x, ebp=%08x, esi=%08x, edi=%08x, efl=%08x\n",
|
||||
p->ss, ld_le32(&ESP), ld_le32(&EBP),
|
||||
ld_le32(&ESI), ld_le32(&EDI), p->eflags);
|
||||
printf("nip=%08x, ds=%04x, es=%04x, fs=%04x, gs=%04x, total=%d\n",
|
||||
p->nexteip, p->ds, p->es, p->fs, p->gs, p->instructions);
|
||||
printf("code: %02x %02x %02x %02x %02x %02x "
|
||||
"%02x %02x %02x %02x %02x %02x\n",
|
||||
s[0], s[1], s[2], s[3], s[4], s[5],
|
||||
s[6], s[7], s[8], s[9], s[10], s[11]);
|
||||
#ifndef __BOOT__
|
||||
printf("op1=%08x, op2=%08x, result=%08x, flags=%08x\n",
|
||||
p->filler[11], p->filler[12], p->filler[13], p->filler[14]);
|
||||
#endif
|
||||
}
|
||||
#else
|
||||
#define dump86(x)
|
||||
#endif
|
||||
|
||||
int bios86pci(x86 * p) {
|
||||
unsigned reg=ld_le16(&DI);
|
||||
reg_type2 tmp;
|
||||
|
||||
if (AL>=8 && AL<=13 && reg>0xff) {
|
||||
AH = PCIBIOS_BAD_REGISTER_NUMBER;
|
||||
} else {
|
||||
switch(AL) {
|
||||
case 2: /* find_device */
|
||||
/* Should be improved for BIOS able to handle
|
||||
* multiple devices. We simply suppose the BIOS
|
||||
* inits a single device, and return an error
|
||||
* if it tries to find more...
|
||||
*/
|
||||
if (SI) {
|
||||
AH=PCIBIOS_DEVICE_NOT_FOUND;
|
||||
} else {
|
||||
BH = p->dev->bus->number;
|
||||
BL = p->dev->devfn;
|
||||
AH = 0;
|
||||
}
|
||||
break;
|
||||
/*
|
||||
case 3: find_class not implemented for now.
|
||||
*/
|
||||
case 8: /* read_config_byte */
|
||||
AH=pcibios_read_config_byte(BH, BL, reg, &CL);
|
||||
break;
|
||||
case 9: /* read_config_word */
|
||||
AH=pcibios_read_config_word(BH, BL, reg, &tmp.x);
|
||||
CX=ld_le16(&tmp.x);
|
||||
break;
|
||||
case 10: /* read_config_dword */
|
||||
AH=pcibios_read_config_dword(BH, BL, reg, &tmp.e);
|
||||
ECX=ld_le32(&tmp.e);
|
||||
break;
|
||||
case 11: /* write_config_byte */
|
||||
AH=pcibios_write_config_byte(BH, BL, reg, CL);
|
||||
break;
|
||||
case 12: /* write_config_word */
|
||||
AH=pcibios_write_config_word(BH, BL, reg, ld_le16(&CX));
|
||||
break;
|
||||
case 13: /* write_config_dword */
|
||||
AH=pcibios_write_config_dword(BH, BL, reg, ld_le32(&ECX));
|
||||
break;
|
||||
default:
|
||||
printf("Unimplemented or illegal PCI service call #%d!\n",
|
||||
AL);
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
p->eip = p->nexteip;
|
||||
/* Set/clear carry according to result */
|
||||
if (AH) p->eflags |= 1; else p->eflags &=~1;
|
||||
return 0;
|
||||
}
|
||||
|
||||
void push2(x86 *p, unsigned value) {
|
||||
unsigned char * sbase= p->ssbase;
|
||||
unsigned newsp = (ld_le16(&SP)-2)&0xffff;
|
||||
st_le16(&SP,newsp);
|
||||
st_le16((unsigned short *)(sbase+newsp), value);
|
||||
}
|
||||
|
||||
unsigned pop2(x86 *p) {
|
||||
unsigned char * sbase=p->ssbase;
|
||||
unsigned oldsp = ld_le16(&SP);
|
||||
st_le16(&SP,oldsp+2);
|
||||
return ld_le16((unsigned short *)(sbase+oldsp));
|
||||
}
|
||||
|
||||
int int10h(x86 * p) { /* Process BIOS video interrupt */
|
||||
unsigned vector;
|
||||
vector=ld_le32((unsigned *)p->vbase+0x10);
|
||||
if (((vector&0xffff0000)>>16)==0xc000) {
|
||||
push2(p, p->eflags);
|
||||
push2(p, p->cs);
|
||||
push2(p, p->nexteip);
|
||||
p->cs=vector>>16;
|
||||
p->csbase=p->vbase + (p->cs<<4);
|
||||
p->eip=vector&0xffff;
|
||||
#if 1
|
||||
p->eflags&=0xfcff; /* Clear AC/TF/IF */
|
||||
#else
|
||||
p->eflags = (p->eflags&0xfcff)|0x100; /* Set TF for debugging */
|
||||
#endif
|
||||
/* p->eflags|=0x100; uncomment to force a trap */
|
||||
return(0);
|
||||
} else {
|
||||
switch(AH) {
|
||||
case 0x12:
|
||||
switch(BL){
|
||||
case 0x32:
|
||||
p->eip=p->nexteip;
|
||||
return(0);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
default:
|
||||
break;
|
||||
}
|
||||
printf("unhandled soft interrupt 0x10: vector=%x\n", vector);
|
||||
return(1);
|
||||
}
|
||||
}
|
||||
|
||||
int process_softint(x86 * p) {
|
||||
#if 0
|
||||
if (p->parm1!=0x10 || AH!=0x0e) {
|
||||
printf("Soft interrupt\n");
|
||||
dump86(p);
|
||||
}
|
||||
#endif
|
||||
switch(p->parm1) {
|
||||
case 0x10: /* BIOS video interrupt */
|
||||
return int10h(p);
|
||||
case 0x1a:
|
||||
if(AH==0xb1) return bios86pci(p);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
dump86(p);
|
||||
printf("Unhandled soft interrupt number 0x%04x, AX=0x%04x\n",
|
||||
p->parm1, ld_le16(&AX));
|
||||
return(1);
|
||||
}
|
||||
|
||||
/* The only function called back by the emulator is em86_trap, all
|
||||
instructions may that change the code segment are trapped here.
|
||||
p->reason is one of the following codes. */
|
||||
#define code_zerdiv 0
|
||||
#define code_trap 1
|
||||
#define code_int3 3
|
||||
#define code_into 4
|
||||
#define code_bound 5
|
||||
#define code_ud 6
|
||||
#define code_dna 7
|
||||
|
||||
#define code_iretw 256
|
||||
#define code_iretl 257
|
||||
#define code_lcallw 258
|
||||
#define code_lcalll 259
|
||||
#define code_ljmpw 260
|
||||
#define code_ljmpl 261
|
||||
#define code_lretw 262
|
||||
#define code_lretl 263
|
||||
#define code_softint 264
|
||||
#define code_lock 265 /* Lock prefix */
|
||||
/* Codes 1024 to 2047 are used for I/O port access instructions:
|
||||
- The three LSB define the port size (1, 2 or 4)
|
||||
- bit of weight 512 means out if set, in if clear
|
||||
- bit of weight 256 means ins/outs if set, in/out if clear
|
||||
- bit of weight 128 means use esi/edi if set, si/di if clear
|
||||
(only used for ins/outs instructions, always clear for in/out)
|
||||
*/
|
||||
#define code_inb 1024+1
|
||||
#define code_inw 1024+2
|
||||
#define code_inl 1024+4
|
||||
#define code_outb 1024+512+1
|
||||
#define code_outw 1024+512+2
|
||||
#define code_outl 1024+512+4
|
||||
#define code_insb_a16 1024+256+1
|
||||
#define code_insw_a16 1024+256+2
|
||||
#define code_insl_a16 1024+256+4
|
||||
#define code_outsb_a16 1024+512+256+1
|
||||
#define code_outsw_a16 1024+512+256+2
|
||||
#define code_outsl_a16 1024+512+256+4
|
||||
#define code_insb_a32 1024+256+128+1
|
||||
#define code_insw_a32 1024+256+128+2
|
||||
#define code_insl_a32 1024+256+128+4
|
||||
#define code_outsb_a32 1024+512+256+128+1
|
||||
#define code_outsw_a32 1024+512+256+128+2
|
||||
#define code_outsl_a32 1024+512+256+128+4
|
||||
|
||||
int em86_trap(x86 *p) {
|
||||
#ifndef __BOOT__
|
||||
int i;
|
||||
unsigned char command[80];
|
||||
unsigned char *verb, *t;
|
||||
unsigned short *fp;
|
||||
static unsigned char def=0;
|
||||
static unsigned char * bptaddr=NULL; /* Breakpoint address */
|
||||
static unsigned char bptopc; /* Replaced breakpoint opcode */
|
||||
unsigned char cmd;
|
||||
unsigned tmp;
|
||||
#endif
|
||||
switch(p->reason) {
|
||||
case code_int3:
|
||||
#ifndef __BOOT__
|
||||
if(p->csbase+p->eip == bptaddr) {
|
||||
*bptaddr=bptopc;
|
||||
bptaddr=NULL;
|
||||
}
|
||||
else printf("Unexpected ");
|
||||
#endif
|
||||
printf("Breakpoint Interrupt !\n");
|
||||
/* Note that this fallthrough (no break;) is on purpose */
|
||||
#ifdef __BOOT__
|
||||
return 0;
|
||||
#else
|
||||
case code_trap:
|
||||
dump86(p);
|
||||
for(;;) {
|
||||
printf("b(reakpoint, g(o, q(uit, s(tack, t(race ? [%c] ", def);
|
||||
fgets(command,sizeof(command),stdin);
|
||||
verb = strtok(command," \n");
|
||||
if(verb) cmd=*verb; else cmd=def;
|
||||
def=0;
|
||||
switch(cmd) {
|
||||
case 'b':
|
||||
case 'B':
|
||||
if(bptaddr) *bptaddr=bptopc;
|
||||
t=strtok(0," \n");
|
||||
i=sscanf(t,"%x",&tmp);
|
||||
if(i==1) {
|
||||
bptaddr=p->vbase + tmp;
|
||||
bptopc=*bptaddr;
|
||||
*bptaddr=0xcc;
|
||||
} else bptaddr=NULL;
|
||||
break;
|
||||
case 'q':
|
||||
case 'Q':
|
||||
return 1;
|
||||
break;
|
||||
|
||||
case 'g':
|
||||
case 'G':
|
||||
p->eflags &= ~0x100;
|
||||
return 0;
|
||||
break;
|
||||
|
||||
case 's':
|
||||
case 'S': /* Print the 8 stack top words */
|
||||
fp = (unsigned short *)(p->ssbase+ld_le16(&SP));
|
||||
printf("Stack [%04x:%04x]: %04x %04x %04x %04x %04x %04x %04x %04x\n",
|
||||
p->ss, ld_le16(&SP),
|
||||
ld_le16(fp+0), ld_le16(fp+1), ld_le16(fp+2), ld_le16(fp+3),
|
||||
ld_le16(fp+4), ld_le16(fp+5), ld_le16(fp+6), ld_le16(fp+7));
|
||||
break;
|
||||
case 't':
|
||||
case 'T':
|
||||
p->eflags |= 0x10100; /* Set the resume and trap flags */
|
||||
def='t';
|
||||
return 0;
|
||||
break;
|
||||
/* Should add some code to edit registers */
|
||||
}
|
||||
}
|
||||
#endif
|
||||
break;
|
||||
case code_ud:
|
||||
printf("Attempt to execute an unimplemented"
|
||||
"or undefined opcode!\n");
|
||||
dump86(p);
|
||||
return(1); /* exit interpreter */
|
||||
break;
|
||||
case code_dna:
|
||||
printf("Attempt to execute a floating point instruction!\n");
|
||||
dump86(p);
|
||||
return(1);
|
||||
break;
|
||||
case code_softint:
|
||||
return process_softint(p);
|
||||
break;
|
||||
case code_iretw:
|
||||
p->eip=pop2(p);
|
||||
p->cs=pop2(p);
|
||||
p->csbase=p->vbase + (p->cs<<4);
|
||||
p->eflags= (p->eflags&0xfffe0000)|pop2(p);
|
||||
/* p->eflags|= 0x100; */ /* Uncomment to trap after iretws */
|
||||
return(0);
|
||||
break;
|
||||
#ifndef __BOOT__
|
||||
case code_inb:
|
||||
case code_inw:
|
||||
case code_inl:
|
||||
case code_insb_a16:
|
||||
case code_insw_a16:
|
||||
case code_insl_a16:
|
||||
case code_insb_a32:
|
||||
case code_insw_a32:
|
||||
case code_insl_a32:
|
||||
case code_outb:
|
||||
case code_outw:
|
||||
case code_outl:
|
||||
case code_outsb_a16:
|
||||
case code_outsw_a16:
|
||||
case code_outsl_a16:
|
||||
case code_outsb_a32:
|
||||
case code_outsw_a32:
|
||||
case code_outsl_a32:
|
||||
/* For now we simply enable I/O to the ports and continue */
|
||||
for(i=p->parm1; i<p->parm1+(p->reason&7); i++) {
|
||||
p->ioperm[i/8] &= ~(1<<i%8);
|
||||
}
|
||||
printf("Access to ports %04x-%04x enabled.\n",
|
||||
p->parm1, p->parm1+(p->reason&7)-1);
|
||||
return(0);
|
||||
#endif
|
||||
case code_lretw:
|
||||
/* Check for the exit eyecatcher */
|
||||
if ( *(u_int *)(p->ssbase+ld_le16(&SP)) == UINT_MAX) return 1;
|
||||
/* No break on purpose */
|
||||
default:
|
||||
dump86(p);
|
||||
printf("em86_trap called with unhandled reason code !\n");
|
||||
return(1);
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
void cleanup_v86_mess(void) {
|
||||
x86 *p = (x86 *) bd->v86_private;
|
||||
|
||||
/* This automatically removes the mappings ! */
|
||||
vfree(p->vbase);
|
||||
p->vbase = 0;
|
||||
pfree(p->ram);
|
||||
p->ram = 0;
|
||||
sfree(p->ioperm);
|
||||
p->ioperm=0;
|
||||
}
|
||||
|
||||
|
||||
int init_v86(void) {
|
||||
x86 *p = (x86 *) bd->v86_private;
|
||||
|
||||
/* p->vbase is non null when the v86 is properly set-up */
|
||||
if (p->vbase) return 0;
|
||||
|
||||
/* Set everything to 0 */
|
||||
memset(p, 0, sizeof(*p));
|
||||
p->ioperm = salloc(65536/8+1);
|
||||
p->ram = palloc(0xa0000);
|
||||
p->iobase = ptr_mem_map->io_base;
|
||||
|
||||
if (!p->ram || !p->ioperm) return 1;
|
||||
|
||||
/* The ioperm array must have an additional byte at the end ! */
|
||||
p->ioperm[65536/8] = 0xff;
|
||||
|
||||
p->vbase = valloc(0x110000);
|
||||
if (!p->vbase) return 1;
|
||||
|
||||
/* These calls should never fail. */
|
||||
vmap(p->vbase, (u_long)p->ram|PTE_RAM, 0xa0000);
|
||||
vmap(p->vbase+0x100000, (u_long)p->ram|PTE_RAM, 0x10000);
|
||||
vmap(p->vbase+0xa0000,
|
||||
((u_long)ptr_mem_map->isa_mem_base+0xa0000)|PTE_IO, 0x20000);
|
||||
return 0;
|
||||
}
|
||||
|
||||
void em86_main(struct pci_dev *dev){
|
||||
x86 *p = (x86 *) bd->v86_private;
|
||||
u_short signature;
|
||||
u_char length;
|
||||
volatile u_int *src;
|
||||
u_int *dst, left, saved_rom;
|
||||
#if defined(MONITOR_IO) && !defined(__BOOT__)
|
||||
#define IOMASK 0xff
|
||||
#else
|
||||
#define IOMASK 0
|
||||
#endif
|
||||
|
||||
|
||||
#ifndef __BOOT__
|
||||
int i;
|
||||
/* Allow or disable access to all ports */
|
||||
for(i=0; i<65536/8; i++) p->ioperm[i]=IOMASK;
|
||||
p->ioperm[i] = 0xff; /* Last unused byte must have this value */
|
||||
#endif
|
||||
p->dev = dev;
|
||||
memset(p->vbase, 0, 0xa0000);
|
||||
/* Set up a few registers */
|
||||
p->cs = 0xc000; p->csbase = p->vbase + 0xc0000;
|
||||
p->ss = 0x1000; p->ssbase = p->vbase + 0x10000;
|
||||
p->eflags=0x200;
|
||||
st_le16(&SP,0xfffc); p->eip=3;
|
||||
|
||||
p->dsbase = p->esbase = p->fsbase = p->gsbase = p->vbase;
|
||||
|
||||
/* Follow the PCI BIOS specification */
|
||||
AH=dev->bus->number;
|
||||
AL=dev->devfn;
|
||||
|
||||
/* All other registers are irrelevant except ES:DI which
|
||||
* should point to a PnP installation check block. This
|
||||
* is not yet implemented due to lack of references. */
|
||||
|
||||
/* Store a return address of 0xffff:0xffff as eyecatcher */
|
||||
*(u_int *)(p->ssbase+ld_le16(&SP)) = UINT_MAX;
|
||||
|
||||
/* Interrupt for BIOS EGA services is 0xf000:0xf065 (int 0x10) */
|
||||
st_le32((u_int *)p->vbase + 0x10, 0xf000f065);
|
||||
|
||||
/* Enable the ROM, read it and disable it immediately */
|
||||
pci_read_config_dword(dev, PCI_ROM_ADDRESS, &saved_rom);
|
||||
pci_write_config_dword(dev, PCI_ROM_ADDRESS, 0x000c0001);
|
||||
|
||||
/* Check that there is an Intel ROM. Should we also check that
|
||||
* the first instruction is a jump (0xe9 or 0xeb) ?
|
||||
*/
|
||||
signature = *(u_short *)(ptr_mem_map->isa_mem_base+0xc0000);
|
||||
if (signature!=0x55aa) {
|
||||
printf("bad signature: %04x.\n", signature);
|
||||
return;
|
||||
}
|
||||
/* Allocate memory and copy the video rom to vbase+0xc0000; */
|
||||
length = ptr_mem_map->isa_mem_base[0xc0002];
|
||||
p->rom = palloc(length*512);
|
||||
if (!p->rom) return;
|
||||
|
||||
|
||||
for(dst=(u_int *) p->rom,
|
||||
src=(volatile u_int *)(ptr_mem_map->isa_mem_base+0xc0000),
|
||||
left = length*512/sizeof(u_int);
|
||||
left--;
|
||||
*dst++=*src++);
|
||||
|
||||
/* Disable the ROM and map the copy in virtual address space, note
|
||||
* that the ROM has to be mapped as RAM since some BIOSes (at least
|
||||
* Cirrus) perform write accesses to their own ROM. The reason seems
|
||||
* to be that they check that they must execute from shadow RAM
|
||||
* because accessing the ROM prevents accessing the video RAM
|
||||
* according to comments in linux/arch/alpha/kernel/bios32.c.
|
||||
*/
|
||||
|
||||
pci_write_config_dword(dev, PCI_ROM_ADDRESS, saved_rom);
|
||||
vmap(p->vbase+0xc0000, (u_long)p->rom|PTE_RAM, length*512);
|
||||
|
||||
/* Now actually emulate the ROM init routine */
|
||||
em86_enter(p);
|
||||
|
||||
/* Free the acquired resources */
|
||||
vunmap(p->vbase+0xc0000);
|
||||
pfree(p->rom);
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
Reference in New Issue
Block a user