xref: /reactos/drivers/base/bootvid/i386/pc/bootvid.c (revision 3b62a89d)
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
VgaInterpretCmdStream(_In_ PUSHORT CmdStream)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
VgaIsPresent(VOID)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
VidInitialize(_In_ BOOLEAN SetMode)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
VidResetDisplay(_In_ BOOLEAN HalReset)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