OpenCore  1.0.4
OpenCore Bootloader
Loading...
Searching...
No Matches
GopUtils.c
Go to the documentation of this file.
1
9
10#include <Base.h>
11#include <Uefi/UefiBaseType.h>
12
15
16#include <Library/BaseLib.h>
17#include <Library/BaseOverflowLib.h>
18#include <Library/DebugLib.h>
19#include <Library/MemoryAllocationLib.h>
20#include <Library/MtrrLib.h>
21#include <Library/OcMemoryLib.h>
22#include <Library/OcStringLib.h>
23#include <Library/PrintLib.h>
24#include <Library/UefiBootServicesTableLib.h>
25
26#define PAT_INDEX_TO_CHANGE 7
27
28EFI_STATUS
30 IN EFI_GRAPHICS_OUTPUT_PROTOCOL_MODE *Mode,
31 OUT UINTN *BytesPerPixel
32 )
33{
34 UINT32 MergedMasks;
35
36 if ( (Mode == NULL)
37 || (Mode->Info == NULL))
38 {
39 ASSERT (
40 (Mode != NULL)
41 && (Mode->Info != NULL)
42 );
43 return EFI_UNSUPPORTED;
44 }
45
46 //
47 // This can occur without PixelBltOnly, including in rotated DirectGopRendering -
48 // see comment about PixelFormat in ConsoleGop.c RotateMode method.
49 //
50 if (Mode->FrameBufferBase == 0ULL) {
51 return EFI_UNSUPPORTED;
52 }
53
54 switch (Mode->Info->PixelFormat) {
55 case PixelRedGreenBlueReserved8BitPerColor:
56 case PixelBlueGreenRedReserved8BitPerColor:
57 *BytesPerPixel = sizeof (EFI_GRAPHICS_OUTPUT_BLT_PIXEL);
58 return EFI_SUCCESS;
59
60 case PixelBitMask:
61 break;
62
63 case PixelBltOnly:
64 return EFI_UNSUPPORTED;
65
66 default:
67 return EFI_INVALID_PARAMETER;
68 }
69
70 MergedMasks = Mode->Info->PixelInformation.RedMask
71 || Mode->Info->PixelInformation.GreenMask
72 || Mode->Info->PixelInformation.BlueMask
73 || Mode->Info->PixelInformation.ReservedMask;
74
75 if (MergedMasks == 0) {
76 return EFI_INVALID_PARAMETER;
77 }
78
79 *BytesPerPixel = (UINT32)((HighBitSet32 (MergedMasks) + 7) / 8);
80
81 return EFI_SUCCESS;
82}
83
84EFI_STATUS
86 IN EFI_GRAPHICS_OUTPUT_PROTOCOL_MODE *Mode,
87 OUT UINTN *FrameBufferSize
88 )
89{
90 EFI_STATUS Status;
91 UINTN BytesPerPixel;
92
93 if ( (Mode == NULL)
94 || (Mode->Info == NULL))
95 {
96 ASSERT (
97 (Mode != NULL)
98 && (Mode->Info != NULL)
99 );
100 return EFI_UNSUPPORTED;
101 }
102
103 Status = OcGopModeBytesPerPixel (Mode, &BytesPerPixel);
104
105 if (EFI_ERROR (Status)) {
106 return Status;
107 }
108
109 if (BaseOverflowTriMulUN (
110 Mode->Info->PixelsPerScanLine,
111 Mode->Info->VerticalResolution,
112 BytesPerPixel,
113 FrameBufferSize
114 ))
115 {
116 return EFI_INVALID_PARAMETER;
117 }
118
119 return EFI_SUCCESS;
120}
121
122//
123// Use PAT to enable write-combining caching (burst mode) on GOP memory,
124// when it is suppported but firmware has not set it up.
125//
126STATIC
127EFI_STATUS
129 EFI_GRAPHICS_OUTPUT_PROTOCOL *Gop,
130 BOOLEAN SetPatWC
131 )
132{
133 EFI_STATUS Status;
134 MTRR_MEMORY_CACHE_TYPE MtrrCacheType;
136 EFI_VIRTUAL_ADDRESS VirtualAddress;
137 EFI_PHYSICAL_ADDRESS PhysicalAddress;
138 UINTN FrameBufferSize;
139 UINT64 Bits;
140 UINT8 Level;
141 BOOLEAN HasMtrr;
142 BOOLEAN HasPat;
143 UINT64 PatMsr;
144 UINT64 ModifiedPatMsr;
145 PAT_INDEX PatIndex;
146 BOOLEAN HasDefaultPat;
147 BOOLEAN AlreadySet;
148
149 if (!SetPatWC) {
150 return EFI_SUCCESS;
151 }
152
153 if ( (Gop == NULL)
154 || (Gop->Mode == NULL)
155 || (Gop->Mode->Info == NULL))
156 {
157 ASSERT (
158 (Gop != NULL)
159 && (Gop->Mode != NULL)
160 && (Gop->Mode->Info != NULL)
161 );
162 return EFI_UNSUPPORTED;
163 }
164
165 Status = OcGopModeSafeFrameBufferSize (Gop->Mode, &FrameBufferSize);
166 if (EFI_ERROR (Status)) {
167 return Status;
168 }
169
170 HasMtrr = IsMtrrSupported ();
171 HasPat = OcIsPatSupported ();
172
173 DEBUG ((
174 DEBUG_INFO,
175 "OCC: MTRR %asupported, PAT %asupported\n",
176 HasMtrr ? "" : "not ",
177 HasPat ? "" : "not "
178 ));
179
180 if (!HasMtrr || !HasPat) {
181 return EFI_UNSUPPORTED;
182 }
183
184 AlreadySet = FALSE;
185 PageTable = OcGetCurrentPageTable (NULL);
186
187 do {
188 PatMsr = AsmReadMsr64 (MSR_IA32_CR_PAT);
189 HasDefaultPat = (PatMsr == PAT_DEFAULTS);
190
191 DEBUG ((
192 DEBUG_INFO,
193 "OCC: PAT 0x%016LX (%a)\n",
194 PatMsr,
195 HasDefaultPat ? "default" : "not default"
196 ));
197
198 MtrrCacheType = MtrrGetMemoryAttribute (Gop->Mode->FrameBufferBase);
199
200 VirtualAddress = Gop->Mode->FrameBufferBase;
201
203 PageTable,
204 VirtualAddress,
205 &PhysicalAddress,
206 &Level,
207 &Bits,
208 &PatIndex,
209 FALSE
210 );
211
212 DEBUG ((
213 DEBUG_INFO,
214 "OCC: 0x%LX MTRR %u=%a PTE%u bits 0x%016LX PAT@%u->%u=%a - %r\n",
215 VirtualAddress,
216 MtrrCacheType,
217 OcGetMtrrTypeString (MtrrCacheType),
218 Level,
219 Bits,
220 PatIndex.Index,
221 GET_PAT_N (PatMsr, PatIndex.Index),
222 OcGetPatTypeString (GET_PAT_N (PatMsr, PatIndex.Index)),
223 Status
224 ));
225
226 if (EFI_ERROR (Status)) {
227 ASSERT (!AlreadySet);
228 return Status;
229 }
230
231 ASSERT (VirtualAddress == PhysicalAddress);
232
233 if (AlreadySet) {
234 break;
235 }
236
237 //
238 // Attempting to set again if set in PAT works on some systems (including if set
239 // before by us) but fails with a hang on some others, so avoid it even though we
240 // might otherwise prefer to make sure to set the whole memory for the current mode.
241 // Definitely no need to set again if set in MTRR.
242 //
243 if ( (MtrrCacheType == CacheWriteCombining)
244 || (GET_PAT_N (PatMsr, PatIndex.Index) == PatWriteCombining)
245 )
246 {
247 return EFI_ALREADY_STARTED;
248 }
249
250 //
251 // We cannot split MTRR ranges (returns out of resources, meaning there are not
252 // enough MTRRs to keep the existing mapping and insert a new one), so set up
253 // PAT7 as write combining (WC) and modify the page tables instead.
254 //
255 if (HasDefaultPat) {
256 //
257 // Modify PAT MSR to add WC entry somewhere reasonable (not first four entries,
258 // and should use an entry likely to be unused) in PAT MSR.
259 // TODO: Could fully check existing page tables to confirm PAT7 is unused.
260 //
261 ModifiedPatMsr = MODIFY_PAT_MSR (PatMsr, PAT_INDEX_TO_CHANGE, PatWriteCombining);
262 DEBUG ((DEBUG_INFO, "OCC: Writing PAT 0x%016LX\n", ModifiedPatMsr));
263 AsmWriteMsr64 (MSR_IA32_CR_PAT, ModifiedPatMsr);
264
265 PatIndex.Index = PAT_INDEX_TO_CHANGE;
266 } else {
267 //
268 // Do not modify non-default PAT MSR, but use WC if it already exists there
269 // (including previously set by us, if GOP mode changes more than once).
270 //
271 for (PatIndex.Index = 0; PatIndex.Index < PAT_INDEX_MAX; PatIndex.Index++) {
272 if (GET_PAT_N (PatMsr, PatIndex.Index) == PatWriteCombining) {
273 break;
274 }
275 }
276
277 if (PatIndex.Index == PAT_INDEX_MAX) {
278 Status = EFI_UNSUPPORTED;
279 DEBUG ((DEBUG_INFO, "OCC: Non-default PAT MSR with no WC entry - %r\n", Status));
280 return Status;
281 }
282 }
283
284 //
285 // TODO: Use full GOP memory range not just range in use for current mode?
286 //
288 PageTable,
289 Gop->Mode->FrameBufferBase,
290 FrameBufferSize,
291 &PatIndex
292 );
293
294 if (EFI_ERROR (Status)) {
295 DEBUG ((DEBUG_ERROR, "OCC: Failed to set PAT index for range - %r\n", Status));
296 return Status;
297 }
298
299 DEBUG_CODE_BEGIN ();
300 AlreadySet = TRUE;
301 DEBUG_CODE_END ();
302 } while (AlreadySet);
303
304 return EFI_SUCCESS;
305}
306
307EFI_STATUS
309 VOID
310 )
311{
312 EFI_STATUS Status;
313 EFI_GRAPHICS_OUTPUT_PROTOCOL *Gop;
314
315 Status = gBS->HandleProtocol (
316 gST->ConsoleOutHandle,
318 (VOID **)&Gop
319 );
320
321 if (EFI_ERROR (Status)) {
322 DEBUG ((DEBUG_INFO, "OCC: No console GOP found for burst mode - %r\n", Status));
323 return Status;
324 }
325
326 Status = WriteCombineGop (Gop, TRUE);
327 if (EFI_ERROR (Status)) {
328 DEBUG ((
329 (Status == EFI_ALREADY_STARTED) ? DEBUG_INFO : DEBUG_WARN,
330 "OCC: Failed to set burst mode - %r\n",
331 Status
332 ));
333 }
334
335 return Status;
336}
EFI_STATUS OcSetGopBurstMode(VOID)
Definition GopUtils.c:308
EFI_STATUS OcGopModeBytesPerPixel(IN EFI_GRAPHICS_OUTPUT_PROTOCOL_MODE *Mode, OUT UINTN *BytesPerPixel)
Definition GopUtils.c:29
EFI_STATUS OcGopModeSafeFrameBufferSize(IN EFI_GRAPHICS_OUTPUT_PROTOCOL_MODE *Mode, OUT UINTN *FrameBufferSize)
Definition GopUtils.c:85
#define PAT_INDEX_TO_CHANGE
Definition GopUtils.c:26
STATIC EFI_STATUS WriteCombineGop(EFI_GRAPHICS_OUTPUT_PROTOCOL *Gop, BOOLEAN SetPatWC)
Definition GopUtils.c:128
EFI_SYSTEM_TABLE * gST
EFI_BOOT_SERVICES * gBS
PAGE_MAP_AND_DIRECTORY_POINTER * OcGetCurrentPageTable(OUT UINTN *Flags OPTIONAL)
EFI_STATUS OcSetPatIndexForAddressRange(IN PAGE_MAP_AND_DIRECTORY_POINTER *PageTable OPTIONAL, IN EFI_VIRTUAL_ADDRESS VirtualAddr, IN UINT64 Length, IN PAT_INDEX *PatIndex)
BOOLEAN OcIsPatSupported(VOID)
CHAR8 * OcGetPatTypeString(UINT8 PatType)
EFI_STATUS OcGetSetPageTableInfoForAddress(IN PAGE_MAP_AND_DIRECTORY_POINTER *PageTable OPTIONAL, IN EFI_VIRTUAL_ADDRESS VirtualAddr, OUT EFI_PHYSICAL_ADDRESS *PhysicalAddr OPTIONAL, OUT UINT8 *Level OPTIONAL, OUT UINT64 *Bits OPTIONAL, IN OUT PAT_INDEX *PatIndex OPTIONAL, IN BOOLEAN SetPat)
CHAR8 * OcGetMtrrTypeString(UINT8 MtrrType)
#define GET_PAT_N(PatMsr, PatIndex)
#define MSR_IA32_CR_PAT
@ PatWriteCombining
Special hardware burst mode (not L1-L3) intended for graphics memory (ref. 11.3.1).
#define PAT_INDEX_MAX
#define PAT_DEFAULTS
#define MODIFY_PAT_MSR(PatMsr, PatIndex, PatType)
EFI_GUID gEfiGraphicsOutputProtocolGuid
UINT64 EFIAPI AsmReadMsr64(IN UINT32 Index)
Definition UserMisc.c:223
UINT64 EFIAPI AsmWriteMsr64(IN UINT32 Index, IN UINT64 Value)
Definition UserMisc.c:250
#define ASSERT(x)
Definition coder.h:55