345 lines
14 KiB
Plaintext
345 lines
14 KiB
Plaintext
|
|
||
|
INT10 X86 Real Mode executor
|
||
|
=============================
|
||
|
|
||
|
PRELIMINARY
|
||
|
|
||
|
INT10 is a XFree86 module for soft-booting and executing real mode
|
||
|
int10 BIOS calls. The BIOS call code is largely untested, yet.
|
||
|
|
||
|
1. Usage
|
||
|
========
|
||
|
|
||
|
To use the int10 module in a driver the header file
|
||
|
xfree86/os-support/int10/xf86int10.h must be included.
|
||
|
|
||
|
a. Initialization
|
||
|
-----------------
|
||
|
|
||
|
The int10-executer gets initialized by calling:
|
||
|
|
||
|
xf86Int10InfoPtr xf86InitInt10(int entityIndex);
|
||
|
|
||
|
The function will soft-boot any non-primary device and return a
|
||
|
pointer to a xf86Int10InfoRec on success. If anything fails or if
|
||
|
int10 execution is disabled by an option in the device section NULL
|
||
|
will be returned. The driver should store this pointer for later
|
||
|
calls to other int10 module functions.
|
||
|
|
||
|
b. Memory allocation
|
||
|
--------------------
|
||
|
|
||
|
To allocate memory in the real mode execution environment
|
||
|
|
||
|
void * xf86Int10AllocPages(xf86Int10InfoPtr pInt,int num, int *off);
|
||
|
|
||
|
can be called. It allocates num consecutive pagesize chunks. It
|
||
|
returns the address of the allocated area. off is set to its offset in
|
||
|
the real mode memory space.
|
||
|
|
||
|
void xf86Int10FreePages(xf86Int10InfoPtr pInt, void *pbase, int num);
|
||
|
|
||
|
Is used to free num pages beginning at pbase.
|
||
|
|
||
|
c. Doing int10 BIOS calls
|
||
|
-------------------------
|
||
|
|
||
|
The BIOS call is executed by calling:
|
||
|
|
||
|
void xf86ExecX86int10(xf86Int10InfoPtr pInt);
|
||
|
|
||
|
The number of the interrupt (normally 10) and the initial values of
|
||
|
the ax, bx, cx, dx, si, di and es x86-CPU registers can be set in the
|
||
|
xf86Int10InfoRec passed to the function. On return this structure
|
||
|
contains the exit values of the registers listed above and the CPU
|
||
|
flag register.
|
||
|
|
||
|
d. De-initializing
|
||
|
-----------------
|
||
|
|
||
|
If no further int10 calls are required for a certain chipset
|
||
|
the driver should call:
|
||
|
|
||
|
void xf86FreeInt10(xf86Int10InfoPtr pInt);
|
||
|
|
||
|
to free the memory allocated for real mode int10 calls.
|
||
|
|
||
|
|
||
|
2. Porting issues
|
||
|
=================
|
||
|
|
||
|
The int10 real mode executor is designed to run on top of various x86
|
||
|
CPU emulators as well as in vm86 mode of a real x86 CPU. If used with
|
||
|
a CPU emulator the emulator and CPU specific interfaces can be held
|
||
|
separate thus requiring minimal efforts to port the int10 module to
|
||
|
new platforms. Currently an interface to the x86emu real mode
|
||
|
emulator is provided. Since details of setting up and running the
|
||
|
vm86 mode is platform dependent both the platform dependent
|
||
|
environment and the emulation layer have to be ported. Several helper
|
||
|
functions are provided for that.
|
||
|
|
||
|
A CPU emulator should meet certain requirements to be usable
|
||
|
for the INT10 executor:
|
||
|
|
||
|
1. It must trap calls to intXX instructions and pass execution to an
|
||
|
external function which is allowed to modify CPU registers
|
||
|
including the instruction pointer (IP) before returning to the
|
||
|
emulator for continuing execution. When the external function is
|
||
|
called the IP must point to the instruction past the intXX call.
|
||
|
|
||
|
2. The emulator should use externally provided functions to handle
|
||
|
PIO.
|
||
|
|
||
|
3. The emulator should be able to use externally provided functions
|
||
|
to access memory from the real mode memory environment. Note, that
|
||
|
the vm86 mode usually requires one hunk of consecutive memory
|
||
|
starting at address 0 in the process virtual memory space. Thus if
|
||
|
this mode is to be used, the OS environment has to be able to provide
|
||
|
that, ie. it must be able to remap the processes virtual memory space
|
||
|
onto itself. If the emulator is able to handle memory access thru
|
||
|
externally provided functions the real mode process memory can be
|
||
|
located anywhere in the processes virtual memory. It does not even
|
||
|
have to be consecutive.
|
||
|
|
||
|
4. The executor should terminate on encountering a 'hlt' instruction.
|
||
|
|
||
|
|
||
|
Functions to implement:
|
||
|
|
||
|
To simplify development the code has been split into a general setup
|
||
|
part and an emulator specific one. A generic setup code is provided in
|
||
|
generic.c. It should be usable with any emulator satisfying the
|
||
|
conditions mentioned above. Therefore the following section on int10
|
||
|
setup may be skipped when porting int10 to new emulator.
|
||
|
|
||
|
If the vm86() is to be used no memory access functions can be used.
|
||
|
Therefore the layout of the real mode memory image has to meet certain
|
||
|
requirements. Therefore when porting to other platforms a new setup
|
||
|
code may have to be designed, too. The following section will give
|
||
|
guidelines how this may be done. A sample implementation using SysV
|
||
|
IPC to map the appropriate real mode memory image to address 0 in
|
||
|
virtual address space just prior to execution may be found in
|
||
|
xfree86/os-support/linux/int10/linux.c.
|
||
|
|
||
|
On non-PC like platforms emulation of certain PC features such as
|
||
|
initialization of BIOS int vectors, sys_BIOS constants or PCI config
|
||
|
method 1 can be turned on by defining _PC.
|
||
|
|
||
|
I. Setup Code
|
||
|
-------------
|
||
|
|
||
|
This sets up the real mode memory image, calls the emulator to POST
|
||
|
the chipset if required and maintains memory allocations in real mode
|
||
|
address space.
|
||
|
|
||
|
1. xf86Int10InfoPtr xf86InitInt10(int entityIndex);
|
||
|
|
||
|
This function should first find the screen assigned to the entity
|
||
|
carrying entitiyIndex and then call
|
||
|
|
||
|
Bool int10skip(ScrnInfoPtr pScrn)
|
||
|
|
||
|
to find out if the user has requested not to initialize int10. If so
|
||
|
xf86InitInt10() should return NULL. Otherwise an xf86Int10InfoRec
|
||
|
should be allocated. This structure contains the following fields:
|
||
|
|
||
|
a. int entityIndex - index of the entity whose BIOS is to be
|
||
|
executed.
|
||
|
b. int scrnIndex - index of the screen assigned the entity.
|
||
|
c. pointer cpuRegs - pointer to a emulator/vm86-mode private
|
||
|
structure. May hold cpu register values
|
||
|
for the emulator.
|
||
|
d. CARD16 BIOSseg - Video BIOS segment address.
|
||
|
e. pointer private - pointer to a os specific data structure.
|
||
|
f. struct _int10Mem* - pointer to a structure to hold the memory
|
||
|
access functions for use by an emulator.
|
||
|
g. int num - number of the int to be called.
|
||
|
h. int ax..es,flags - CPU register values to pass to int-call.
|
||
|
|
||
|
The Init function should initialize a-f. To initialize the emulator
|
||
|
specific execute environment the function
|
||
|
|
||
|
Bool xf86Int10ExecSetup(xf86Int10InfoPtr pInt)
|
||
|
|
||
|
should be called. If this function returns FALSE any already allocated
|
||
|
memory should be freed and xf86Int10Init(0 should exit returning NULL.
|
||
|
|
||
|
If the platform has a PC like system BIOS it may be copied to or
|
||
|
mapped into memory locations SYS_BIOS to SYS_SIZE-1 of the real mode
|
||
|
memory environment of this process. Otherwise the helper function:
|
||
|
|
||
|
int setup_system_bios(CARD32 base_addr);
|
||
|
|
||
|
may be called to set up a rudimentary system BIOS sufficient to be
|
||
|
used to boot video BIOSes. base_addr specifies the virtual address
|
||
|
corresponding to SYS_BIOS in the real mode environment. If a PC-like
|
||
|
int vector and BIOS data area is available it should be copied to 0 to
|
||
|
LOW_PAGE_SIZE of the entities real mode environment. In this case the
|
||
|
video interrupt related entries should be reset for all non-primary
|
||
|
cards by calling:
|
||
|
|
||
|
void reset_int_vect(xf86Int10InfoPtr pInt); To initialize the
|
||
|
|
||
|
correct video BIOS entry points the BIOS must be warm-booted. If no
|
||
|
PC-like int vector is available one can be set up by calling
|
||
|
|
||
|
void setup_int_vect(xf86Int10InfoPtr pInt);
|
||
|
|
||
|
In this case the video BIOS has to be warm-booted always. If the
|
||
|
video BIOS for this entity has been installed during boot it may be
|
||
|
mapped (or copied) directly to the correct address in the real mode
|
||
|
memory environment. Otherwise
|
||
|
|
||
|
int mapPciRom(xf86Int10InfoPtr pInt, unsigned char * address);
|
||
|
|
||
|
should be called to copy the BIOS image from PCI ROM. 'address'
|
||
|
specifies the address this image should be copied to. Sufficient space
|
||
|
to hold an entire BIOS image should be allocated prior to calling
|
||
|
mapPciRom(). This function will return the size of the BIOS image in
|
||
|
bytes if it was able to successfully copy the image and 0
|
||
|
otherwise. To create a well defined point to exit the softbooter
|
||
|
|
||
|
void set_return_trap(xf86Int10Ptr pInt);
|
||
|
|
||
|
may be called. It sets up a 'hlt' instruction in the emulator memory
|
||
|
just above the BIOS variable area. Before entering real mode execution
|
||
|
this address will be pushed onto the return stack. If the BIOS needs
|
||
|
to be warm-booted this should be done before leaving xf86InitInt10()
|
||
|
by setting num in the xf86Int10InfoRec to 0xe6 and calling
|
||
|
|
||
|
void xf86ExecX86int10(xf86Int10IfoPtr pInt);
|
||
|
|
||
|
The implementation of this function will be discussed below. This
|
||
|
function should be wrapped by calls to void LockLegacyVGA(screen,
|
||
|
legacyVGAPtr vga); and void UnlockLegacyVGA(screen, legacyVGAPtr vga);
|
||
|
The struct vga is used to hold the state of the legacy VGA access
|
||
|
registers if a legacy VGA device exists. xf86InitInt10() should
|
||
|
return a pointer to the xf86Int10InfoRec allocated.
|
||
|
|
||
|
2. Bool MapCurrentInt10(xf86Int10InfoPtr pInt);
|
||
|
|
||
|
In case a platform specific mapping has to be performed to map the
|
||
|
memory allocated for the real mode memory environment into a specific
|
||
|
location prior to executing the x86 real mode code a function
|
||
|
|
||
|
Bool MapCurrentInt10(xf86Int10InfoPtr pInt);
|
||
|
|
||
|
has to be provided. It will be called by a helper function whenever
|
||
|
the active entity changes. If the vm86 mode is used it is most likely
|
||
|
that the 1MB real mode memory space located somewhere in the processes
|
||
|
virtual memory will have to be remapped to address 0 of the virtual
|
||
|
memory space.
|
||
|
|
||
|
3. void xf86FreeInt10(xf86Int10InfoPtr pInt);
|
||
|
|
||
|
To free all memory allocated for video BIOS calls of a specific entity
|
||
|
the function
|
||
|
|
||
|
void xf86FreeInt10(xf86Int10InfoPtr pInt);
|
||
|
|
||
|
should be provided. If the entity to be freed was mapped by
|
||
|
MapCurrentInt10() this mapping needs to be undone also.
|
||
|
|
||
|
4.
|
||
|
void * xf86Int10AllocPages(xf86Int10InfoPtr pInt,int num, int *off)
|
||
|
void xf86Int10FreePages(xf86Int10InfoPtr pInt, void *pbase, int num)
|
||
|
|
||
|
xf86Int10AllocPages() should allocate 'num' consecutive page-size
|
||
|
chunks of memory. In real mode memory space this range needs to occupy
|
||
|
consecutive addresses, too. The function must return the address of
|
||
|
this memory. The offset in real mode memory needs to be returned in
|
||
|
'off'. If no block of 'num' pages are available the function should
|
||
|
return NULL.
|
||
|
|
||
|
xf86Int10FreePages() will free the 'num' pages starting at 'pbase'.
|
||
|
'num' is equal to the number of pages allocated by a single
|
||
|
xf86Int10AllocatePages() call. 'pbase' is the address of the range
|
||
|
previously returned by xf86Int10AllocatePages().
|
||
|
|
||
|
II. Emulator specific functions
|
||
|
-------------------------------
|
||
|
|
||
|
1. Bool xf86Int10ExecSetup(xf86Int10InfoPtr pInt);
|
||
|
|
||
|
This function will be called from xf86InitInt10(). It may be used to
|
||
|
set up the static emulator specific part of the real mode
|
||
|
environment. On success it should return TRUE.
|
||
|
|
||
|
2. xf86ExecX86int10(xf86Int10InfoPtr pInt);
|
||
|
|
||
|
This function gets called to execute an int call. It may call the
|
||
|
helper function:
|
||
|
|
||
|
void setup_int(xf86Int10InfoPrt pInt);
|
||
|
|
||
|
to copy the register values to the emulator specific locations and to
|
||
|
set up the non-static real mode execution environment. On return from
|
||
|
setup_int() 'Int10Current' holds a pointer to the current
|
||
|
xf86Int10InfoRec.
|
||
|
|
||
|
It should start execution by calling
|
||
|
|
||
|
Bool int_handler(xf86Int10InfoPtr pInt);
|
||
|
|
||
|
and if this function returns TRUE it should call whatever necessary to
|
||
|
continue execution until a 'hlt' instruction is encountered. To copy
|
||
|
the resulting register values back to the xf86Int10InfoRec structure
|
||
|
|
||
|
void finish_int(xf86Int10InfoPtr pInt);
|
||
|
|
||
|
should be called.
|
||
|
|
||
|
Helper functions are provided to aid the implementation of a vm86
|
||
|
call:
|
||
|
|
||
|
Bool vm86_GP_fault(xf86Int10InfoPtr pInt);
|
||
|
|
||
|
This function handles instructions which cause a vm86 call to
|
||
|
trap. PIO access is handled by the in/out calls as defined in
|
||
|
compiler.h. Optionally the PIO instructions can be logged by defining
|
||
|
PRINT_PORT in xf86int10.h. This is meant for debugging purposes.
|
||
|
|
||
|
Unknown instructions and 'hlt' cause vm86_GP_fault() to return
|
||
|
FALSE. Otherwise TRUE is returned.
|
||
|
|
||
|
Note: This function is currently based on the Linux vm86 call. It
|
||
|
might have to be modified or even rewritten for other OS. So your
|
||
|
milage may vary.
|
||
|
|
||
|
Functions to dump memory, code, xf86 CPU register values and stack are
|
||
|
also provided. Take a look at helper.c To view a memory range the
|
||
|
function
|
||
|
|
||
|
void dprint(unsigned long start, unsigned long size)
|
||
|
|
||
|
is provided. The use should be self explanatory.
|
||
|
|
||
|
Register and memory access functions are provided in helper_mem.c.
|
||
|
The PIO register access functions can trap access to PCI config space
|
||
|
access register (config method 1) if _PC is not defined.
|
||
|
|
||
|
A header file 'defines.h' is required to define OS/emulator specific
|
||
|
ways to access memory and xf86 CPU registers: Defines need to be
|
||
|
provided for memory byte/work/long read/write access
|
||
|
(MEM_RB(name,addr),MEM_RW(name,addr),MEM_RL(name,addr),
|
||
|
MEM_WB(name,addr,val),MEM_WL(name,addr,val),MEM_WL(name,addr,val)) of
|
||
|
the real mode memory environment. 'name' will contain a pointer to the
|
||
|
current xf86Int10InfoRec. Currently defines are available for
|
||
|
vm86-mode under Linux and x86emu. They may be activated by defining
|
||
|
_X86EMU or _VM86_LINUX respectively.
|
||
|
|
||
|
Note: Emulators usually are not able to pass this pointer when calling
|
||
|
memory access functions. In this case a global variable should be
|
||
|
defined which can hold this pointer. This variable can be set in
|
||
|
MapCurrentInt10(). It also must be set in xf86InitInt10() if this
|
||
|
function calls the memory access functions either directly or by
|
||
|
calling xf86ExecX86int10(pInt). Defines to access the emulator
|
||
|
specific xf86 CPU register locations are also required:
|
||
|
X86_EAX,...,X86_EFLAGS for access of the full 32 bit registers,
|
||
|
X86_AX...X86_FLAGS for access of the 16 bit registers and
|
||
|
XF86_AL,XF86_BL,XF86_CL,XF86_DL to access the lower byte of the
|
||
|
AX,BX,CX and DX register.
|
||
|
|
||
|
|
||
|
$XFree86: xc/programs/Xserver/hw/xfree86/int10/INT10.HOWTO,v 1.3 2001/04/30 14:34:57 tsi Exp $
|