From d1445c8e69933b22b25fb85d72674b225325bcd0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Herm=C3=A8s=20B=C3=A9lusca-Ma=C3=AFto?= Date: Fri, 5 Dec 2025 22:11:38 +0100 Subject: [PATCH] [FREELDR:XBOX/UEFI] Unify the XBOX and UEFI linear framebuffer support together (#8509) CORE-11954, CORE-16216 Here only moving and adjusting existing code has been done. In the future, this code will be expanded to support other bits-per-pixel formats and pixel bitmasks, available in UEFI platforms, and be used to handle bios-based PC VESA framebuffer. --- .../freeldr/freeldr/arch/i386/xbox/machxbox.c | 5 +- boot/freeldr/freeldr/arch/i386/xbox/xboxmem.c | 6 +- .../freeldr/arch/i386/xbox/xboxvideo.c | 215 ++++--------- boot/freeldr/freeldr/arch/uefi/uefivid.c | 164 +++------- boot/freeldr/freeldr/arch/vidfb.c | 301 ++++++++++++++++++ boot/freeldr/freeldr/arch/vidfb.h | 78 +++++ boot/freeldr/freeldr/pcat.cmake | 4 + boot/freeldr/freeldr/rosload.cmake | 6 - boot/freeldr/freeldr/uefi.cmake | 1 + 9 files changed, 486 insertions(+), 294 deletions(-) create mode 100644 boot/freeldr/freeldr/arch/vidfb.c create mode 100644 boot/freeldr/freeldr/arch/vidfb.h diff --git a/boot/freeldr/freeldr/arch/i386/xbox/machxbox.c b/boot/freeldr/freeldr/arch/i386/xbox/machxbox.c index 7443d7d73ef..7632890025d 100644 --- a/boot/freeldr/freeldr/arch/i386/xbox/machxbox.c +++ b/boot/freeldr/freeldr/arch/i386/xbox/machxbox.c @@ -24,14 +24,13 @@ DBG_DEFAULT_CHANNEL(HWDETECT); #define MAX_XBOX_COM_PORTS 2 -extern PVOID FrameBuffer; +extern ULONG_PTR FrameBuffer; extern ULONG FrameBufferSize; BOOLEAN XboxFindPciBios(PPCI_REGISTRY_INFO BusData) { /* We emulate PCI BIOS here, there are 2 known working PCI buses on an original Xbox */ - BusData->NoBuses = 2; BusData->MajorRevision = 1; BusData->MinorRevision = 0; @@ -181,7 +180,7 @@ DetectDisplayController(PCONFIGURATION_COMPONENT_DATA BusKey) PartialDescriptor->Type = CmResourceTypeMemory; PartialDescriptor->ShareDisposition = CmResourceShareDeviceExclusive; PartialDescriptor->Flags = CM_RESOURCE_MEMORY_READ_WRITE; - PartialDescriptor->u.Memory.Start.LowPart = (ULONG_PTR)FrameBuffer & 0x0FFFFFFF; + PartialDescriptor->u.Memory.Start.LowPart = (FrameBuffer & 0x0FFFFFFF); PartialDescriptor->u.Memory.Length = FrameBufferSize; FldrCreateComponentKey(BusKey, diff --git a/boot/freeldr/freeldr/arch/i386/xbox/xboxmem.c b/boot/freeldr/freeldr/arch/i386/xbox/xboxmem.c index 14f700e7751..8f4b3571965 100644 --- a/boot/freeldr/freeldr/arch/i386/xbox/xboxmem.c +++ b/boot/freeldr/freeldr/arch/i386/xbox/xboxmem.c @@ -20,15 +20,15 @@ */ #include -#include +#include DBG_DEFAULT_CHANNEL(MEMORY); static ULONG InstalledMemoryMb = 0; static ULONG AvailableMemoryMb = 0; extern multiboot_info_t * MultibootInfoPtr; extern ULONG NvBase; -extern PVOID FrameBuffer; +extern ULONG_PTR FrameBuffer; extern ULONG FrameBufferSize; #define TEST_SIZE 0x200 @@ -267,7 +267,7 @@ XboxMemGetMemoryMap(ULONG *MemoryMapSize) { /* Video memory */ ReserveMemory(XboxMemoryMap, - (ULONG_PTR)FrameBuffer, + FrameBuffer, FrameBufferSize, LoaderFirmwarePermanent, "Video memory"); diff --git a/boot/freeldr/freeldr/arch/i386/xbox/xboxvideo.c b/boot/freeldr/freeldr/arch/i386/xbox/xboxvideo.c index 4e30a3f59a5..ca490cacc76 100644 --- a/boot/freeldr/freeldr/arch/i386/xbox/xboxvideo.c +++ b/boot/freeldr/freeldr/arch/i386/xbox/xboxvideo.c @@ -20,148 +20,58 @@ */ #include -#include "../../vgafont.h" +#include "../../vidfb.h" #include DBG_DEFAULT_CHANNEL(UI); ULONG NvBase = 0xFD000000; -PVOID FrameBuffer; +ULONG_PTR FrameBuffer; ULONG FrameBufferSize; -static ULONG ScreenWidth; -static ULONG ScreenHeight; -static ULONG BytesPerPixel; -static ULONG Delta; extern multiboot_info_t * MultibootInfoPtr; +#define FB_SIZE_MB 4 UCHAR MachDefaultTextColor = COLOR_GRAY; -#define TOP_BOTTOM_LINES 0 - -#define FB_SIZE_MB 4 - #define MAKE_COLOR(Red, Green, Blue) (0xff000000 | (((Red) & 0xff) << 16) | (((Green) & 0xff) << 8) | ((Blue) & 0xff)) -static VOID -XboxVideoOutputChar(UCHAR Char, unsigned X, unsigned Y, ULONG FgColor, ULONG BgColor) -{ - const UCHAR* FontPtr; - PULONG Pixel; - UCHAR Mask; - unsigned Line; - unsigned Col; - - FontPtr = BitmapFont8x16 + Char * CHAR_HEIGHT; - Pixel = (PULONG) ((char *) FrameBuffer + (Y * CHAR_HEIGHT + TOP_BOTTOM_LINES) * Delta - + X * CHAR_WIDTH * BytesPerPixel); - for (Line = 0; Line < CHAR_HEIGHT; Line++) - { - Mask = 0x80; - for (Col = 0; Col < CHAR_WIDTH; Col++) - { - Pixel[Col] = (0 != (FontPtr[Line] & Mask) ? FgColor : BgColor); - Mask = Mask >> 1; - } - Pixel = (PULONG) ((char *) Pixel + Delta); - } -} - -static ULONG -XboxVideoAttrToSingleColor(UCHAR Attr) -{ - UCHAR Intensity; - - Intensity = (0 == (Attr & 0x08) ? 127 : 255); - - return 0xff000000 | - (0 == (Attr & 0x04) ? 0 : (Intensity << 16)) | - (0 == (Attr & 0x02) ? 0 : (Intensity << 8)) | - (0 == (Attr & 0x01) ? 0 : Intensity); -} - -static VOID -XboxVideoAttrToColors(UCHAR Attr, ULONG *FgColor, ULONG *BgColor) -{ - *FgColor = XboxVideoAttrToSingleColor(Attr & 0xf); - *BgColor = XboxVideoAttrToSingleColor((Attr >> 4) & 0xf); -} - -static VOID -XboxVideoClearScreenColor(ULONG Color, BOOLEAN FullScreen) -{ - ULONG Line, Col; - PULONG p; - - for (Line = 0; Line < ScreenHeight - (FullScreen ? 0 : 2 * TOP_BOTTOM_LINES); Line++) - { - p = (PULONG) ((char *) FrameBuffer + (Line + (FullScreen ? 0 : TOP_BOTTOM_LINES)) * Delta); - for (Col = 0; Col < ScreenWidth; Col++) - { - *p++ = Color; - } - } -} - VOID XboxVideoScrollUp(VOID) { - ULONG BgColor, Dummy; - ULONG PixelCount = ScreenWidth * CHAR_HEIGHT * - (((ScreenHeight - 2 * TOP_BOTTOM_LINES) / CHAR_HEIGHT) - 1); - PULONG Src = (PULONG)((PUCHAR)FrameBuffer + (CHAR_HEIGHT + TOP_BOTTOM_LINES) * Delta); - PULONG Dst = (PULONG)((PUCHAR)FrameBuffer + TOP_BOTTOM_LINES * Delta); - - XboxVideoAttrToColors(ATTR(COLOR_WHITE, COLOR_BLACK), &Dummy, &BgColor); - - while (PixelCount--) - *Dst++ = *Src++; - - for (PixelCount = 0; PixelCount < ScreenWidth * CHAR_HEIGHT; PixelCount++) - *Dst++ = BgColor; + VidFbScrollUp(); } VOID XboxVideoClearScreen(UCHAR Attr) { - ULONG FgColor, BgColor; - - XboxVideoAttrToColors(Attr, &FgColor, &BgColor); - - XboxVideoClearScreenColor(BgColor, FALSE); + VidFbClearScreen(Attr); } VOID XboxVideoPutChar(int Ch, UCHAR Attr, unsigned X, unsigned Y) { - ULONG FgColor, BgColor; - - XboxVideoAttrToColors(Attr, &FgColor, &BgColor); - - XboxVideoOutputChar(Ch, X, Y, FgColor, BgColor); + VidFbPutChar(Ch, Attr, X, Y); } -UCHAR +static UCHAR NvGetCrtc(UCHAR Index) { WRITE_REGISTER_UCHAR(NvBase + NV2A_CRTC_REGISTER_INDEX, Index); return READ_REGISTER_UCHAR(NvBase + NV2A_CRTC_REGISTER_VALUE); } -ULONG -XboxGetFramebufferSize(PVOID Offset) +static ULONG +XboxGetFramebufferSize( + _In_ ULONG_PTR Offset) { memory_map_t * MemoryMap; INT Count, i; if (!MultibootInfoPtr) - { return 0; - } if (!(MultibootInfoPtr->flags & MB_INFO_FLAG_MEMORY_MAP)) - { return 0; - } MemoryMap = (memory_map_t *)MultibootInfoPtr->mmap_addr; @@ -180,7 +90,7 @@ XboxGetFramebufferSize(PVOID Offset) /* Framebuffer address offset value is coming from the GPU within * memory mapped I/O address space, so we're comparing only low * 28 bits of the address within actual RAM address space */ - if (MemoryMap->base_addr_low == ((ULONG)Offset & 0x0FFFFFFF) && MemoryMap->base_addr_high == 0) + if (MemoryMap->base_addr_low == (Offset & 0x0FFFFFFF) && MemoryMap->base_addr_high == 0) { TRACE("Video memory found\n"); return MemoryMap->length_low; @@ -193,62 +103,67 @@ XboxGetFramebufferSize(PVOID Offset) VOID XboxVideoInit(VOID) { - /* Reuse framebuffer that was set up by firmware */ - FrameBuffer = (PVOID)READ_REGISTER_ULONG(NvBase + NV2A_CRTC_FRAMEBUFFER_START); - /* Verify that framebuffer address is page-aligned */ - ASSERT((ULONG_PTR)FrameBuffer % PAGE_SIZE == 0); + ULONG ScreenWidth; + ULONG ScreenHeight; + ULONG BytesPerPixel; - /* Obtain framebuffer memory size from multiboot memory map */ - if ((FrameBufferSize = XboxGetFramebufferSize(FrameBuffer)) == 0) - { - /* Fallback to Cromwell standard which reserves high 4 MB of RAM */ - FrameBufferSize = 4 * 1024 * 1024; - WARN("Could not detect framebuffer memory size, fallback to 4 MB\n"); - } + /* Reuse framebuffer that was set up by firmware */ + FrameBuffer = (ULONG_PTR)READ_REGISTER_ULONG(NvBase + NV2A_CRTC_FRAMEBUFFER_START); + /* Verify that framebuffer address is page-aligned */ + ASSERT(FrameBuffer % PAGE_SIZE == 0); - ScreenWidth = READ_REGISTER_ULONG(NvBase + NV2A_RAMDAC_FP_HVALID_END) + 1; - ScreenHeight = READ_REGISTER_ULONG(NvBase + NV2A_RAMDAC_FP_VVALID_END) + 1; - /* Get BPP directly from NV2A CRTC (magic constants are from Cromwell) */ - BytesPerPixel = 8 * (((NvGetCrtc(0x19) & 0xE0) << 3) | (NvGetCrtc(0x13) & 0xFF)) / ScreenWidth; - if (BytesPerPixel == 4) - { - ASSERT((NvGetCrtc(0x28) & 0xF) == BytesPerPixel - 1); - } - else - { - ASSERT((NvGetCrtc(0x28) & 0xF) == BytesPerPixel); - } - Delta = (ScreenWidth * BytesPerPixel + 3) & ~ 0x3; + /* Obtain framebuffer memory size from multiboot memory map */ + if ((FrameBufferSize = XboxGetFramebufferSize(FrameBuffer)) == 0) + { + /* Fallback to Cromwell standard which reserves high 4 MB of RAM */ + FrameBufferSize = 4 * 1024 * 1024; + WARN("Could not detect framebuffer memory size, fallback to 4 MB\n"); + } - /* Verify screen resolution */ - ASSERT(ScreenWidth > 1); - ASSERT(ScreenHeight > 1); - ASSERT(BytesPerPixel >= 1 && BytesPerPixel <= 4); - /* Verify that screen fits framebuffer size */ - ASSERT(ScreenWidth * ScreenHeight * BytesPerPixel <= FrameBufferSize); + ScreenWidth = READ_REGISTER_ULONG(NvBase + NV2A_RAMDAC_FP_HVALID_END) + 1; + ScreenHeight = READ_REGISTER_ULONG(NvBase + NV2A_RAMDAC_FP_VVALID_END) + 1; - XboxVideoClearScreenColor(MAKE_COLOR(0, 0, 0), TRUE); + /* Get BPP directly from NV2A CRTC (magic constants are from Cromwell) */ + BytesPerPixel = 8 * (((NvGetCrtc(0x19) & 0xE0) << 3) | (NvGetCrtc(0x13) & 0xFF)) / ScreenWidth; + if (BytesPerPixel == 4) + ASSERT((NvGetCrtc(0x28) & 0xF) == BytesPerPixel - 1); + else + ASSERT((NvGetCrtc(0x28) & 0xF) == BytesPerPixel); + + /* Verify screen resolution */ + ASSERT(ScreenWidth > 1); + ASSERT(ScreenHeight > 1); + ASSERT(BytesPerPixel >= 1 && BytesPerPixel <= 4); + /* Verify that screen fits framebuffer size */ + ASSERT(ScreenWidth * ScreenHeight * BytesPerPixel <= FrameBufferSize); + + VidFbInitializeVideo(FrameBuffer, + FrameBufferSize, + ScreenWidth, + ScreenHeight, + ScreenWidth, // PixelsPerScanLine + BytesPerPixel * 8); + + VidFbClearScreenColor(MAKE_COLOR(0, 0, 0), TRUE); } VIDEODISPLAYMODE XboxVideoSetDisplayMode(PCSTR DisplayMode, BOOLEAN Init) { - /* We only have one mode, semi-text */ - return VideoTextMode; + /* We only have one mode, semi-text */ + return VideoTextMode; } VOID XboxVideoGetDisplaySize(PULONG Width, PULONG Height, PULONG Depth) { - *Width = ScreenWidth / CHAR_WIDTH; - *Height = (ScreenHeight - 2 * TOP_BOTTOM_LINES) / CHAR_HEIGHT; - *Depth = 0; + VidFbGetDisplaySize(Width, Height, Depth); } ULONG XboxVideoGetBufferSize(VOID) { - return (ScreenHeight - 2 * TOP_BOTTOM_LINES) / CHAR_HEIGHT * (ScreenWidth / CHAR_WIDTH) * 2; + return VidFbGetBufferSize(); } VOID @@ -260,47 +175,37 @@ XboxVideoGetFontsFromFirmware(PULONG RomFontPointers) VOID XboxVideoSetTextCursorPosition(UCHAR X, UCHAR Y) { - /* We don't have a cursor yet */ + /* We don't have a cursor yet */ } VOID XboxVideoHideShowTextCursor(BOOLEAN Show) { - /* We don't have a cursor yet */ + /* We don't have a cursor yet */ } VOID XboxVideoCopyOffScreenBufferToVRAM(PVOID Buffer) { - PUCHAR OffScreenBuffer = (PUCHAR) Buffer; - ULONG Col, Line; - - for (Line = 0; Line < (ScreenHeight - 2 * TOP_BOTTOM_LINES) / CHAR_HEIGHT; Line++) - { - for (Col = 0; Col < ScreenWidth / CHAR_WIDTH; Col++) - { - XboxVideoPutChar(OffScreenBuffer[0], OffScreenBuffer[1], Col, Line); - OffScreenBuffer += 2; - } - } + VidFbCopyOffScreenBufferToVRAM(Buffer); } BOOLEAN XboxVideoIsPaletteFixed(VOID) { - return FALSE; + return FALSE; } VOID XboxVideoSetPaletteColor(UCHAR Color, UCHAR Red, UCHAR Green, UCHAR Blue) { - /* Not supported */ + /* Not supported */ } VOID XboxVideoGetPaletteColor(UCHAR Color, UCHAR* Red, UCHAR* Green, UCHAR* Blue) { - /* Not supported */ + /* Not supported */ } VOID @@ -312,7 +217,7 @@ XboxVideoSync(VOID) VOID XboxVideoPrepareForReactOS(VOID) { - XboxVideoClearScreenColor(MAKE_COLOR(0, 0, 0), TRUE); + VidFbClearScreenColor(MAKE_COLOR(0, 0, 0), TRUE); XboxVideoHideShowTextCursor(FALSE); } diff --git a/boot/freeldr/freeldr/arch/uefi/uefivid.c b/boot/freeldr/freeldr/arch/uefi/uefivid.c index 4d40c8aeb0c..8eb951a98ee 100644 --- a/boot/freeldr/freeldr/arch/uefi/uefivid.c +++ b/boot/freeldr/freeldr/arch/uefi/uefivid.c @@ -6,13 +6,10 @@ */ #include -#include "../vgafont.h" +#include "../vidfb.h" #include -DBG_DEFAULT_CHANNEL(WARNING); - -#define TOP_BOTTOM_LINES 0 -#define LOWEST_SUPPORTED_RES 1 +DBG_DEFAULT_CHANNEL(UI); /* GLOBALS ********************************************************************/ @@ -20,18 +17,21 @@ extern EFI_SYSTEM_TABLE* GlobalSystemTable; extern EFI_HANDLE GlobalImageHandle; UCHAR MachDefaultTextColor = COLOR_GRAY; -REACTOS_INTERNAL_BGCONTEXT framebufferData; EFI_GUID EfiGraphicsOutputProtocol = EFI_GRAPHICS_OUTPUT_PROTOCOL_GUID; +#define LOWEST_SUPPORTED_RES 1 + /* FUNCTIONS ******************************************************************/ EFI_STATUS UefiInitializeVideo(VOID) { + EFI_GRAPHICS_PIXEL_FORMAT PixelFormat; + ULONG BitsPerPixel; + EFI_STATUS Status; EFI_GRAPHICS_OUTPUT_PROTOCOL* gop = NULL; - RtlZeroMemory(&framebufferData, sizeof(framebufferData)); Status = GlobalSystemTable->BootServices->LocateProtocol(&EfiGraphicsOutputProtocol, 0, (void**)&gop); if (Status != EFI_SUCCESS) { @@ -42,118 +42,52 @@ UefiInitializeVideo(VOID) /* We don't need high resolutions for freeldr */ gop->SetMode(gop, LOWEST_SUPPORTED_RES); - framebufferData.BaseAddress = (ULONG_PTR)gop->Mode->FrameBufferBase; - framebufferData.BufferSize = gop->Mode->FrameBufferSize; - framebufferData.ScreenWidth = gop->Mode->Info->HorizontalResolution; - framebufferData.ScreenHeight = gop->Mode->Info->VerticalResolution; - framebufferData.PixelsPerScanLine = gop->Mode->Info->PixelsPerScanLine; - framebufferData.PixelFormat = gop->Mode->Info->PixelFormat; - - return Status; -} - -VOID -UefiPrintFramebufferData(VOID) -{ - TRACE("Framebuffer BaseAddress : %X\n", framebufferData.BaseAddress); - TRACE("Framebuffer BufferSize : %X\n", framebufferData.BufferSize); - TRACE("Framebuffer ScreenWidth : %d\n", framebufferData.ScreenWidth); - TRACE("Framebuffer ScreenHeight : %d\n", framebufferData.ScreenHeight); - TRACE("Framebuffer PixelsPerScanLine : %d\n", framebufferData.PixelsPerScanLine); - TRACE("Framebuffer PixelFormat : %d\n", framebufferData.PixelFormat); -} - -static ULONG -UefiVideoAttrToSingleColor(UCHAR Attr) -{ - UCHAR Intensity; - Intensity = (0 == (Attr & 0x08) ? 127 : 255); - - return 0xff000000 | - (0 == (Attr & 0x04) ? 0 : (Intensity << 16)) | - (0 == (Attr & 0x02) ? 0 : (Intensity << 8)) | - (0 == (Attr & 0x01) ? 0 : Intensity); -} - -static VOID -UefiVideoAttrToColors(UCHAR Attr, ULONG *FgColor, ULONG *BgColor) -{ - *FgColor = UefiVideoAttrToSingleColor(Attr & 0xf); - *BgColor = UefiVideoAttrToSingleColor((Attr >> 4) & 0xf); -} - - -static VOID -UefiVideoClearScreenColor(ULONG Color, BOOLEAN FullScreen) -{ - ULONG Delta; - ULONG Line, Col; - PULONG p; - - Delta = (framebufferData.PixelsPerScanLine * 4 + 3) & ~ 0x3; - for (Line = 0; Line < framebufferData.ScreenHeight - (FullScreen ? 0 : 2 * TOP_BOTTOM_LINES); Line++) + /* Physical format of the pixel */ + PixelFormat = gop->Mode->Info->PixelFormat; + switch (PixelFormat) { - p = (PULONG) ((char *) framebufferData.BaseAddress + (Line + (FullScreen ? 0 : TOP_BOTTOM_LINES)) * Delta); - for (Col = 0; Col < framebufferData.ScreenWidth; Col++) + case PixelRedGreenBlueReserved8BitPerColor: + case PixelBlueGreenRedReserved8BitPerColor: { - *p++ = Color; + BitsPerPixel = RTL_BITS_OF(EFI_GRAPHICS_OUTPUT_BLT_PIXEL); + break; + } + + case PixelBitMask: + case PixelBltOnly: + default: + { + ERR("Unsupported UFEI GOP format %lu\n", PixelFormat); + BitsPerPixel = 0; + break; } } + + VidFbInitializeVideo((ULONG_PTR)gop->Mode->FrameBufferBase, + gop->Mode->FrameBufferSize, + gop->Mode->Info->HorizontalResolution, + gop->Mode->Info->VerticalResolution, + gop->Mode->Info->PixelsPerScanLine, + BitsPerPixel); + return Status; } VOID UefiVideoClearScreen(UCHAR Attr) { - ULONG FgColor, BgColor; - - UefiVideoAttrToColors(Attr, &FgColor, &BgColor); - UefiVideoClearScreenColor(BgColor, FALSE); -} - -VOID -UefiVideoOutputChar(UCHAR Char, unsigned X, unsigned Y, ULONG FgColor, ULONG BgColor) -{ - const UCHAR* FontPtr; - PULONG Pixel; - UCHAR Mask; - unsigned Line; - unsigned Col; - ULONG Delta; - Delta = (framebufferData.PixelsPerScanLine * 4 + 3) & ~ 0x3; - FontPtr = BitmapFont8x16 + Char * CHAR_HEIGHT; - Pixel = (PULONG) ((char *) framebufferData.BaseAddress + - (Y * CHAR_HEIGHT + TOP_BOTTOM_LINES) * Delta + X * CHAR_WIDTH * 4); - - for (Line = 0; Line < CHAR_HEIGHT; Line++) - { - Mask = 0x80; - for (Col = 0; Col < CHAR_WIDTH; Col++) - { - Pixel[Col] = (0 != (FontPtr[Line] & Mask) ? FgColor : BgColor); - Mask = Mask >> 1; - } - Pixel = (PULONG) ((char *) Pixel + Delta); - } + VidFbClearScreen(Attr); } VOID UefiVideoPutChar(int Ch, UCHAR Attr, unsigned X, unsigned Y) { - ULONG FgColor = 0; - ULONG BgColor = 0; - if (Ch != 0) - { - UefiVideoAttrToColors(Attr, &FgColor, &BgColor); - UefiVideoOutputChar(Ch, X, Y, FgColor, BgColor); - } + VidFbPutChar(Ch, Attr, X, Y); } VOID UefiVideoGetDisplaySize(PULONG Width, PULONG Height, PULONG Depth) { - *Width = framebufferData.ScreenWidth / CHAR_WIDTH; - *Height = (framebufferData.ScreenHeight - 2 * TOP_BOTTOM_LINES) / CHAR_HEIGHT; - *Depth = 0; + VidFbGetDisplaySize(Width, Height, Depth); } VIDEODISPLAYMODE @@ -166,43 +100,19 @@ UefiVideoSetDisplayMode(PCSTR DisplayMode, BOOLEAN Init) ULONG UefiVideoGetBufferSize(VOID) { - return ((framebufferData.ScreenHeight - 2 * TOP_BOTTOM_LINES) / CHAR_HEIGHT * (framebufferData.ScreenWidth / CHAR_WIDTH) * 2); + return VidFbGetBufferSize(); } VOID UefiVideoCopyOffScreenBufferToVRAM(PVOID Buffer) { - PUCHAR OffScreenBuffer = (PUCHAR)Buffer; - - ULONG Col, Line; - for (Line = 0; Line < (framebufferData.ScreenHeight - 2 * TOP_BOTTOM_LINES) / CHAR_HEIGHT; Line++) - { - for (Col = 0; Col < framebufferData.ScreenWidth / CHAR_WIDTH; Col++) - { - UefiVideoPutChar(OffScreenBuffer[0], OffScreenBuffer[1], Col, Line); - OffScreenBuffer += 2; - } - } + VidFbCopyOffScreenBufferToVRAM(Buffer); } VOID UefiVideoScrollUp(VOID) { - ULONG BgColor, Dummy; - ULONG Delta; - Delta = (framebufferData.PixelsPerScanLine * 4 + 3) & ~ 0x3; - ULONG PixelCount = framebufferData.ScreenWidth * CHAR_HEIGHT * - (((framebufferData.ScreenHeight - 2 * TOP_BOTTOM_LINES) / CHAR_HEIGHT) - 1); - PULONG Src = (PULONG)((PUCHAR)framebufferData.BaseAddress + (CHAR_HEIGHT + TOP_BOTTOM_LINES) * Delta); - PULONG Dst = (PULONG)((PUCHAR)framebufferData.BaseAddress + TOP_BOTTOM_LINES * Delta); - - UefiVideoAttrToColors(ATTR(COLOR_WHITE, COLOR_BLACK), &Dummy, &BgColor); - - while (PixelCount--) - *Dst++ = *Src++; - - for (PixelCount = 0; PixelCount < framebufferData.ScreenWidth * CHAR_HEIGHT; PixelCount++) - *Dst++ = BgColor; + VidFbScrollUp(); } VOID diff --git a/boot/freeldr/freeldr/arch/vidfb.c b/boot/freeldr/freeldr/arch/vidfb.c new file mode 100644 index 00000000000..3a85b103411 --- /dev/null +++ b/boot/freeldr/freeldr/arch/vidfb.c @@ -0,0 +1,301 @@ +/* + * PROJECT: FreeLoader + * LICENSE: GPL-2.0-or-later (https://spdx.org/licenses/GPL-2.0-or-later) + * PURPOSE: Video support for linear framebuffers + * COPYRIGHT: Authors of uefivid.c and xboxvideo.c + * Copyright 2025 Hermès Bélusca-Maïto + */ + +#include +#include "vidfb.h" +#include "vgafont.h" + +#include +DBG_DEFAULT_CHANNEL(UI); + +#define VGA_CHAR_SIZE 2 + +/* This is used to introduce artificial symmetric borders at the top and bottom */ +#define TOP_BOTTOM_LINES 0 + + +/* GLOBALS ********************************************************************/ + +typedef struct _FRAMEBUFFER_INFO +{ + ULONG_PTR BaseAddress; + ULONG BufferSize; + + /* Horizontal and Vertical resolution in pixels */ + ULONG ScreenWidth; + ULONG ScreenHeight; + + ULONG PixelsPerScanLine; // aka. "Pitch" or "ScreenStride", but Stride is in bytes or bits... + ULONG BitsPerPixel; // aka. "PixelStride". + +/** Calculated values */ + + ULONG BytesPerPixel; + ULONG Delta; // aka. "Pitch": actual size in bytes of a scanline. +} FRAMEBUFFER_INFO, *PFRAMEBUFFER_INFO; + +FRAMEBUFFER_INFO framebufInfo; + + +/* FUNCTIONS ******************************************************************/ + +#if DBG +static VOID +VidFbPrintFramebufferInfo(VOID) +{ + TRACE("Framebuffer format:\n"); + TRACE(" BaseAddress : 0x%X\n", framebufInfo.BaseAddress); + TRACE(" BufferSize : %lu\n", framebufInfo.BufferSize); + TRACE(" ScreenWidth : %lu\n", framebufInfo.ScreenWidth); + TRACE(" ScreenHeight : %lu\n", framebufInfo.ScreenHeight); + TRACE(" PixelsPerScanLine : %lu\n", framebufInfo.PixelsPerScanLine); + TRACE(" BitsPerPixel : %lu\n", framebufInfo.BitsPerPixel); + TRACE(" BytesPerPixel : %lu\n", framebufInfo.BytesPerPixel); + TRACE(" Delta : %lu\n", framebufInfo.Delta); +} +#endif + +/** + * @brief + * Initializes internal framebuffer data based on the given parameters. + * + * @param[in] BaseAddress + * The framebuffer physical base address. + * + * @param[in] BufferSize + * The framebuffer size, in bytes. + * + * @param[in] ScreenWidth + * @param[in] ScreenHeight + * The width and height of the visible framebuffer area, in pixels. + * + * @param[in] PixelsPerScanLine + * The size in number of pixels of a whole horizontal video memory scanline. + * + * @param[in] BitsPerPixel + * The number of usable bits (not counting the reserved ones) per pixel. + * + * @return + * TRUE if initialization is successful; FALSE if not. + **/ +BOOLEAN +VidFbInitializeVideo( + _In_ ULONG_PTR BaseAddress, + _In_ ULONG BufferSize, + _In_ UINT32 ScreenWidth, + _In_ UINT32 ScreenHeight, + _In_ UINT32 PixelsPerScanLine, + _In_ UINT32 BitsPerPixel) +{ + RtlZeroMemory(&framebufInfo, sizeof(framebufInfo)); + + framebufInfo.BaseAddress = BaseAddress; + framebufInfo.BufferSize = BufferSize; + framebufInfo.ScreenWidth = ScreenWidth; + framebufInfo.ScreenHeight = ScreenHeight; + framebufInfo.PixelsPerScanLine = PixelsPerScanLine; + framebufInfo.BitsPerPixel = BitsPerPixel; + + framebufInfo.BytesPerPixel = ((BitsPerPixel + 7) & ~7) / 8; // Round up to nearest byte. + framebufInfo.Delta = (PixelsPerScanLine * framebufInfo.BytesPerPixel + 3) & ~3; + + /* We currently only support 32bpp */ + if (BitsPerPixel != 32) + { + /* Unsupported BPP */ + ERR("Unsupported %lu bits per pixel format\n", BitsPerPixel); + return FALSE; + } + +#if DBG + VidFbPrintFramebufferInfo(); +#endif + + return TRUE; +} + +static inline +UINT32 +VidFbAttrToSingleColor( + _In_ UCHAR Attr) +{ + UCHAR Intensity; + Intensity = (0 == (Attr & 0x08) ? 127 : 255); + + return 0xff000000 | + (0 == (Attr & 0x04) ? 0 : (Intensity << 16)) | + (0 == (Attr & 0x02) ? 0 : (Intensity << 8)) | + (0 == (Attr & 0x01) ? 0 : Intensity); +} + +static VOID +VidFbAttrToColors( + _In_ UCHAR Attr, + _Out_ PUINT32 FgColor, + _Out_ PUINT32 BgColor) +{ + *FgColor = VidFbAttrToSingleColor(Attr & 0x0F); + *BgColor = VidFbAttrToSingleColor((Attr >> 4) & 0x0F); +} + +VOID +VidFbClearScreenColor( + _In_ UINT32 Color, + _In_ BOOLEAN FullScreen) +{ + ULONG Line, Col; + PUINT32 p; + + for (Line = 0; Line < framebufInfo.ScreenHeight - (FullScreen ? 0 : 2 * TOP_BOTTOM_LINES); Line++) + { + p = (PUINT32)((PUCHAR)framebufInfo.BaseAddress + (Line + (FullScreen ? 0 : TOP_BOTTOM_LINES)) * framebufInfo.Delta); + for (Col = 0; Col < framebufInfo.ScreenWidth; Col++) + { + *p++ = Color; + } + } +} + +VOID +VidFbClearScreen( + _In_ UCHAR Attr) +{ + UINT32 FgColor, BgColor; + VidFbAttrToColors(Attr, &FgColor, &BgColor); + VidFbClearScreenColor(BgColor, FALSE); +} + +VOID +VidFbOutputChar( + _In_ UCHAR Char, + _In_ ULONG X, + _In_ ULONG Y, + _In_ UINT32 FgColor, + _In_ UINT32 BgColor) +{ + const UCHAR* FontPtr; + PUINT32 Pixel; + UCHAR Mask; + ULONG Line, Col; + + FontPtr = BitmapFont8x16 + Char * CHAR_HEIGHT; + Pixel = (PUINT32)((PUCHAR)framebufInfo.BaseAddress + + (Y * CHAR_HEIGHT + TOP_BOTTOM_LINES) * framebufInfo.Delta + X * CHAR_WIDTH * 4); + + for (Line = 0; Line < CHAR_HEIGHT; Line++) + { + Mask = 0x80; + for (Col = 0; Col < CHAR_WIDTH; Col++) + { + Pixel[Col] = (0 != (FontPtr[Line] & Mask) ? FgColor : BgColor); + Mask = Mask >> 1; + } + Pixel = (PUINT32)((PUCHAR)Pixel + framebufInfo.Delta); + } +} + +VOID +VidFbPutChar( + _In_ UCHAR Char, + _In_ UCHAR Attr, + _In_ ULONG X, + _In_ ULONG Y) +{ + UINT32 FgColor, BgColor; + VidFbAttrToColors(Attr, &FgColor, &BgColor); + VidFbOutputChar(Char, X, Y, FgColor, BgColor); +} + +VOID +VidFbGetDisplaySize( + _Out_ PULONG Width, + _Out_ PULONG Height, + _Out_ PULONG Depth) +{ + *Width = framebufInfo.ScreenWidth / CHAR_WIDTH; + *Height = (framebufInfo.ScreenHeight - 2 * TOP_BOTTOM_LINES) / CHAR_HEIGHT; + *Depth = framebufInfo.BitsPerPixel; +} + +ULONG +VidFbGetBufferSize(VOID) +{ + return ((framebufInfo.ScreenHeight - 2 * TOP_BOTTOM_LINES) / CHAR_HEIGHT * + (framebufInfo.ScreenWidth / CHAR_WIDTH) * VGA_CHAR_SIZE); +} + +VOID +VidFbCopyOffScreenBufferToVRAM( + _In_ PVOID Buffer) +{ + PUCHAR OffScreenBuffer = (PUCHAR)Buffer; + ULONG Line, Col; + + for (Line = 0; Line < (framebufInfo.ScreenHeight - 2 * TOP_BOTTOM_LINES) / CHAR_HEIGHT; Line++) + { + for (Col = 0; Col < framebufInfo.ScreenWidth / CHAR_WIDTH; Col++) + { + VidFbPutChar(OffScreenBuffer[0], OffScreenBuffer[1], Col, Line); + OffScreenBuffer += VGA_CHAR_SIZE; + } + } +} + +VOID +VidFbScrollUp(VOID) +{ + UINT32 BgColor, Dummy; + ULONG PixelCount = framebufInfo.ScreenWidth * CHAR_HEIGHT * + (((framebufInfo.ScreenHeight - 2 * TOP_BOTTOM_LINES) / CHAR_HEIGHT) - 1); + PUINT32 Src = (PUINT32)((PUCHAR)framebufInfo.BaseAddress + (CHAR_HEIGHT + TOP_BOTTOM_LINES) * framebufInfo.Delta); + PUINT32 Dst = (PUINT32)((PUCHAR)framebufInfo.BaseAddress + TOP_BOTTOM_LINES * framebufInfo.Delta); + + VidFbAttrToColors(ATTR(COLOR_WHITE, COLOR_BLACK), &Dummy, &BgColor); + + while (PixelCount--) + *Dst++ = *Src++; + + for (PixelCount = 0; PixelCount < framebufInfo.ScreenWidth * CHAR_HEIGHT; PixelCount++) + *Dst++ = BgColor; +} + +#if 0 +VOID +VidFbSetTextCursorPosition(UCHAR X, UCHAR Y) +{ + /* We don't have a cursor yet */ +} + +VOID +VidFbHideShowTextCursor(BOOLEAN Show) +{ + /* We don't have a cursor yet */ +} + +BOOLEAN +VidFbIsPaletteFixed(VOID) +{ + return FALSE; +} + +VOID +VidFbSetPaletteColor( + _In_ UCHAR Color, + _In_ UCHAR Red, _In_ UCHAR Green, _In_ UCHAR Blue) +{ + /* Not supported */ +} + +VOID +VidFbGetPaletteColor( + _In_ UCHAR Color, + _Out_ PUCHAR Red, _Out_ PUCHAR Green, _Out_ PUCHAR Blue) +{ + /* Not supported */ +} +#endif diff --git a/boot/freeldr/freeldr/arch/vidfb.h b/boot/freeldr/freeldr/arch/vidfb.h new file mode 100644 index 00000000000..ef0ed4d65d2 --- /dev/null +++ b/boot/freeldr/freeldr/arch/vidfb.h @@ -0,0 +1,78 @@ +/* + * PROJECT: FreeLoader + * LICENSE: GPL-2.0-or-later (https://spdx.org/licenses/GPL-2.0-or-later) + * PURPOSE: Video support for linear framebuffers + * COPYRIGHT: Copyright 2025 Hermès Bélusca-Maïto + */ + +#pragma once + +BOOLEAN +VidFbInitializeVideo( + _In_ ULONG_PTR BaseAddress, + _In_ ULONG BufferSize, + _In_ UINT32 ScreenWidth, + _In_ UINT32 ScreenHeight, + _In_ UINT32 PixelsPerScanLine, + _In_ UINT32 BitsPerPixel); + +VOID +VidFbClearScreenColor( + _In_ UINT32 Color, + _In_ BOOLEAN FullScreen); + +VOID +VidFbClearScreen( + _In_ UCHAR Attr); + +VOID +VidFbOutputChar( + _In_ UCHAR Char, + _In_ ULONG X, + _In_ ULONG Y, + _In_ UINT32 FgColor, + _In_ UINT32 BgColor); + +VOID +VidFbPutChar( + _In_ UCHAR Char, + _In_ UCHAR Attr, + _In_ ULONG X, + _In_ ULONG Y); + +VOID +VidFbGetDisplaySize( + _Out_ PULONG Width, + _Out_ PULONG Height, + _Out_ PULONG Depth); + +ULONG +VidFbGetBufferSize(VOID); + +VOID +VidFbCopyOffScreenBufferToVRAM( + _In_ PVOID Buffer); + +VOID +VidFbScrollUp(VOID); + +#if 0 +VOID +VidFbSetTextCursorPosition(UCHAR X, UCHAR Y); + +VOID +VidFbHideShowTextCursor(BOOLEAN Show); + +BOOLEAN +VidFbIsPaletteFixed(VOID); + +VOID +VidFbSetPaletteColor( + _In_ UCHAR Color, + _In_ UCHAR Red, _In_ UCHAR Green, _In_ UCHAR Blue); + +VOID +VidFbGetPaletteColor( + _In_ UCHAR Color, + _Out_ PUCHAR Red, _Out_ PUCHAR Green, _Out_ PUCHAR Blue); +#endif diff --git a/boot/freeldr/freeldr/pcat.cmake b/boot/freeldr/freeldr/pcat.cmake index 374404fb457..51ec6dfca2c 100644 --- a/boot/freeldr/freeldr/pcat.cmake +++ b/boot/freeldr/freeldr/pcat.cmake @@ -58,6 +58,10 @@ if(ARCH STREQUAL "i386") # arch/i386/i386bug.c arch/i386/i386idt.c) + if(SARCH STREQUAL "xbox") + list(APPEND PCATLDR_ARC_SOURCE + arch/vidfb.c) + endif() if(SARCH STREQUAL "pc98" OR SARCH STREQUAL "xbox") # These machine types require built-in bitmap font list(APPEND PCATLDR_ARC_SOURCE diff --git a/boot/freeldr/freeldr/rosload.cmake b/boot/freeldr/freeldr/rosload.cmake index 6af8eccf380..1b077c47d1a 100644 --- a/boot/freeldr/freeldr/rosload.cmake +++ b/boot/freeldr/freeldr/rosload.cmake @@ -35,12 +35,6 @@ if(ARCH STREQUAL "i386") ntldr/arch/i386/winldr.c ntldr/headless.c) - if(SARCH STREQUAL "pc98" OR SARCH STREQUAL "xbox") - # These machine types require built-in bitmap font - list(APPEND ROSLOAD_SOURCE - arch/vgafont.c) - endif() - list(APPEND ROSLOAD_ASM_SOURCE arch/i386/drvmap.S arch/i386/linux.S) diff --git a/boot/freeldr/freeldr/uefi.cmake b/boot/freeldr/freeldr/uefi.cmake index 97a9a82c102..82de22a5d19 100644 --- a/boot/freeldr/freeldr/uefi.cmake +++ b/boot/freeldr/freeldr/uefi.cmake @@ -21,6 +21,7 @@ list(APPEND UEFILDR_ARC_SOURCE arch/uefi/uefisetup.c arch/uefi/uefiutil.c arch/uefi/uefivid.c + arch/vidfb.c arch/vgafont.c) if(ARCH STREQUAL "i386")