| /**************************************************************************** | |
| * | |
| * BIOS emulator and interface | |
| * to Realmode X86 Emulator Library | |
| * | |
| * Copyright (C) 2007 Freescale Semiconductor, Inc. | |
| * Jason Jin <Jason.jin@freescale.com> | |
| * | |
| * Copyright (C) 1996-1999 SciTech Software, Inc. | |
| * | |
| * ======================================================================== | |
| * | |
| * Permission to use, copy, modify, distribute, and sell this software and | |
| * its documentation for any purpose is hereby granted without fee, | |
| * provided that the above copyright notice appear in all copies and that | |
| * both that copyright notice and this permission notice appear in | |
| * supporting documentation, and that the name of the authors not be used | |
| * in advertising or publicity pertaining to distribution of the software | |
| * without specific, written prior permission. The authors makes no | |
| * representations about the suitability of this software for any purpose. | |
| * It is provided "as is" without express or implied warranty. | |
| * | |
| * THE AUTHORS DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, | |
| * INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO | |
| * EVENT SHALL THE AUTHORS BE LIABLE FOR ANY SPECIAL, INDIRECT OR | |
| * CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF | |
| * USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR | |
| * OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR | |
| * PERFORMANCE OF THIS SOFTWARE. | |
| * | |
| * ======================================================================== | |
| * | |
| * Language: ANSI C | |
| * Environment: Any | |
| * Developer: Kendall Bennett | |
| * | |
| * Description: Module implementing the BIOS specific functions. | |
| * | |
| * Jason ported this file to u-boot to run the ATI video card | |
| * video BIOS. | |
| * | |
| ****************************************************************************/ | |
| #include <common.h> | |
| #include "biosemui.h" | |
| /*----------------------------- Implementation ----------------------------*/ | |
| /**************************************************************************** | |
| PARAMETERS: | |
| intno - Interrupt number being serviced | |
| REMARKS: | |
| Handler for undefined interrupts. | |
| ****************************************************************************/ | |
| static void X86API undefined_intr(int intno) | |
| { | |
| if (BE_rdw(intno * 4 + 2) == BIOS_SEG) { | |
| DB(printf("biosEmu: undefined interrupt %xh called!\n", intno);) | |
| } else | |
| X86EMU_prepareForInt(intno); | |
| } | |
| /**************************************************************************** | |
| PARAMETERS: | |
| intno - Interrupt number being serviced | |
| REMARKS: | |
| This function handles the default system BIOS Int 10h (the default is stored | |
| in the Int 42h vector by the system BIOS at bootup). We only need to handle | |
| a small number of special functions used by the BIOS during POST time. | |
| ****************************************************************************/ | |
| static void X86API int42(int intno) | |
| { | |
| if (M.x86.R_AH == 0x12 && M.x86.R_BL == 0x32) { | |
| if (M.x86.R_AL == 0) { | |
| /* Enable CPU accesses to video memory */ | |
| PM_outpb(0x3c2, PM_inpb(0x3cc) | (u8) 0x02); | |
| return; | |
| } else if (M.x86.R_AL == 1) { | |
| /* Disable CPU accesses to video memory */ | |
| PM_outpb(0x3c2, PM_inpb(0x3cc) & (u8) ~ 0x02); | |
| return; | |
| } | |
| #ifdef DEBUG | |
| else { | |
| printf("int42: unknown function AH=0x12, BL=0x32, AL=%#02x\n", | |
| M.x86.R_AL); | |
| } | |
| #endif | |
| } | |
| #ifdef DEBUG | |
| else { | |
| printf("int42: unknown function AH=%#02x, AL=%#02x, BL=%#02x\n", | |
| M.x86.R_AH, M.x86.R_AL, M.x86.R_BL); | |
| } | |
| #endif | |
| } | |
| /**************************************************************************** | |
| PARAMETERS: | |
| intno - Interrupt number being serviced | |
| REMARKS: | |
| This function handles the default system BIOS Int 10h. If the POST code | |
| has not yet re-vectored the Int 10h BIOS interrupt vector, we handle this | |
| by simply calling the int42 interrupt handler above. Very early in the | |
| BIOS POST process, the vector gets replaced and we simply let the real | |
| mode interrupt handler process the interrupt. | |
| ****************************************************************************/ | |
| static void X86API int10(int intno) | |
| { | |
| if (BE_rdw(intno * 4 + 2) == BIOS_SEG) | |
| int42(intno); | |
| else | |
| X86EMU_prepareForInt(intno); | |
| } | |
| /* Result codes returned by the PCI BIOS */ | |
| #define SUCCESSFUL 0x00 | |
| #define FUNC_NOT_SUPPORT 0x81 | |
| #define BAD_VENDOR_ID 0x83 | |
| #define DEVICE_NOT_FOUND 0x86 | |
| #define BAD_REGISTER_NUMBER 0x87 | |
| #define SET_FAILED 0x88 | |
| #define BUFFER_TOO_SMALL 0x89 | |
| /**************************************************************************** | |
| PARAMETERS: | |
| intno - Interrupt number being serviced | |
| REMARKS: | |
| This function handles the default Int 1Ah interrupt handler for the real | |
| mode code, which provides support for the PCI BIOS functions. Since we only | |
| want to allow the real mode BIOS code *only* see the PCI config space for | |
| its own device, we only return information for the specific PCI config | |
| space that we have passed in to the init function. This solves problems | |
| when using the BIOS to warm boot a secondary adapter when there is an | |
| identical adapter before it on the bus (some BIOS'es get confused in this | |
| case). | |
| ****************************************************************************/ | |
| static void X86API int1A(int unused) | |
| { | |
| u16 pciSlot; | |
| #ifdef __KERNEL__ | |
| u8 interface, subclass, baseclass; | |
| /* Initialise the PCI slot number */ | |
| pciSlot = ((int)_BE_env.vgaInfo.bus << 8) | | |
| ((int)_BE_env.vgaInfo.device << 3) | (int)_BE_env.vgaInfo.function; | |
| #else | |
| /* Fail if no PCI device information has been registered */ | |
| if (!_BE_env.vgaInfo.pciInfo) | |
| return; | |
| pciSlot = (u16) (_BE_env.vgaInfo.pciInfo->slot.i >> 8); | |
| #endif | |
| switch (M.x86.R_AX) { | |
| case 0xB101: /* PCI bios present? */ | |
| M.x86.R_AL = 0x00; /* no config space/special cycle generation support */ | |
| M.x86.R_EDX = 0x20494350; /* " ICP" */ | |
| M.x86.R_BX = 0x0210; /* Version 2.10 */ | |
| M.x86.R_CL = 0; /* Max bus number in system */ | |
| CLEAR_FLAG(F_CF); | |
| break; | |
| case 0xB102: /* Find PCI device */ | |
| M.x86.R_AH = DEVICE_NOT_FOUND; | |
| #ifdef __KERNEL__ | |
| if (M.x86.R_DX == _BE_env.vgaInfo.VendorID && | |
| M.x86.R_CX == _BE_env.vgaInfo.DeviceID && M.x86.R_SI == 0) { | |
| #else | |
| if (M.x86.R_DX == _BE_env.vgaInfo.pciInfo->VendorID && | |
| M.x86.R_CX == _BE_env.vgaInfo.pciInfo->DeviceID && | |
| M.x86.R_SI == 0) { | |
| #endif | |
| M.x86.R_AH = SUCCESSFUL; | |
| M.x86.R_BX = pciSlot; | |
| } | |
| CONDITIONAL_SET_FLAG((M.x86.R_AH != SUCCESSFUL), F_CF); | |
| break; | |
| case 0xB103: /* Find PCI class code */ | |
| M.x86.R_AH = DEVICE_NOT_FOUND; | |
| #ifdef __KERNEL__ | |
| pci_read_config_byte(_BE_env.vgaInfo.pcidev, PCI_CLASS_PROG, | |
| &interface); | |
| pci_read_config_byte(_BE_env.vgaInfo.pcidev, PCI_CLASS_DEVICE, | |
| &subclass); | |
| pci_read_config_byte(_BE_env.vgaInfo.pcidev, | |
| PCI_CLASS_DEVICE + 1, &baseclass); | |
| if (M.x86.R_CL == interface && M.x86.R_CH == subclass | |
| && (u8) (M.x86.R_ECX >> 16) == baseclass) { | |
| #else | |
| if (M.x86.R_CL == _BE_env.vgaInfo.pciInfo->Interface && | |
| M.x86.R_CH == _BE_env.vgaInfo.pciInfo->SubClass && | |
| (u8) (M.x86.R_ECX >> 16) == | |
| _BE_env.vgaInfo.pciInfo->BaseClass) { | |
| #endif | |
| M.x86.R_AH = SUCCESSFUL; | |
| M.x86.R_BX = pciSlot; | |
| } | |
| CONDITIONAL_SET_FLAG((M.x86.R_AH != SUCCESSFUL), F_CF); | |
| break; | |
| case 0xB108: /* Read configuration byte */ | |
| M.x86.R_AH = BAD_REGISTER_NUMBER; | |
| if (M.x86.R_BX == pciSlot) { | |
| M.x86.R_AH = SUCCESSFUL; | |
| #ifdef __KERNEL__ | |
| pci_read_config_byte(_BE_env.vgaInfo.pcidev, M.x86.R_DI, | |
| &M.x86.R_CL); | |
| #else | |
| M.x86.R_CL = | |
| (u8) PCI_accessReg(M.x86.R_DI, 0, PCI_READ_BYTE, | |
| _BE_env.vgaInfo.pciInfo); | |
| #endif | |
| } | |
| CONDITIONAL_SET_FLAG((M.x86.R_AH != SUCCESSFUL), F_CF); | |
| break; | |
| case 0xB109: /* Read configuration word */ | |
| M.x86.R_AH = BAD_REGISTER_NUMBER; | |
| if (M.x86.R_BX == pciSlot) { | |
| M.x86.R_AH = SUCCESSFUL; | |
| #ifdef __KERNEL__ | |
| pci_read_config_word(_BE_env.vgaInfo.pcidev, M.x86.R_DI, | |
| &M.x86.R_CX); | |
| #else | |
| M.x86.R_CX = | |
| (u16) PCI_accessReg(M.x86.R_DI, 0, PCI_READ_WORD, | |
| _BE_env.vgaInfo.pciInfo); | |
| #endif | |
| } | |
| CONDITIONAL_SET_FLAG((M.x86.R_AH != SUCCESSFUL), F_CF); | |
| break; | |
| case 0xB10A: /* Read configuration dword */ | |
| M.x86.R_AH = BAD_REGISTER_NUMBER; | |
| if (M.x86.R_BX == pciSlot) { | |
| M.x86.R_AH = SUCCESSFUL; | |
| #ifdef __KERNEL__ | |
| pci_read_config_dword(_BE_env.vgaInfo.pcidev, | |
| M.x86.R_DI, &M.x86.R_ECX); | |
| #else | |
| M.x86.R_ECX = | |
| (u32) PCI_accessReg(M.x86.R_DI, 0, PCI_READ_DWORD, | |
| _BE_env.vgaInfo.pciInfo); | |
| #endif | |
| } | |
| CONDITIONAL_SET_FLAG((M.x86.R_AH != SUCCESSFUL), F_CF); | |
| break; | |
| case 0xB10B: /* Write configuration byte */ | |
| M.x86.R_AH = BAD_REGISTER_NUMBER; | |
| if (M.x86.R_BX == pciSlot) { | |
| M.x86.R_AH = SUCCESSFUL; | |
| #ifdef __KERNEL__ | |
| pci_write_config_byte(_BE_env.vgaInfo.pcidev, | |
| M.x86.R_DI, M.x86.R_CL); | |
| #else | |
| PCI_accessReg(M.x86.R_DI, M.x86.R_CL, PCI_WRITE_BYTE, | |
| _BE_env.vgaInfo.pciInfo); | |
| #endif | |
| } | |
| CONDITIONAL_SET_FLAG((M.x86.R_AH != SUCCESSFUL), F_CF); | |
| break; | |
| case 0xB10C: /* Write configuration word */ | |
| M.x86.R_AH = BAD_REGISTER_NUMBER; | |
| if (M.x86.R_BX == pciSlot) { | |
| M.x86.R_AH = SUCCESSFUL; | |
| #ifdef __KERNEL__ | |
| pci_write_config_word(_BE_env.vgaInfo.pcidev, | |
| M.x86.R_DI, M.x86.R_CX); | |
| #else | |
| PCI_accessReg(M.x86.R_DI, M.x86.R_CX, PCI_WRITE_WORD, | |
| _BE_env.vgaInfo.pciInfo); | |
| #endif | |
| } | |
| CONDITIONAL_SET_FLAG((M.x86.R_AH != SUCCESSFUL), F_CF); | |
| break; | |
| case 0xB10D: /* Write configuration dword */ | |
| M.x86.R_AH = BAD_REGISTER_NUMBER; | |
| if (M.x86.R_BX == pciSlot) { | |
| M.x86.R_AH = SUCCESSFUL; | |
| #ifdef __KERNEL__ | |
| pci_write_config_dword(_BE_env.vgaInfo.pcidev, | |
| M.x86.R_DI, M.x86.R_ECX); | |
| #else | |
| PCI_accessReg(M.x86.R_DI, M.x86.R_ECX, PCI_WRITE_DWORD, | |
| _BE_env.vgaInfo.pciInfo); | |
| #endif | |
| } | |
| CONDITIONAL_SET_FLAG((M.x86.R_AH != SUCCESSFUL), F_CF); | |
| break; | |
| default: | |
| printf("biosEmu/bios.int1a: unknown function AX=%#04x\n", | |
| M.x86.R_AX); | |
| } | |
| } | |
| /**************************************************************************** | |
| REMARKS: | |
| This function initialises the BIOS emulation functions for the specific | |
| PCI display device. We insulate the real mode BIOS from any other devices | |
| on the bus, so that it will work correctly thinking that it is the only | |
| device present on the bus (ie: avoiding any adapters present in from of | |
| the device we are trying to control). | |
| ****************************************************************************/ | |
| #define BE_constLE_32(v) ((((((v)&0xff00)>>8)|(((v)&0xff)<<8))<<16)|(((((v)&0xff000000)>>8)|(((v)&0x00ff0000)<<8))>>16)) | |
| void _BE_bios_init(u32 * intrTab) | |
| { | |
| int i; | |
| X86EMU_intrFuncs bios_intr_tab[256]; | |
| for (i = 0; i < 256; ++i) { | |
| intrTab[i] = BE_constLE_32(BIOS_SEG << 16); | |
| bios_intr_tab[i] = undefined_intr; | |
| } | |
| bios_intr_tab[0x10] = int10; | |
| bios_intr_tab[0x1A] = int1A; | |
| bios_intr_tab[0x42] = int42; | |
| bios_intr_tab[0x6D] = int10; | |
| X86EMU_setupIntrFuncs(bios_intr_tab); | |
| } |