1 /* 2 * PROJECT: ReactOS Boot Video Driver for VGA-compatible cards 3 * LICENSE: GPL-2.0-or-later (https://spdx.org/licenses/GPL-2.0-or-later) 4 * PURPOSE: Main file 5 * COPYRIGHT: Copyright 2007 Alex Ionescu <alex.ionescu@reactos.org> 6 * Copyright 2019 Hermès Bélusca-Maïto <hermes.belusca-maito@reactos.org> 7 */ 8 9 #include "precomp.h" 10 11 /* PRIVATE FUNCTIONS *********************************************************/ 12 13 static BOOLEAN 14 VgaInterpretCmdStream( 15 _In_ PUSHORT CmdStream) 16 { 17 USHORT Cmd; 18 UCHAR Major, Minor; 19 USHORT Port; 20 USHORT Count; 21 UCHAR Index; 22 UCHAR Value; 23 USHORT ShortValue; 24 25 /* First make sure that we have a Command Stream */ 26 if (!CmdStream) return TRUE; 27 28 /* Loop as long as we have commands */ 29 while (*CmdStream != EOD) 30 { 31 /* Get the next command and its Major and Minor functions */ 32 Cmd = *CmdStream++; 33 Major = Cmd & 0xF0; 34 Minor = Cmd & 0x0F; 35 36 /* Check which major function this is */ 37 if (Major == INOUT) 38 { 39 /* Check the minor function */ 40 if (Minor & IO /* CMD_STREAM_READ */) 41 { 42 /* Check the sub-type */ 43 if (Minor & BW /* CMD_STREAM_USHORT */) 44 { 45 /* Get the port and read an USHORT from it */ 46 Port = *CmdStream++; 47 ShortValue = __inpw(Port); 48 } 49 else // if (Minor & CMD_STREAM_WRITE) 50 { 51 /* Get the port and read an UCHAR from it */ 52 Port = *CmdStream++; 53 Value = __inpb(Port); 54 } 55 } 56 else if (Minor & MULTI /* CMD_STREAM_WRITE_ARRAY */) 57 { 58 /* Check the sub-type */ 59 if (Minor & BW /* CMD_STREAM_USHORT */) 60 { 61 /* Get the port and the count of elements */ 62 Port = *CmdStream++; 63 Count = *CmdStream++; 64 65 /* Write the USHORT to the port; the buffer is what's in the command stream */ 66 WRITE_PORT_BUFFER_USHORT((PUSHORT)(VgaRegisterBase + Port), CmdStream, Count); 67 68 /* Move past the buffer in the command stream */ 69 CmdStream += Count; 70 } 71 else // if (Minor & CMD_STREAM_WRITE) 72 { 73 /* Get the port and the count of elements */ 74 Port = *CmdStream++; 75 Count = *CmdStream++; 76 77 /* Loop the command array */ 78 for (; Count; --Count, ++CmdStream) 79 { 80 /* Get the UCHAR and write it to the port */ 81 Value = (UCHAR)*CmdStream; 82 __outpb(Port, Value); 83 } 84 } 85 } 86 else if (Minor & BW /* CMD_STREAM_USHORT */) 87 { 88 /* Get the port */ 89 Port = *CmdStream++; 90 91 /* Get the USHORT and write it to the port */ 92 ShortValue = *CmdStream++; 93 __outpw(Port, ShortValue); 94 } 95 else // if (Minor & CMD_STREAM_WRITE) 96 { 97 /* Get the port */ 98 Port = *CmdStream++; 99 100 /* Get the UCHAR and write it to the port */ 101 Value = (UCHAR)*CmdStream++; 102 __outpb(Port, Value); 103 } 104 } 105 else if (Major == METAOUT) 106 { 107 /* Check the minor function. Note these are not flags. */ 108 switch (Minor) 109 { 110 case INDXOUT: 111 { 112 /* Get the port, the count of elements and the start index */ 113 Port = *CmdStream++; 114 Count = *CmdStream++; 115 Index = (UCHAR)*CmdStream++; 116 117 /* Loop the command array */ 118 for (; Count; --Count, ++Index, ++CmdStream) 119 { 120 /* Get the USHORT and write it to the port */ 121 ShortValue = (USHORT)Index + ((*CmdStream) << 8); 122 __outpw(Port, ShortValue); 123 } 124 break; 125 } 126 127 case ATCOUT: 128 { 129 /* Get the port, the count of elements and the start index */ 130 Port = *CmdStream++; 131 Count = *CmdStream++; 132 Index = (UCHAR)*CmdStream++; 133 134 /* Loop the command array */ 135 for (; Count; --Count, ++Index, ++CmdStream) 136 { 137 /* Write the index */ 138 __outpb(Port, Index); 139 140 /* Get the UCHAR and write it to the port */ 141 Value = (UCHAR)*CmdStream; 142 __outpb(Port, Value); 143 } 144 break; 145 } 146 147 case MASKOUT: 148 { 149 /* Get the port */ 150 Port = *CmdStream++; 151 152 /* Read the current value and add the stream data */ 153 Value = __inpb(Port); 154 Value &= *CmdStream++; 155 Value ^= *CmdStream++; 156 157 /* Write the value */ 158 __outpb(Port, Value); 159 break; 160 } 161 162 default: 163 /* Unknown command, fail */ 164 return FALSE; 165 } 166 } 167 else if (Major != NCMD) 168 { 169 /* Unknown major function, fail */ 170 return FALSE; 171 } 172 } 173 174 /* If we got here, return success */ 175 return TRUE; 176 } 177 178 static BOOLEAN 179 VgaIsPresent(VOID) 180 { 181 UCHAR OrgGCAddr, OrgReadMap, OrgBitMask; 182 UCHAR OrgSCAddr, OrgMemMode; 183 UCHAR i; 184 185 /* Remember the original state of the Graphics Controller Address register */ 186 OrgGCAddr = __inpb(VGA_BASE_IO_PORT + GRAPH_ADDRESS_PORT); 187 188 /* 189 * Write the Read Map register with a known state so we can verify 190 * that it isn't changed after we fool with the Bit Mask. This ensures 191 * that we're dealing with indexed registers, since both the Read Map and 192 * the Bit Mask are addressed at GRAPH_DATA_PORT. 193 */ 194 __outpb(VGA_BASE_IO_PORT + GRAPH_ADDRESS_PORT, IND_READ_MAP); 195 196 /* 197 * If we can't read back the Graphics Address register setting we just 198 * performed, it's not readable and this isn't a VGA. 199 */ 200 if ((__inpb(VGA_BASE_IO_PORT + GRAPH_ADDRESS_PORT) & GRAPH_ADDR_MASK) != IND_READ_MAP) 201 return FALSE; 202 203 /* 204 * Set the Read Map register to a known state. 205 */ 206 OrgReadMap = __inpb(VGA_BASE_IO_PORT + GRAPH_DATA_PORT); 207 __outpb(VGA_BASE_IO_PORT + GRAPH_DATA_PORT, READ_MAP_TEST_SETTING); 208 209 /* Read it back... it should be the same */ 210 if (__inpb(VGA_BASE_IO_PORT + GRAPH_DATA_PORT) != READ_MAP_TEST_SETTING) 211 { 212 /* 213 * The Read Map setting we just performed can't be read back; not a 214 * VGA. Restore the default Read Map state and fail. 215 */ 216 __outpb(VGA_BASE_IO_PORT + GRAPH_DATA_PORT, READ_MAP_DEFAULT); 217 return FALSE; 218 } 219 220 /* Remember the original setting of the Bit Mask register */ 221 __outpb(VGA_BASE_IO_PORT + GRAPH_ADDRESS_PORT, IND_BIT_MASK); 222 223 /* Read it back... it should be the same */ 224 if ((__inpb(VGA_BASE_IO_PORT + GRAPH_ADDRESS_PORT) & GRAPH_ADDR_MASK) != IND_BIT_MASK) 225 { 226 /* 227 * The Graphics Address register setting we just made can't be read 228 * back; not a VGA. Restore the default Read Map state and fail. 229 */ 230 __outpb(VGA_BASE_IO_PORT + GRAPH_ADDRESS_PORT, IND_READ_MAP); 231 __outpb(VGA_BASE_IO_PORT + GRAPH_DATA_PORT, READ_MAP_DEFAULT); 232 return FALSE; 233 } 234 235 /* Read the VGA Data Register */ 236 OrgBitMask = __inpb(VGA_BASE_IO_PORT + GRAPH_DATA_PORT); 237 238 /* 239 * Set up the initial test mask we'll write to and read from the Bit Mask, 240 * and loop on the bitmasks. 241 */ 242 for (i = 0xBB; i; i >>= 1) 243 { 244 /* Write the test mask to the Bit Mask */ 245 __outpb(VGA_BASE_IO_PORT + GRAPH_DATA_PORT, i); 246 247 /* Read it back... it should be the same */ 248 if (__inpb(VGA_BASE_IO_PORT + GRAPH_DATA_PORT) != i) 249 { 250 /* 251 * The Bit Mask is not properly writable and readable; not a VGA. 252 * Restore the Bit Mask and Read Map to their default states and fail. 253 */ 254 __outpb(VGA_BASE_IO_PORT + GRAPH_DATA_PORT, BIT_MASK_DEFAULT); 255 __outpb(VGA_BASE_IO_PORT + GRAPH_ADDRESS_PORT, IND_READ_MAP); 256 __outpb(VGA_BASE_IO_PORT + GRAPH_DATA_PORT, READ_MAP_DEFAULT); 257 return FALSE; 258 } 259 } 260 261 /* 262 * There's something readable at GRAPH_DATA_PORT; now switch back and 263 * make sure that the Read Map register hasn't changed, to verify that 264 * we're dealing with indexed registers. 265 */ 266 __outpb(VGA_BASE_IO_PORT + GRAPH_ADDRESS_PORT, IND_READ_MAP); 267 268 /* Read it back */ 269 if (__inpb(VGA_BASE_IO_PORT + GRAPH_DATA_PORT) != READ_MAP_TEST_SETTING) 270 { 271 /* 272 * The Read Map is not properly writable and readable; not a VGA. 273 * Restore the Bit Mask and Read Map to their default states, in case 274 * this is an EGA, so subsequent writes to the screen aren't garbled. 275 * Then fail. 276 */ 277 __outpb(VGA_BASE_IO_PORT + GRAPH_DATA_PORT, READ_MAP_DEFAULT); 278 __outpb(VGA_BASE_IO_PORT + GRAPH_ADDRESS_PORT, IND_BIT_MASK); 279 __outpb(VGA_BASE_IO_PORT + GRAPH_DATA_PORT, BIT_MASK_DEFAULT); 280 return FALSE; 281 } 282 283 /* 284 * We've pretty surely verified the existence of the Bit Mask register. 285 * Put the Graphics Controller back to the original state. 286 */ 287 __outpb(VGA_BASE_IO_PORT + GRAPH_DATA_PORT, OrgReadMap); 288 __outpb(VGA_BASE_IO_PORT + GRAPH_ADDRESS_PORT, IND_BIT_MASK); 289 __outpb(VGA_BASE_IO_PORT + GRAPH_DATA_PORT, OrgBitMask); 290 __outpb(VGA_BASE_IO_PORT + GRAPH_ADDRESS_PORT, OrgGCAddr); 291 292 /* 293 * Now, check for the existence of the Chain4 bit. 294 */ 295 296 /* 297 * Remember the original states of the Sequencer Address and Memory Mode 298 * registers. 299 */ 300 OrgSCAddr = __inpb(VGA_BASE_IO_PORT + SEQ_ADDRESS_PORT); 301 __outpb(VGA_BASE_IO_PORT + SEQ_ADDRESS_PORT, IND_MEMORY_MODE); 302 303 /* Read it back... it should be the same */ 304 if ((__inpb(VGA_BASE_IO_PORT + SEQ_ADDRESS_PORT) & SEQ_ADDR_MASK) != IND_MEMORY_MODE) 305 { 306 /* 307 * Couldn't read back the Sequencer Address register setting 308 * we just performed, fail. 309 */ 310 return FALSE; 311 } 312 313 /* Read sequencer Data */ 314 OrgMemMode = __inpb(VGA_BASE_IO_PORT + SEQ_DATA_PORT); 315 316 /* 317 * Toggle the Chain4 bit and read back the result. This must be done during 318 * sync reset, since we're changing the chaining state. 319 */ 320 321 /* Begin sync reset */ 322 __outpw(VGA_BASE_IO_PORT + SEQ_ADDRESS_PORT, (IND_SYNC_RESET + (START_SYNC_RESET_VALUE << 8))); 323 324 /* Toggle the Chain4 bit */ 325 __outpb(VGA_BASE_IO_PORT + SEQ_ADDRESS_PORT, IND_MEMORY_MODE); 326 __outpb(VGA_BASE_IO_PORT + SEQ_DATA_PORT, OrgMemMode ^ CHAIN4_MASK); 327 328 /* Read it back... it should be the same */ 329 if (__inpb(VGA_BASE_IO_PORT + SEQ_DATA_PORT) != (OrgMemMode ^ CHAIN4_MASK)) 330 { 331 /* 332 * Chain4 bit is not there, not a VGA. 333 * Set text mode default for Memory Mode register. 334 */ 335 __outpb(VGA_BASE_IO_PORT + SEQ_DATA_PORT, MEMORY_MODE_TEXT_DEFAULT); 336 337 /* End sync reset */ 338 __outpw(VGA_BASE_IO_PORT + SEQ_ADDRESS_PORT, (IND_SYNC_RESET + (END_SYNC_RESET_VALUE << 8))); 339 340 /* Fail */ 341 return FALSE; 342 } 343 344 /* 345 * It's a VGA. 346 */ 347 348 /* Restore the original Memory Mode setting */ 349 __outpb(VGA_BASE_IO_PORT + SEQ_DATA_PORT, OrgMemMode); 350 351 /* End sync reset */ 352 __outpw(VGA_BASE_IO_PORT + SEQ_ADDRESS_PORT, (IND_SYNC_RESET + (END_SYNC_RESET_VALUE << 8))); 353 354 /* Restore the original Sequencer Address setting */ 355 __outpb(VGA_BASE_IO_PORT + SEQ_ADDRESS_PORT, OrgSCAddr); 356 357 /* VGA is present! */ 358 return TRUE; 359 } 360 361 /* PUBLIC FUNCTIONS **********************************************************/ 362 363 BOOLEAN 364 NTAPI 365 VidInitialize( 366 _In_ BOOLEAN SetMode) 367 { 368 ULONG_PTR Context = 0; 369 PHYSICAL_ADDRESS TranslatedAddress; 370 PHYSICAL_ADDRESS NullAddress = {{0, 0}}, VgaAddress; 371 ULONG AddressSpace; 372 BOOLEAN Result; 373 ULONG_PTR Base; 374 375 /* Make sure that we have a bus translation function */ 376 if (!HalFindBusAddressTranslation) return FALSE; 377 378 /* Loop trying to find possible VGA base addresses */ 379 while (TRUE) 380 { 381 /* Get the VGA Register address */ 382 AddressSpace = 1; 383 Result = HalFindBusAddressTranslation(NullAddress, 384 &AddressSpace, 385 &TranslatedAddress, 386 &Context, 387 TRUE); 388 if (!Result) return FALSE; 389 390 /* See if this is I/O Space, which we need to map */ 391 if (!AddressSpace) 392 { 393 /* Map it */ 394 Base = (ULONG_PTR)MmMapIoSpace(TranslatedAddress, 0x400, MmNonCached); 395 } 396 else 397 { 398 /* The base is the translated address, no need to map I/O space */ 399 Base = TranslatedAddress.LowPart; 400 } 401 402 /* Try to see if this is VGA */ 403 VgaRegisterBase = Base; 404 if (VgaIsPresent()) 405 { 406 /* Translate the VGA Memory Address */ 407 VgaAddress.LowPart = MEM_VGA; 408 VgaAddress.HighPart = 0; 409 AddressSpace = 0; 410 Result = HalFindBusAddressTranslation(VgaAddress, 411 &AddressSpace, 412 &TranslatedAddress, 413 &Context, 414 FALSE); 415 if (Result) break; 416 } 417 else 418 { 419 /* It's not, so unmap the I/O space if we mapped it */ 420 if (!AddressSpace) MmUnmapIoSpace((PVOID)VgaRegisterBase, 0x400); 421 } 422 423 /* Continue trying to see if there's any other address */ 424 } 425 426 /* Success! See if this is I/O Space, which we need to map */ 427 if (!AddressSpace) 428 { 429 /* Map it */ 430 Base = (ULONG_PTR)MmMapIoSpace(TranslatedAddress, 431 MEM_VGA_SIZE, 432 MmNonCached); 433 } 434 else 435 { 436 /* The base is the translated address, no need to map I/O space */ 437 Base = TranslatedAddress.LowPart; 438 } 439 440 /* Set the VGA Memory Base */ 441 VgaBase = Base; 442 443 /* Now check if we have to set the mode */ 444 if (SetMode) 445 { 446 /* Clear the current position */ 447 VidpCurrentX = 0; 448 VidpCurrentY = 0; 449 450 /* Reset the display and initialize it */ 451 if (HalResetDisplay()) 452 { 453 /* The HAL handled the display, re-initialize only the AC registers */ 454 VgaInterpretCmdStream(AT_Initialization); 455 } 456 else 457 { 458 /* The HAL didn't handle the display, fully re-initialize the VGA */ 459 VgaInterpretCmdStream(VGA_640x480); 460 } 461 } 462 463 /* VGA is ready */ 464 return TRUE; 465 } 466 467 VOID 468 NTAPI 469 VidResetDisplay( 470 _In_ BOOLEAN HalReset) 471 { 472 /* Clear the current position */ 473 VidpCurrentX = 0; 474 VidpCurrentY = 0; 475 476 /* Clear the screen with HAL if we were asked to */ 477 if (HalReset) 478 { 479 if (!HalResetDisplay()) 480 { 481 /* The HAL didn't handle the display, fully re-initialize the VGA */ 482 VgaInterpretCmdStream(VGA_640x480); 483 } 484 } 485 486 /* Always re-initialize the AC registers */ 487 VgaInterpretCmdStream(AT_Initialization); 488 489 /* Re-initialize the palette and fill the screen black */ 490 InitializePalette(); 491 VidSolidColorFill(0, 0, SCREEN_WIDTH - 1, SCREEN_HEIGHT - 1, BV_COLOR_BLACK); 492 } 493