[CRT] Remove useless #undef abort from process.h
[reactos.git] / ntoskrnl / inbv / inbv.c
1 /*
2 * PROJECT: ReactOS Kernel
3 * LICENSE: BSD - See COPYING.ARM in the top level directory
4 * PURPOSE: Boot Video Driver support
5 * COPYRIGHT: Copyright 2007 Alex Ionescu (alex.ionescu@reactos.org)
6 * Copyright 2010 Aleksey Bragin (aleksey@reactos.org)
7 * Copyright 2015-2022 Hermès Bélusca-Maïto
8 */
9
10 /* INCLUDES ******************************************************************/
11
12 #include <ntoskrnl.h>
13 #include "inbv/logo.h"
14
15 /* GLOBALS *******************************************************************/
16
17 /*
18 * Enable this define if you want Inbv to use coloured headless mode.
19 */
20 // #define INBV_HEADLESS_COLORS
21
22 typedef struct _INBV_PROGRESS_STATE
23 {
24 ULONG Floor;
25 ULONG Ceiling;
26 ULONG Bias;
27 } INBV_PROGRESS_STATE;
28
29 typedef struct _BT_PROGRESS_INDICATOR
30 {
31 ULONG Count;
32 ULONG Expected;
33 ULONG Percentage;
34 } BT_PROGRESS_INDICATOR, *PBT_PROGRESS_INDICATOR;
35
36 static KSPIN_LOCK BootDriverLock;
37 static KIRQL InbvOldIrql;
38 static INBV_DISPLAY_STATE InbvDisplayState = INBV_DISPLAY_STATE_DISABLED;
39 BOOLEAN InbvBootDriverInstalled = FALSE;
40 static INBV_RESET_DISPLAY_PARAMETERS InbvResetDisplayParameters = NULL;
41
42 static BOOLEAN InbvDisplayDebugStrings = FALSE;
43 static INBV_DISPLAY_STRING_FILTER InbvDisplayFilter = NULL;
44
45 ULONG ProgressBarLeft = 0, ProgressBarTop = 0;
46 BOOLEAN ShowProgressBar = FALSE;
47 static INBV_PROGRESS_STATE InbvProgressState;
48 static BT_PROGRESS_INDICATOR InbvProgressIndicator = {0, 25, 0};
49
50 static ULONG ResourceCount = 0;
51 static PUCHAR ResourceList[1 + IDB_MAX_RESOURCES]; // First entry == NULL, followed by 'ResourceCount' entries.
52
53
54 /*
55 * Headless terminal text colors
56 */
57
58 #ifdef INBV_HEADLESS_COLORS
59
60 // Conversion table CGA to ANSI color index
61 static const UCHAR CGA_TO_ANSI_COLOR_TABLE[16] =
62 {
63 0, // Black
64 4, // Blue
65 2, // Green
66 6, // Cyan
67 1, // Red
68 5, // Magenta
69 3, // Brown/Yellow
70 7, // Grey/White
71
72 60, // Bright Black
73 64, // Bright Blue
74 62, // Bright Green
75 66, // Bright Cyan
76 61, // Bright Red
77 65, // Bright Magenta
78 63, // Bright Yellow
79 67 // Bright Grey (White)
80 };
81
82 #define CGA_TO_ANSI_COLOR(CgaColor) \
83 CGA_TO_ANSI_COLOR_TABLE[CgaColor & 0x0F]
84
85 #endif
86
87 // Default colors: text in white, background in black
88 static ULONG InbvTerminalTextColor = 37;
89 static ULONG InbvTerminalBkgdColor = 40;
90
91
92 /* FUNCTIONS *****************************************************************/
93
94 CODE_SEG("INIT")
95 static
96 PVOID
97 FindBitmapResource(
98 _In_ PLOADER_PARAMETER_BLOCK LoaderBlock,
99 _In_ ULONG ResourceId)
100 {
101 UNICODE_STRING UpString = RTL_CONSTANT_STRING(L"ntoskrnl.exe");
102 UNICODE_STRING MpString = RTL_CONSTANT_STRING(L"ntkrnlmp.exe");
103 PLIST_ENTRY NextEntry, ListHead;
104 PLDR_DATA_TABLE_ENTRY LdrEntry;
105 PIMAGE_RESOURCE_DATA_ENTRY ResourceDataEntry;
106 LDR_RESOURCE_INFO ResourceInfo;
107 NTSTATUS Status;
108 PVOID Data = NULL;
109
110 /* Loop the driver list */
111 ListHead = &LoaderBlock->LoadOrderListHead;
112 for (NextEntry = ListHead->Flink;
113 NextEntry != ListHead;
114 NextEntry = NextEntry->Flink)
115 {
116 /* Get the entry */
117 LdrEntry = CONTAINING_RECORD(NextEntry,
118 LDR_DATA_TABLE_ENTRY,
119 InLoadOrderLinks);
120
121 /* Check for a match */
122 if (RtlEqualUnicodeString(&LdrEntry->BaseDllName, &UpString, TRUE) ||
123 RtlEqualUnicodeString(&LdrEntry->BaseDllName, &MpString, TRUE))
124 {
125 /* Break out */
126 break;
127 }
128 }
129
130 /* Check if we found it */
131 if (NextEntry != ListHead)
132 {
133 /* Try to find the resource */
134 ResourceInfo.Type = 2; // RT_BITMAP;
135 ResourceInfo.Name = ResourceId;
136 ResourceInfo.Language = 0;
137 Status = LdrFindResource_U(LdrEntry->DllBase,
138 &ResourceInfo,
139 RESOURCE_DATA_LEVEL,
140 &ResourceDataEntry);
141 if (NT_SUCCESS(Status))
142 {
143 /* Access the resource */
144 ULONG Size = 0;
145 Status = LdrAccessResource(LdrEntry->DllBase,
146 ResourceDataEntry,
147 &Data,
148 &Size);
149 if ((Data) && (ResourceId < 3))
150 {
151 KiBugCheckData[4] ^= RtlComputeCrc32(0, Data, Size);
152 }
153 if (!NT_SUCCESS(Status)) Data = NULL;
154 }
155 }
156
157 /* Return the pointer */
158 return Data;
159 }
160
161 PUCHAR
162 NTAPI
163 InbvGetResourceAddress(
164 _In_ ULONG ResourceNumber)
165 {
166 /* Validate the resource number */
167 if (ResourceNumber > ResourceCount) return NULL;
168
169 /* Return the address */
170 return ResourceList[ResourceNumber];
171 }
172
173 CODE_SEG("INIT")
174 BOOLEAN
175 NTAPI
176 InbvDriverInitialize(
177 _In_ PLOADER_PARAMETER_BLOCK LoaderBlock,
178 _In_ ULONG Count)
179 {
180 PCHAR CommandLine;
181 BOOLEAN ResetMode = FALSE; // By default do not reset the video mode
182 ULONG i;
183
184 /* Quit if we're already installed */
185 if (InbvBootDriverInstalled) return TRUE;
186
187 /* Initialize the lock and check the current display state */
188 KeInitializeSpinLock(&BootDriverLock);
189 if (InbvDisplayState == INBV_DISPLAY_STATE_OWNED)
190 {
191 /* Reset the video mode in case we do not have a custom boot logo */
192 CommandLine = (LoaderBlock->LoadOptions ? _strupr(LoaderBlock->LoadOptions) : NULL);
193 ResetMode = (CommandLine == NULL) || (strstr(CommandLine, "BOOTLOGO") == NULL);
194 }
195
196 /* Initialize the video */
197 InbvBootDriverInstalled = VidInitialize(ResetMode);
198 if (InbvBootDriverInstalled)
199 {
200 /* Find bitmap resources in the kernel */
201 ResourceCount = min(Count, RTL_NUMBER_OF(ResourceList) - 1);
202 for (i = 1; i <= ResourceCount; i++)
203 {
204 /* Do the lookup */
205 ResourceList[i] = FindBitmapResource(LoaderBlock, i);
206 }
207
208 /* Set the progress bar ranges */
209 InbvSetProgressBarSubset(0, 100);
210
211 // BootAnimInitialize(LoaderBlock, Count);
212 }
213
214 /* Return install state */
215 return InbvBootDriverInstalled;
216 }
217
218 VOID
219 NTAPI
220 InbvAcquireLock(VOID)
221 {
222 KIRQL OldIrql;
223
224 /* Check if we're at dispatch level or lower */
225 OldIrql = KeGetCurrentIrql();
226 if (OldIrql <= DISPATCH_LEVEL)
227 {
228 /* Loop until the lock is free */
229 while (!KeTestSpinLock(&BootDriverLock));
230
231 /* Raise IRQL to dispatch level */
232 KeRaiseIrql(DISPATCH_LEVEL, &OldIrql);
233 }
234
235 /* Acquire the lock */
236 KiAcquireSpinLock(&BootDriverLock);
237 InbvOldIrql = OldIrql;
238 }
239
240 VOID
241 NTAPI
242 InbvReleaseLock(VOID)
243 {
244 KIRQL OldIrql;
245
246 /* Capture the old IRQL */
247 OldIrql = InbvOldIrql;
248
249 /* Release the driver lock */
250 KiReleaseSpinLock(&BootDriverLock);
251
252 /* If we were at dispatch level or lower, restore the old IRQL */
253 if (InbvOldIrql <= DISPATCH_LEVEL) KeLowerIrql(OldIrql);
254 }
255
256 VOID
257 NTAPI
258 InbvEnableBootDriver(
259 _In_ BOOLEAN Enable)
260 {
261 /* Check if we're installed */
262 if (InbvBootDriverInstalled)
263 {
264 /* Check for lost state */
265 if (InbvDisplayState >= INBV_DISPLAY_STATE_LOST) return;
266
267 /* Acquire the lock */
268 InbvAcquireLock();
269
270 /* Cleanup the screen if we own it */
271 if (InbvDisplayState == INBV_DISPLAY_STATE_OWNED) VidCleanUp();
272
273 /* Set the new display state */
274 InbvDisplayState = Enable ? INBV_DISPLAY_STATE_OWNED :
275 INBV_DISPLAY_STATE_DISABLED;
276
277 /* Release the lock */
278 InbvReleaseLock();
279 }
280 else
281 {
282 /* Set the new display state */
283 InbvDisplayState = Enable ? INBV_DISPLAY_STATE_OWNED :
284 INBV_DISPLAY_STATE_DISABLED;
285 }
286 }
287
288 VOID
289 NTAPI
290 InbvAcquireDisplayOwnership(VOID)
291 {
292 /* Check if we have a callback and we're just acquiring it now */
293 if ((InbvResetDisplayParameters) &&
294 (InbvDisplayState == INBV_DISPLAY_STATE_LOST))
295 {
296 /* Call the callback */
297 InbvResetDisplayParameters(80, 50);
298 }
299
300 /* Acquire the display */
301 InbvDisplayState = INBV_DISPLAY_STATE_OWNED;
302 }
303
304 VOID
305 NTAPI
306 InbvSetDisplayOwnership(
307 _In_ BOOLEAN DisplayOwned)
308 {
309 /* Set the new display state */
310 InbvDisplayState = DisplayOwned ? INBV_DISPLAY_STATE_OWNED :
311 INBV_DISPLAY_STATE_LOST;
312 }
313
314 BOOLEAN
315 NTAPI
316 InbvCheckDisplayOwnership(VOID)
317 {
318 /* Return if we own it or not */
319 return InbvDisplayState != INBV_DISPLAY_STATE_LOST;
320 }
321
322 INBV_DISPLAY_STATE
323 NTAPI
324 InbvGetDisplayState(VOID)
325 {
326 /* Return the actual state */
327 return InbvDisplayState;
328 }
329
330 BOOLEAN
331 NTAPI
332 InbvDisplayString(
333 _In_ PCHAR String)
334 {
335 /* Make sure we own the display */
336 if (InbvDisplayState == INBV_DISPLAY_STATE_OWNED)
337 {
338 /* If we're not allowed, return success anyway */
339 if (!InbvDisplayDebugStrings) return TRUE;
340
341 /* Check if a filter is installed */
342 if (InbvDisplayFilter) InbvDisplayFilter(&String);
343
344 /* Acquire the lock */
345 InbvAcquireLock();
346
347 /* Make sure we're installed and display the string */
348 if (InbvBootDriverInstalled) VidDisplayString((PUCHAR)String);
349
350 /* Print the string on the EMS port */
351 HeadlessDispatch(HeadlessCmdPutString,
352 String,
353 strlen(String) + sizeof(ANSI_NULL),
354 NULL,
355 NULL);
356
357 /* Release the lock */
358 InbvReleaseLock();
359
360 /* All done */
361 return TRUE;
362 }
363
364 /* We don't own it, fail */
365 return FALSE;
366 }
367
368 BOOLEAN
369 NTAPI
370 InbvEnableDisplayString(
371 _In_ BOOLEAN Enable)
372 {
373 BOOLEAN OldSetting;
374
375 /* Get the old setting */
376 OldSetting = InbvDisplayDebugStrings;
377
378 /* Update it */
379 InbvDisplayDebugStrings = Enable;
380
381 /* Return the old setting */
382 return OldSetting;
383 }
384
385 VOID
386 NTAPI
387 InbvInstallDisplayStringFilter(
388 _In_ INBV_DISPLAY_STRING_FILTER DisplayFilter)
389 {
390 /* Save the filter */
391 InbvDisplayFilter = DisplayFilter;
392 }
393
394 BOOLEAN
395 NTAPI
396 InbvIsBootDriverInstalled(VOID)
397 {
398 /* Return driver state */
399 return InbvBootDriverInstalled;
400 }
401
402 VOID
403 NTAPI
404 InbvNotifyDisplayOwnershipLost(
405 _In_ INBV_RESET_DISPLAY_PARAMETERS Callback)
406 {
407 /* Check if we're installed */
408 if (InbvBootDriverInstalled)
409 {
410 /* Acquire the lock and cleanup if we own the screen */
411 InbvAcquireLock();
412 if (InbvDisplayState != INBV_DISPLAY_STATE_LOST) VidCleanUp();
413
414 /* Set the reset callback and display state */
415 InbvResetDisplayParameters = Callback;
416 InbvDisplayState = INBV_DISPLAY_STATE_LOST;
417
418 /* Release the lock */
419 InbvReleaseLock();
420 }
421 else
422 {
423 /* Set the reset callback and display state */
424 InbvResetDisplayParameters = Callback;
425 InbvDisplayState = INBV_DISPLAY_STATE_LOST;
426 }
427 }
428
429 BOOLEAN
430 NTAPI
431 InbvResetDisplay(VOID)
432 {
433 /* Check if we're installed and we own it */
434 if (InbvBootDriverInstalled &&
435 (InbvDisplayState == INBV_DISPLAY_STATE_OWNED))
436 {
437 /* Do the reset */
438 VidResetDisplay(TRUE);
439 return TRUE;
440 }
441
442 /* Nothing to reset */
443 return FALSE;
444 }
445
446 VOID
447 NTAPI
448 InbvSetScrollRegion(
449 _In_ ULONG Left,
450 _In_ ULONG Top,
451 _In_ ULONG Right,
452 _In_ ULONG Bottom)
453 {
454 /* Just call bootvid */
455 VidSetScrollRegion(Left, Top, Right, Bottom);
456 }
457
458 VOID
459 NTAPI
460 InbvSetTextColor(
461 _In_ ULONG Color)
462 {
463 HEADLESS_CMD_SET_COLOR HeadlessSetColor;
464
465 /* Set color for EMS port */
466 #ifdef INBV_HEADLESS_COLORS
467 InbvTerminalTextColor = 30 + CGA_TO_ANSI_COLOR(Color);
468 #else
469 InbvTerminalTextColor = 37;
470 #endif
471 HeadlessSetColor.TextColor = InbvTerminalTextColor;
472 HeadlessSetColor.BkgdColor = InbvTerminalBkgdColor;
473 HeadlessDispatch(HeadlessCmdSetColor,
474 &HeadlessSetColor,
475 sizeof(HeadlessSetColor),
476 NULL,
477 NULL);
478
479 /* Update the text color */
480 VidSetTextColor(Color);
481 }
482
483 VOID
484 NTAPI
485 InbvSolidColorFill(
486 _In_ ULONG Left,
487 _In_ ULONG Top,
488 _In_ ULONG Right,
489 _In_ ULONG Bottom,
490 _In_ ULONG Color)
491 {
492 HEADLESS_CMD_SET_COLOR HeadlessSetColor;
493
494 /* Make sure we own it */
495 if (InbvDisplayState == INBV_DISPLAY_STATE_OWNED)
496 {
497 /* Acquire the lock */
498 InbvAcquireLock();
499
500 /* Check if we're installed */
501 if (InbvBootDriverInstalled)
502 {
503 /* Call bootvid */
504 VidSolidColorFill(Left, Top, Right, Bottom, (UCHAR)Color);
505 }
506
507 /* Set color for EMS port and clear display */
508 #ifdef INBV_HEADLESS_COLORS
509 InbvTerminalBkgdColor = 40 + CGA_TO_ANSI_COLOR(Color);
510 #else
511 InbvTerminalBkgdColor = 40;
512 #endif
513 HeadlessSetColor.TextColor = InbvTerminalTextColor;
514 HeadlessSetColor.BkgdColor = InbvTerminalBkgdColor;
515 HeadlessDispatch(HeadlessCmdSetColor,
516 &HeadlessSetColor,
517 sizeof(HeadlessSetColor),
518 NULL,
519 NULL);
520 HeadlessDispatch(HeadlessCmdClearDisplay,
521 NULL, 0,
522 NULL, NULL);
523
524 /* Release the lock */
525 InbvReleaseLock();
526 }
527 }
528
529 VOID
530 NTAPI
531 InbvBitBlt(
532 _In_ PUCHAR Buffer,
533 _In_ ULONG X,
534 _In_ ULONG Y)
535 {
536 /* Check if we're installed and we own it */
537 if (InbvBootDriverInstalled &&
538 (InbvDisplayState == INBV_DISPLAY_STATE_OWNED))
539 {
540 /* Acquire the lock */
541 InbvAcquireLock();
542
543 /* Do the blit */
544 VidBitBlt(Buffer, X, Y);
545
546 /* Release the lock */
547 InbvReleaseLock();
548 }
549 }
550
551 VOID
552 NTAPI
553 InbvBufferToScreenBlt(
554 _In_ PUCHAR Buffer,
555 _In_ ULONG X,
556 _In_ ULONG Y,
557 _In_ ULONG Width,
558 _In_ ULONG Height,
559 _In_ ULONG Delta)
560 {
561 /* Check if we're installed and we own it */
562 if (InbvBootDriverInstalled &&
563 (InbvDisplayState == INBV_DISPLAY_STATE_OWNED))
564 {
565 /* Do the blit */
566 VidBufferToScreenBlt(Buffer, X, Y, Width, Height, Delta);
567 }
568 }
569
570 VOID
571 NTAPI
572 InbvScreenToBufferBlt(
573 _Out_ PUCHAR Buffer,
574 _In_ ULONG X,
575 _In_ ULONG Y,
576 _In_ ULONG Width,
577 _In_ ULONG Height,
578 _In_ ULONG Delta)
579 {
580 /* Check if we're installed and we own it */
581 if (InbvBootDriverInstalled &&
582 (InbvDisplayState == INBV_DISPLAY_STATE_OWNED))
583 {
584 /* Do the blit */
585 VidScreenToBufferBlt(Buffer, X, Y, Width, Height, Delta);
586 }
587 }
588
589 /**
590 * @brief
591 * Sets the screen coordinates of the loading progress bar and enable it.
592 *
593 * @param[in] Left
594 * @param[in] Top
595 * The left/top coordinates.
596 *
597 * @return None.
598 **/
599 VOID
600 NTAPI
601 InbvSetProgressBarCoordinates(
602 _In_ ULONG Left,
603 _In_ ULONG Top)
604 {
605 /* Update the coordinates */
606 ProgressBarLeft = Left;
607 ProgressBarTop = Top;
608
609 /* Enable the progress bar */
610 ShowProgressBar = TRUE;
611 }
612
613 /**
614 * @brief
615 * Gives some progress feedback, without specifying any explicit number
616 * of progress steps or percentage.
617 * The corresponding percentage is derived from the progress indicator's
618 * current count, capped to the number of expected calls to be made to
619 * this function (default: 25, see @b InbvProgressIndicator.Expected).
620 *
621 * @return None.
622 **/
623 CODE_SEG("INIT")
624 VOID
625 NTAPI
626 InbvIndicateProgress(VOID)
627 {
628 ULONG Percentage;
629
630 /* Increase progress */
631 InbvProgressIndicator.Count++;
632
633 /* Compute the new percentage - Don't go over 100% */
634 Percentage = 100 * InbvProgressIndicator.Count /
635 InbvProgressIndicator.Expected;
636 Percentage = min(Percentage, 99);
637
638 if (Percentage != InbvProgressIndicator.Percentage)
639 {
640 /* Percentage has changed, update the progress bar */
641 InbvProgressIndicator.Percentage = Percentage;
642 InbvUpdateProgressBar(Percentage);
643 }
644 }
645
646 /**
647 * @brief
648 * Specifies a progress percentage sub-range.
649 * Further calls to InbvIndicateProgress() or InbvUpdateProgressBar()
650 * will update the progress percentage relative to this sub-range.
651 * In particular, the percentage provided to InbvUpdateProgressBar()
652 * is relative to this sub-range.
653 *
654 * @param[in] Floor
655 * The lower bound percentage of the sub-range (default: 0).
656 *
657 * @param[in] Ceiling
658 * The upper bound percentage of the sub-range (default: 100).
659 *
660 * @return None.
661 **/
662 VOID
663 NTAPI
664 InbvSetProgressBarSubset(
665 _In_ ULONG Floor,
666 _In_ ULONG Ceiling)
667 {
668 /* Sanity checks */
669 ASSERT(Floor < Ceiling);
670 ASSERT(Ceiling <= 100);
671
672 /* Update the progress bar state */
673 InbvProgressState.Floor = Floor * 100;
674 InbvProgressState.Ceiling = Ceiling * 100;
675 InbvProgressState.Bias = Ceiling - Floor;
676 }
677
678 /**
679 * @brief
680 * Updates the progress bar percentage, relative to the current
681 * percentage sub-range previously set by InbvSetProgressBarSubset().
682 *
683 * @param[in] Percentage
684 * The progress percentage, relative to the current sub-range.
685 *
686 * @return None.
687 **/
688 VOID
689 NTAPI
690 InbvUpdateProgressBar(
691 _In_ ULONG Percentage)
692 {
693 ULONG TotalProgress;
694
695 /* Make sure the progress bar is enabled, that we own and are installed */
696 if (ShowProgressBar &&
697 InbvBootDriverInstalled &&
698 (InbvDisplayState == INBV_DISPLAY_STATE_OWNED))
699 {
700 /* Compute the total progress and tick the progress bar */
701 TotalProgress = InbvProgressState.Floor + (Percentage * InbvProgressState.Bias);
702 // TotalProgress /= (100 * 100);
703
704 BootAnimTickProgressBar(TotalProgress);
705 }
706 }
707
708 NTSTATUS
709 NTAPI
710 NtDisplayString(IN PUNICODE_STRING DisplayString)
711 {
712 NTSTATUS Status;
713 UNICODE_STRING CapturedString;
714 OEM_STRING OemString;
715 ULONG OemLength;
716 KPROCESSOR_MODE PreviousMode;
717
718 PAGED_CODE();
719
720 PreviousMode = ExGetPreviousMode();
721
722 /* We require the TCB privilege */
723 if (!SeSinglePrivilegeCheck(SeTcbPrivilege, PreviousMode))
724 return STATUS_PRIVILEGE_NOT_HELD;
725
726 /* Capture the string */
727 Status = ProbeAndCaptureUnicodeString(&CapturedString, PreviousMode, DisplayString);
728 if (!NT_SUCCESS(Status))
729 return Status;
730
731 /* Do not display the string if it is empty */
732 if (CapturedString.Length == 0 || CapturedString.Buffer == NULL)
733 {
734 Status = STATUS_SUCCESS;
735 goto Quit;
736 }
737
738 /*
739 * Convert the string since INBV understands only ANSI/OEM. Allocate the
740 * string buffer in non-paged pool because INBV passes it down to BOOTVID.
741 * We cannot perform the allocation using RtlUnicodeStringToOemString()
742 * since its allocator uses PagedPool.
743 */
744 OemLength = RtlUnicodeStringToOemSize(&CapturedString);
745 if (OemLength > MAXUSHORT)
746 {
747 Status = STATUS_BUFFER_OVERFLOW;
748 goto Quit;
749 }
750 RtlInitEmptyAnsiString((PANSI_STRING)&OemString, NULL, (USHORT)OemLength);
751 OemString.Buffer = ExAllocatePoolWithTag(NonPagedPool, OemLength, TAG_OSTR);
752 if (OemString.Buffer == NULL)
753 {
754 Status = STATUS_NO_MEMORY;
755 goto Quit;
756 }
757 Status = RtlUnicodeStringToOemString(&OemString, &CapturedString, FALSE);
758 if (!NT_SUCCESS(Status))
759 {
760 ExFreePoolWithTag(OemString.Buffer, TAG_OSTR);
761 goto Quit;
762 }
763
764 /* Display the string */
765 InbvDisplayString(OemString.Buffer);
766
767 /* Free the string buffer */
768 ExFreePoolWithTag(OemString.Buffer, TAG_OSTR);
769
770 Status = STATUS_SUCCESS;
771
772 Quit:
773 /* Free the captured string */
774 ReleaseCapturedUnicodeString(&CapturedString, PreviousMode);
775
776 return Status;
777 }