Jason Jin | a63ce95 | 2007-07-06 08:34:56 +0800 | [diff] [blame] | 1 | /**************************************************************************** |
| 2 | * |
| 3 | * BIOS emulator and interface |
| 4 | * to Realmode X86 Emulator Library |
| 5 | * |
Kumar Gala | 6a6d948 | 2009-07-28 21:49:52 -0500 | [diff] [blame] | 6 | * Copyright (C) 2007 Freescale Semiconductor, Inc. |
Jason Jin | a63ce95 | 2007-07-06 08:34:56 +0800 | [diff] [blame] | 7 | * Jason Jin <Jason.jin@freescale.com> |
| 8 | * |
| 9 | * Copyright (C) 1996-1999 SciTech Software, Inc. |
| 10 | * |
| 11 | * ======================================================================== |
| 12 | * |
| 13 | * Permission to use, copy, modify, distribute, and sell this software and |
| 14 | * its documentation for any purpose is hereby granted without fee, |
| 15 | * provided that the above copyright notice appear in all copies and that |
| 16 | * both that copyright notice and this permission notice appear in |
| 17 | * supporting documentation, and that the name of the authors not be used |
| 18 | * in advertising or publicity pertaining to distribution of the software |
| 19 | * without specific, written prior permission. The authors makes no |
| 20 | * representations about the suitability of this software for any purpose. |
| 21 | * It is provided "as is" without express or implied warranty. |
| 22 | * |
| 23 | * THE AUTHORS DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, |
| 24 | * INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO |
| 25 | * EVENT SHALL THE AUTHORS BE LIABLE FOR ANY SPECIAL, INDIRECT OR |
| 26 | * CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF |
| 27 | * USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR |
| 28 | * OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR |
| 29 | * PERFORMANCE OF THIS SOFTWARE. |
| 30 | * |
| 31 | * ======================================================================== |
| 32 | * |
| 33 | * Language: ANSI C |
| 34 | * Environment: Any |
| 35 | * Developer: Kendall Bennett |
| 36 | * |
| 37 | * Description: Module implementing the BIOS specific functions. |
| 38 | * |
Wolfgang Denk | a1be476 | 2008-05-20 16:00:29 +0200 | [diff] [blame] | 39 | * Jason ported this file to u-boot to run the ATI video card |
| 40 | * video BIOS. |
Jason Jin | a63ce95 | 2007-07-06 08:34:56 +0800 | [diff] [blame] | 41 | * |
| 42 | ****************************************************************************/ |
| 43 | |
Linus Walleij | 9b6a5af | 2013-04-01 22:14:14 +0000 | [diff] [blame] | 44 | #define __io |
Michal Simek | c73a477 | 2007-08-16 10:46:28 +0200 | [diff] [blame] | 45 | #include <common.h> |
Simon Glass | a9b9ef4 | 2014-12-10 20:12:01 -0700 | [diff] [blame] | 46 | #include <asm/io.h> |
Jason Jin | a63ce95 | 2007-07-06 08:34:56 +0800 | [diff] [blame] | 47 | #include "biosemui.h" |
| 48 | |
| 49 | /*----------------------------- Implementation ----------------------------*/ |
| 50 | |
| 51 | /**************************************************************************** |
| 52 | PARAMETERS: |
| 53 | intno - Interrupt number being serviced |
| 54 | |
| 55 | REMARKS: |
| 56 | Handler for undefined interrupts. |
| 57 | ****************************************************************************/ |
| 58 | static void X86API undefined_intr(int intno) |
| 59 | { |
| 60 | if (BE_rdw(intno * 4 + 2) == BIOS_SEG) { |
| 61 | DB(printf("biosEmu: undefined interrupt %xh called!\n", intno);) |
| 62 | } else |
| 63 | X86EMU_prepareForInt(intno); |
| 64 | } |
| 65 | |
| 66 | /**************************************************************************** |
| 67 | PARAMETERS: |
| 68 | intno - Interrupt number being serviced |
| 69 | |
| 70 | REMARKS: |
| 71 | This function handles the default system BIOS Int 10h (the default is stored |
| 72 | in the Int 42h vector by the system BIOS at bootup). We only need to handle |
| 73 | a small number of special functions used by the BIOS during POST time. |
| 74 | ****************************************************************************/ |
| 75 | static void X86API int42(int intno) |
| 76 | { |
| 77 | if (M.x86.R_AH == 0x12 && M.x86.R_BL == 0x32) { |
| 78 | if (M.x86.R_AL == 0) { |
| 79 | /* Enable CPU accesses to video memory */ |
| 80 | PM_outpb(0x3c2, PM_inpb(0x3cc) | (u8) 0x02); |
| 81 | return; |
| 82 | } else if (M.x86.R_AL == 1) { |
| 83 | /* Disable CPU accesses to video memory */ |
| 84 | PM_outpb(0x3c2, PM_inpb(0x3cc) & (u8) ~ 0x02); |
| 85 | return; |
| 86 | } |
Simon Glass | d8414fc | 2014-11-14 20:56:42 -0700 | [diff] [blame] | 87 | #ifdef CONFIG_X86EMU_DEBUG |
Jason Jin | a63ce95 | 2007-07-06 08:34:56 +0800 | [diff] [blame] | 88 | else { |
| 89 | printf("int42: unknown function AH=0x12, BL=0x32, AL=%#02x\n", |
| 90 | M.x86.R_AL); |
| 91 | } |
| 92 | #endif |
| 93 | } |
Simon Glass | d8414fc | 2014-11-14 20:56:42 -0700 | [diff] [blame] | 94 | #ifdef CONFIG_X86EMU_DEBUG |
Jason Jin | a63ce95 | 2007-07-06 08:34:56 +0800 | [diff] [blame] | 95 | else { |
| 96 | printf("int42: unknown function AH=%#02x, AL=%#02x, BL=%#02x\n", |
| 97 | M.x86.R_AH, M.x86.R_AL, M.x86.R_BL); |
| 98 | } |
| 99 | #endif |
| 100 | } |
| 101 | |
| 102 | /**************************************************************************** |
| 103 | PARAMETERS: |
| 104 | intno - Interrupt number being serviced |
| 105 | |
| 106 | REMARKS: |
| 107 | This function handles the default system BIOS Int 10h. If the POST code |
| 108 | has not yet re-vectored the Int 10h BIOS interrupt vector, we handle this |
| 109 | by simply calling the int42 interrupt handler above. Very early in the |
| 110 | BIOS POST process, the vector gets replaced and we simply let the real |
| 111 | mode interrupt handler process the interrupt. |
| 112 | ****************************************************************************/ |
| 113 | static void X86API int10(int intno) |
| 114 | { |
| 115 | if (BE_rdw(intno * 4 + 2) == BIOS_SEG) |
| 116 | int42(intno); |
| 117 | else |
| 118 | X86EMU_prepareForInt(intno); |
| 119 | } |
| 120 | |
| 121 | /* Result codes returned by the PCI BIOS */ |
| 122 | |
| 123 | #define SUCCESSFUL 0x00 |
| 124 | #define FUNC_NOT_SUPPORT 0x81 |
| 125 | #define BAD_VENDOR_ID 0x83 |
| 126 | #define DEVICE_NOT_FOUND 0x86 |
| 127 | #define BAD_REGISTER_NUMBER 0x87 |
| 128 | #define SET_FAILED 0x88 |
| 129 | #define BUFFER_TOO_SMALL 0x89 |
| 130 | |
| 131 | /**************************************************************************** |
| 132 | PARAMETERS: |
| 133 | intno - Interrupt number being serviced |
| 134 | |
| 135 | REMARKS: |
| 136 | This function handles the default Int 1Ah interrupt handler for the real |
| 137 | mode code, which provides support for the PCI BIOS functions. Since we only |
| 138 | want to allow the real mode BIOS code *only* see the PCI config space for |
| 139 | its own device, we only return information for the specific PCI config |
| 140 | space that we have passed in to the init function. This solves problems |
| 141 | when using the BIOS to warm boot a secondary adapter when there is an |
| 142 | identical adapter before it on the bus (some BIOS'es get confused in this |
| 143 | case). |
| 144 | ****************************************************************************/ |
| 145 | static void X86API int1A(int unused) |
| 146 | { |
| 147 | u16 pciSlot; |
| 148 | |
| 149 | #ifdef __KERNEL__ |
| 150 | u8 interface, subclass, baseclass; |
| 151 | |
| 152 | /* Initialise the PCI slot number */ |
| 153 | pciSlot = ((int)_BE_env.vgaInfo.bus << 8) | |
| 154 | ((int)_BE_env.vgaInfo.device << 3) | (int)_BE_env.vgaInfo.function; |
| 155 | #else |
| 156 | /* Fail if no PCI device information has been registered */ |
| 157 | if (!_BE_env.vgaInfo.pciInfo) |
| 158 | return; |
| 159 | |
| 160 | pciSlot = (u16) (_BE_env.vgaInfo.pciInfo->slot.i >> 8); |
| 161 | #endif |
| 162 | switch (M.x86.R_AX) { |
| 163 | case 0xB101: /* PCI bios present? */ |
| 164 | M.x86.R_AL = 0x00; /* no config space/special cycle generation support */ |
| 165 | M.x86.R_EDX = 0x20494350; /* " ICP" */ |
| 166 | M.x86.R_BX = 0x0210; /* Version 2.10 */ |
| 167 | M.x86.R_CL = 0; /* Max bus number in system */ |
| 168 | CLEAR_FLAG(F_CF); |
| 169 | break; |
| 170 | case 0xB102: /* Find PCI device */ |
| 171 | M.x86.R_AH = DEVICE_NOT_FOUND; |
| 172 | #ifdef __KERNEL__ |
| 173 | if (M.x86.R_DX == _BE_env.vgaInfo.VendorID && |
| 174 | M.x86.R_CX == _BE_env.vgaInfo.DeviceID && M.x86.R_SI == 0) { |
| 175 | #else |
| 176 | if (M.x86.R_DX == _BE_env.vgaInfo.pciInfo->VendorID && |
| 177 | M.x86.R_CX == _BE_env.vgaInfo.pciInfo->DeviceID && |
| 178 | M.x86.R_SI == 0) { |
| 179 | #endif |
| 180 | M.x86.R_AH = SUCCESSFUL; |
| 181 | M.x86.R_BX = pciSlot; |
| 182 | } |
| 183 | CONDITIONAL_SET_FLAG((M.x86.R_AH != SUCCESSFUL), F_CF); |
| 184 | break; |
| 185 | case 0xB103: /* Find PCI class code */ |
| 186 | M.x86.R_AH = DEVICE_NOT_FOUND; |
| 187 | #ifdef __KERNEL__ |
Simon Glass | d3e0c8f | 2016-01-17 16:11:09 -0700 | [diff] [blame^] | 188 | #ifdef CONFIG_DM_PCI |
| 189 | dm_pci_read_config8(_BE_env.vgaInfo.pcidev, PCI_CLASS_PROG, |
| 190 | &interface); |
| 191 | dm_pci_read_config8(_BE_env.vgaInfo.pcidev, PCI_CLASS_DEVICE, |
| 192 | &subclass); |
| 193 | dm_pci_read_config8(_BE_env.vgaInfo.pcidev, |
| 194 | PCI_CLASS_DEVICE + 1, &baseclass); |
| 195 | #else |
Jason Jin | a63ce95 | 2007-07-06 08:34:56 +0800 | [diff] [blame] | 196 | pci_read_config_byte(_BE_env.vgaInfo.pcidev, PCI_CLASS_PROG, |
| 197 | &interface); |
| 198 | pci_read_config_byte(_BE_env.vgaInfo.pcidev, PCI_CLASS_DEVICE, |
| 199 | &subclass); |
| 200 | pci_read_config_byte(_BE_env.vgaInfo.pcidev, |
| 201 | PCI_CLASS_DEVICE + 1, &baseclass); |
Simon Glass | d3e0c8f | 2016-01-17 16:11:09 -0700 | [diff] [blame^] | 202 | #endif |
Jason Jin | a63ce95 | 2007-07-06 08:34:56 +0800 | [diff] [blame] | 203 | if (M.x86.R_CL == interface && M.x86.R_CH == subclass |
| 204 | && (u8) (M.x86.R_ECX >> 16) == baseclass) { |
| 205 | #else |
| 206 | if (M.x86.R_CL == _BE_env.vgaInfo.pciInfo->Interface && |
| 207 | M.x86.R_CH == _BE_env.vgaInfo.pciInfo->SubClass && |
| 208 | (u8) (M.x86.R_ECX >> 16) == |
| 209 | _BE_env.vgaInfo.pciInfo->BaseClass) { |
| 210 | #endif |
| 211 | M.x86.R_AH = SUCCESSFUL; |
| 212 | M.x86.R_BX = pciSlot; |
| 213 | } |
| 214 | CONDITIONAL_SET_FLAG((M.x86.R_AH != SUCCESSFUL), F_CF); |
| 215 | break; |
| 216 | case 0xB108: /* Read configuration byte */ |
| 217 | M.x86.R_AH = BAD_REGISTER_NUMBER; |
| 218 | if (M.x86.R_BX == pciSlot) { |
| 219 | M.x86.R_AH = SUCCESSFUL; |
| 220 | #ifdef __KERNEL__ |
Simon Glass | d3e0c8f | 2016-01-17 16:11:09 -0700 | [diff] [blame^] | 221 | # ifdef CONFIG_DM_PCI |
| 222 | dm_pci_read_config8(_BE_env.vgaInfo.pcidev, M.x86.R_DI, |
| 223 | &M.x86.R_CL); |
| 224 | # else |
Jason Jin | a63ce95 | 2007-07-06 08:34:56 +0800 | [diff] [blame] | 225 | pci_read_config_byte(_BE_env.vgaInfo.pcidev, M.x86.R_DI, |
| 226 | &M.x86.R_CL); |
Simon Glass | d3e0c8f | 2016-01-17 16:11:09 -0700 | [diff] [blame^] | 227 | # endif |
Jason Jin | a63ce95 | 2007-07-06 08:34:56 +0800 | [diff] [blame] | 228 | #else |
| 229 | M.x86.R_CL = |
| 230 | (u8) PCI_accessReg(M.x86.R_DI, 0, PCI_READ_BYTE, |
| 231 | _BE_env.vgaInfo.pciInfo); |
| 232 | #endif |
| 233 | } |
| 234 | CONDITIONAL_SET_FLAG((M.x86.R_AH != SUCCESSFUL), F_CF); |
| 235 | break; |
| 236 | case 0xB109: /* Read configuration word */ |
| 237 | M.x86.R_AH = BAD_REGISTER_NUMBER; |
| 238 | if (M.x86.R_BX == pciSlot) { |
| 239 | M.x86.R_AH = SUCCESSFUL; |
| 240 | #ifdef __KERNEL__ |
Simon Glass | d3e0c8f | 2016-01-17 16:11:09 -0700 | [diff] [blame^] | 241 | # ifdef CONFIG_DM_PCI |
| 242 | dm_pci_read_config16(_BE_env.vgaInfo.pcidev, M.x86.R_DI, |
| 243 | &M.x86.R_CX); |
| 244 | # else |
Jason Jin | a63ce95 | 2007-07-06 08:34:56 +0800 | [diff] [blame] | 245 | pci_read_config_word(_BE_env.vgaInfo.pcidev, M.x86.R_DI, |
| 246 | &M.x86.R_CX); |
Simon Glass | d3e0c8f | 2016-01-17 16:11:09 -0700 | [diff] [blame^] | 247 | # endif |
Jason Jin | a63ce95 | 2007-07-06 08:34:56 +0800 | [diff] [blame] | 248 | #else |
| 249 | M.x86.R_CX = |
| 250 | (u16) PCI_accessReg(M.x86.R_DI, 0, PCI_READ_WORD, |
| 251 | _BE_env.vgaInfo.pciInfo); |
| 252 | #endif |
| 253 | } |
| 254 | CONDITIONAL_SET_FLAG((M.x86.R_AH != SUCCESSFUL), F_CF); |
| 255 | break; |
| 256 | case 0xB10A: /* Read configuration dword */ |
| 257 | M.x86.R_AH = BAD_REGISTER_NUMBER; |
| 258 | if (M.x86.R_BX == pciSlot) { |
| 259 | M.x86.R_AH = SUCCESSFUL; |
| 260 | #ifdef __KERNEL__ |
Simon Glass | d3e0c8f | 2016-01-17 16:11:09 -0700 | [diff] [blame^] | 261 | # ifdef CONFIG_DM_PCI |
| 262 | dm_pci_read_config32(_BE_env.vgaInfo.pcidev, |
| 263 | M.x86.R_DI, &M.x86.R_ECX); |
| 264 | # else |
Jason Jin | a63ce95 | 2007-07-06 08:34:56 +0800 | [diff] [blame] | 265 | pci_read_config_dword(_BE_env.vgaInfo.pcidev, |
| 266 | M.x86.R_DI, &M.x86.R_ECX); |
Simon Glass | d3e0c8f | 2016-01-17 16:11:09 -0700 | [diff] [blame^] | 267 | # endif |
Jason Jin | a63ce95 | 2007-07-06 08:34:56 +0800 | [diff] [blame] | 268 | #else |
| 269 | M.x86.R_ECX = |
| 270 | (u32) PCI_accessReg(M.x86.R_DI, 0, PCI_READ_DWORD, |
| 271 | _BE_env.vgaInfo.pciInfo); |
| 272 | #endif |
| 273 | } |
| 274 | CONDITIONAL_SET_FLAG((M.x86.R_AH != SUCCESSFUL), F_CF); |
| 275 | break; |
| 276 | case 0xB10B: /* Write configuration byte */ |
| 277 | M.x86.R_AH = BAD_REGISTER_NUMBER; |
| 278 | if (M.x86.R_BX == pciSlot) { |
| 279 | M.x86.R_AH = SUCCESSFUL; |
| 280 | #ifdef __KERNEL__ |
Simon Glass | d3e0c8f | 2016-01-17 16:11:09 -0700 | [diff] [blame^] | 281 | # ifdef CONFIG_DM_PCI |
| 282 | dm_pci_write_config8(_BE_env.vgaInfo.pcidev, |
| 283 | M.x86.R_DI, M.x86.R_CL); |
| 284 | # else |
Jason Jin | a63ce95 | 2007-07-06 08:34:56 +0800 | [diff] [blame] | 285 | pci_write_config_byte(_BE_env.vgaInfo.pcidev, |
| 286 | M.x86.R_DI, M.x86.R_CL); |
Simon Glass | d3e0c8f | 2016-01-17 16:11:09 -0700 | [diff] [blame^] | 287 | # endif |
Jason Jin | a63ce95 | 2007-07-06 08:34:56 +0800 | [diff] [blame] | 288 | #else |
| 289 | PCI_accessReg(M.x86.R_DI, M.x86.R_CL, PCI_WRITE_BYTE, |
| 290 | _BE_env.vgaInfo.pciInfo); |
| 291 | #endif |
| 292 | } |
| 293 | CONDITIONAL_SET_FLAG((M.x86.R_AH != SUCCESSFUL), F_CF); |
| 294 | break; |
| 295 | case 0xB10C: /* Write configuration word */ |
| 296 | M.x86.R_AH = BAD_REGISTER_NUMBER; |
| 297 | if (M.x86.R_BX == pciSlot) { |
| 298 | M.x86.R_AH = SUCCESSFUL; |
| 299 | #ifdef __KERNEL__ |
Simon Glass | d3e0c8f | 2016-01-17 16:11:09 -0700 | [diff] [blame^] | 300 | # ifdef CONFIG_DM_PCI |
| 301 | dm_pci_write_config32(_BE_env.vgaInfo.pcidev, |
| 302 | M.x86.R_DI, M.x86.R_CX); |
| 303 | # else |
Jason Jin | a63ce95 | 2007-07-06 08:34:56 +0800 | [diff] [blame] | 304 | pci_write_config_word(_BE_env.vgaInfo.pcidev, |
| 305 | M.x86.R_DI, M.x86.R_CX); |
Simon Glass | d3e0c8f | 2016-01-17 16:11:09 -0700 | [diff] [blame^] | 306 | # endif |
Jason Jin | a63ce95 | 2007-07-06 08:34:56 +0800 | [diff] [blame] | 307 | #else |
| 308 | PCI_accessReg(M.x86.R_DI, M.x86.R_CX, PCI_WRITE_WORD, |
| 309 | _BE_env.vgaInfo.pciInfo); |
| 310 | #endif |
| 311 | } |
| 312 | CONDITIONAL_SET_FLAG((M.x86.R_AH != SUCCESSFUL), F_CF); |
| 313 | break; |
| 314 | case 0xB10D: /* Write configuration dword */ |
| 315 | M.x86.R_AH = BAD_REGISTER_NUMBER; |
| 316 | if (M.x86.R_BX == pciSlot) { |
| 317 | M.x86.R_AH = SUCCESSFUL; |
| 318 | #ifdef __KERNEL__ |
Simon Glass | d3e0c8f | 2016-01-17 16:11:09 -0700 | [diff] [blame^] | 319 | # ifdef CONFIG_DM_PCI |
| 320 | dm_pci_write_config32(_BE_env.vgaInfo.pcidev, |
| 321 | M.x86.R_DI, M.x86.R_ECX); |
| 322 | # else |
Jason Jin | a63ce95 | 2007-07-06 08:34:56 +0800 | [diff] [blame] | 323 | pci_write_config_dword(_BE_env.vgaInfo.pcidev, |
| 324 | M.x86.R_DI, M.x86.R_ECX); |
Simon Glass | d3e0c8f | 2016-01-17 16:11:09 -0700 | [diff] [blame^] | 325 | # endif |
Jason Jin | a63ce95 | 2007-07-06 08:34:56 +0800 | [diff] [blame] | 326 | #else |
| 327 | PCI_accessReg(M.x86.R_DI, M.x86.R_ECX, PCI_WRITE_DWORD, |
| 328 | _BE_env.vgaInfo.pciInfo); |
| 329 | #endif |
| 330 | } |
| 331 | CONDITIONAL_SET_FLAG((M.x86.R_AH != SUCCESSFUL), F_CF); |
| 332 | break; |
| 333 | default: |
| 334 | printf("biosEmu/bios.int1a: unknown function AX=%#04x\n", |
| 335 | M.x86.R_AX); |
| 336 | } |
| 337 | } |
| 338 | |
| 339 | /**************************************************************************** |
| 340 | REMARKS: |
| 341 | This function initialises the BIOS emulation functions for the specific |
| 342 | PCI display device. We insulate the real mode BIOS from any other devices |
| 343 | on the bus, so that it will work correctly thinking that it is the only |
| 344 | device present on the bus (ie: avoiding any adapters present in from of |
| 345 | the device we are trying to control). |
| 346 | ****************************************************************************/ |
| 347 | #define BE_constLE_32(v) ((((((v)&0xff00)>>8)|(((v)&0xff)<<8))<<16)|(((((v)&0xff000000)>>8)|(((v)&0x00ff0000)<<8))>>16)) |
| 348 | |
| 349 | void _BE_bios_init(u32 * intrTab) |
| 350 | { |
| 351 | int i; |
| 352 | X86EMU_intrFuncs bios_intr_tab[256]; |
| 353 | |
| 354 | for (i = 0; i < 256; ++i) { |
| 355 | intrTab[i] = BE_constLE_32(BIOS_SEG << 16); |
| 356 | bios_intr_tab[i] = undefined_intr; |
| 357 | } |
| 358 | bios_intr_tab[0x10] = int10; |
| 359 | bios_intr_tab[0x1A] = int1A; |
| 360 | bios_intr_tab[0x42] = int42; |
| 361 | bios_intr_tab[0x6D] = int10; |
| 362 | X86EMU_setupIntrFuncs(bios_intr_tab); |
| 363 | } |