From aab65cbeb773f0fa2d1e271acf9628d725d3bb34 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Herm=C3=A8s=20B=C3=A9lusca-Ma=C3=AFto?= Date: Thu, 18 Dec 2025 15:25:52 +0100 Subject: [PATCH] [FREELDR:XBOX/UEFI] vidfb: stub-plement pixel bitmasks support (#8531) This is the starting point where both support for generic framebuffer video driver support (in bootvid and win32k) for XBOX, UEFI etc., and in FreeLoader itself also for PC VESA, begins. --- .../freeldr/arch/i386/xbox/xboxvideo.c | 3 +- boot/freeldr/freeldr/arch/twidbits.h | 39 ++++++++++ boot/freeldr/freeldr/arch/uefi/uefivid.c | 49 +++++++++++- boot/freeldr/freeldr/arch/vidfb.c | 75 ++++++++++++++++++- boot/freeldr/freeldr/arch/vidfb.h | 46 +++++++++++- 5 files changed, 208 insertions(+), 4 deletions(-) create mode 100644 boot/freeldr/freeldr/arch/twidbits.h diff --git a/boot/freeldr/freeldr/arch/i386/xbox/xboxvideo.c b/boot/freeldr/freeldr/arch/i386/xbox/xboxvideo.c index 928c6068444..6c325fa52e6 100644 --- a/boot/freeldr/freeldr/arch/i386/xbox/xboxvideo.c +++ b/boot/freeldr/freeldr/arch/i386/xbox/xboxvideo.c @@ -134,7 +134,8 @@ XboxVideoInit(VOID) ScreenWidth, ScreenHeight, ScreenWidth, // PixelsPerScanLine - BytesPerPixel * 8); + BytesPerPixel * 8, + NULL); VidFbClearScreenColor(MAKE_COLOR(0, 0, 0), TRUE); } diff --git a/boot/freeldr/freeldr/arch/twidbits.h b/boot/freeldr/freeldr/arch/twidbits.h new file mode 100644 index 00000000000..bbc8f0faa95 --- /dev/null +++ b/boot/freeldr/freeldr/arch/twidbits.h @@ -0,0 +1,39 @@ +/* + * PROJECT: FreeLoader + * LICENSE: GPL-2.0-or-later (https://spdx.org/licenses/GPL-2.0-or-later) + * or MIT (https://spdx.org/licenses/MIT) + * PURPOSE: Bit twiddling helpers + * COPYRIGHT: Copyright 2025 Hermès Bélusca-Maïto + * + * Based on http://www.graphics.stanford.edu/~seander/bithacks.html + * and other sources. + */ + +// HAVE___BUILTIN_POPCOUNT + +#pragma once + +/** + * @brief Return the number of bits set in a 32-bit integer. + * @note Equivalent to __popcnt(). + **/ +FORCEINLINE +ULONG +CountNumberOfBits( + _In_ UINT32 n) +{ +#ifdef HAVE___BUILTIN_POPCOUNT + return __popcnt(n); +#else + n -= ((n >> 1) & 0x55555555); + n = (((n >> 2) & 0x33333333) + (n & 0x33333333)); +#if 0 + n = (((n >> 4) + n) & 0x0f0f0f0f); + n += (n >> 8); + n += (n >> 16); + return (n & 0x3f); +#else + return (((n >> 4) + n) & 0x0f0f0f0f) * 0x01010101 >> 24; +#endif +#endif /* HAVE___BUILTIN_POPCOUNT */ +} diff --git a/boot/freeldr/freeldr/arch/uefi/uefivid.c b/boot/freeldr/freeldr/arch/uefi/uefivid.c index 95304babe95..e00c9e875ef 100644 --- a/boot/freeldr/freeldr/arch/uefi/uefivid.c +++ b/boot/freeldr/freeldr/arch/uefi/uefivid.c @@ -21,10 +21,38 @@ EFI_GUID EfiGraphicsOutputProtocol = EFI_GRAPHICS_OUTPUT_PROTOCOL_GUID; /* FUNCTIONS ******************************************************************/ +/* EFI 1.x */ +#ifdef EFI_UGA_DRAW_PROTOCOL_GUID + +/* NOTE: EFI UGA does not support any other format than 32-bit xRGB, and + * no direct access to the underlying hardware framebuffer is offered */ +C_ASSERT(sizeof(EFI_UGA_PIXEL) == sizeof(ULONG)); + +#endif /* EFI */ + +/* UEFI support, see efi/GraphicsOutput.h */ +#ifdef EFI_GRAPHICS_OUTPUT_PROTOCOL_GUID // __GRAPHICS_OUTPUT_H__ + +C_ASSERT(sizeof(EFI_GRAPHICS_OUTPUT_BLT_PIXEL) == sizeof(ULONG)); + +/** + * @brief Maps UEFI GOP pixel format to pixel masks. + * @see EFI_PIXEL_BITMASK + **/ +static EFI_PIXEL_BITMASK EfiPixelMasks[] = +{ /* Red, Green, Blue, Reserved */ + {0x000000FF, 0x0000FF00, 0x00FF0000, 0xFF000000}, // PixelRedGreenBlueReserved8BitPerColor + {0x00FF0000, 0x0000FF00, 0x000000FF, 0xFF000000}, // PixelBlueGreenRedReserved8BitPerColor + {0, 0, 0, 0} // PixelBitMask, PixelBltOnly, ... +}; + +#endif /* UEFI */ + EFI_STATUS UefiInitializeVideo(VOID) { EFI_GRAPHICS_PIXEL_FORMAT PixelFormat; + EFI_PIXEL_BITMASK* pPixelBitmask; ULONG BitsPerPixel; EFI_STATUS Status; @@ -47,15 +75,33 @@ UefiInitializeVideo(VOID) case PixelRedGreenBlueReserved8BitPerColor: case PixelBlueGreenRedReserved8BitPerColor: { + pPixelBitmask = &EfiPixelMasks[PixelFormat]; BitsPerPixel = RTL_BITS_OF(EFI_GRAPHICS_OUTPUT_BLT_PIXEL); break; } case PixelBitMask: + { + /* + * When the GOP pixel format is given by PixelBitMask, the pixel + * element size _may be_ different from 4 bytes. + * See UEFI Spec Rev.2.10 Section 12.9 "Graphics Output Protocol": + * example code "GetPixelElementSize()" function. + */ + pPixelBitmask = &gop->Mode->Info->PixelInformation; + BitsPerPixel = + PixelBitmasksToBpp(pPixelBitmask->RedMask, + pPixelBitmask->GreenMask, + pPixelBitmask->BlueMask, + pPixelBitmask->ReservedMask); + break; + } + case PixelBltOnly: default: { ERR("Unsupported UFEI GOP format %lu\n", PixelFormat); + pPixelBitmask = NULL; BitsPerPixel = 0; break; } @@ -66,7 +112,8 @@ UefiInitializeVideo(VOID) gop->Mode->Info->HorizontalResolution, gop->Mode->Info->VerticalResolution, gop->Mode->Info->PixelsPerScanLine, - BitsPerPixel); + BitsPerPixel, + (PPIXEL_BITMASK)pPixelBitmask); return Status; } diff --git a/boot/freeldr/freeldr/arch/vidfb.c b/boot/freeldr/freeldr/arch/vidfb.c index 72d50e15d32..5d0b01442ca 100644 --- a/boot/freeldr/freeldr/arch/vidfb.c +++ b/boot/freeldr/freeldr/arch/vidfb.c @@ -31,6 +31,9 @@ typedef struct _FRAMEBUFFER_INFO ULONG PixelsPerScanLine; // aka. "Pitch" or "ScreenStride", but Stride is in bytes or bits... ULONG BitsPerPixel; // aka. "PixelStride". + /* Physical format of the pixel for BPP > 8, specified by bit-mask */ + PIXEL_BITMASK PixelMasks; + /** Calculated values */ ULONG BytesPerPixel; @@ -55,6 +58,11 @@ VidFbPrintFramebufferInfo(VOID) TRACE(" BitsPerPixel : %lu\n", framebufInfo.BitsPerPixel); TRACE(" BytesPerPixel : %lu\n", framebufInfo.BytesPerPixel); TRACE(" Delta : %lu\n", framebufInfo.Delta); + TRACE(" ARGB masks: : %08x/%08x/%08x/%08x\n", + framebufInfo.PixelMasks.ReservedMask, + framebufInfo.PixelMasks.RedMask, + framebufInfo.PixelMasks.GreenMask, + framebufInfo.PixelMasks.BlueMask); } #endif @@ -78,6 +86,10 @@ VidFbPrintFramebufferInfo(VOID) * @param[in] BitsPerPixel * The number of usable bits (not counting the reserved ones) per pixel. * + * @param[in] PixelMasks + * Optional pointer to a PIXEL_BITMASK structure describing the pixel + * format used by the framebuffer. + * * @return * TRUE if initialization is successful; FALSE if not. **/ @@ -88,8 +100,11 @@ VidFbInitializeVideo( _In_ UINT32 ScreenWidth, _In_ UINT32 ScreenHeight, _In_ UINT32 PixelsPerScanLine, - _In_ UINT32 BitsPerPixel) + _In_ UINT32 BitsPerPixel, + _In_opt_ PPIXEL_BITMASK PixelMasks) { + PPIXEL_BITMASK BitMasks = &framebufInfo.PixelMasks; + RtlZeroMemory(&framebufInfo, sizeof(framebufInfo)); framebufInfo.BaseAddress = BaseAddress; @@ -110,8 +125,66 @@ VidFbInitializeVideo( return FALSE; } + //ASSERT((BitsPerPixel <= 8 && !PixelMasks) || (BitsPerPixel > 8)); + if (BitsPerPixel > 8) + { + if (!PixelMasks || + (PixelMasks->RedMask == 0 && + PixelMasks->GreenMask == 0 && + PixelMasks->BlueMask == 0 /* && + PixelMasks->ReservedMask == 0 */)) + { + /* Determine pixel mask given color depth and color channel */ + switch (BitsPerPixel) + { + case 32: + case 24: /* 8:8:8 */ + BitMasks->RedMask = 0x00FF0000; // 0x00FF0000; + BitMasks->GreenMask = 0x0000FF00; // 0x00FF0000 >> 8; + BitMasks->BlueMask = 0x000000FF; // 0x00FF0000 >> 16; + BitMasks->ReservedMask = ((1 << (BitsPerPixel - 24)) - 1) << 24; + break; + case 16: /* 5:6:5 */ + BitMasks->RedMask = 0xF800; // 0xF800; + BitMasks->GreenMask = 0x07E0; // (0xF800 >> 5) | 0x20; + BitMasks->BlueMask = 0x001F; // 0xF800 >> 11; + BitMasks->ReservedMask = 0; + break; + case 15: /* 5:5:5 */ + BitMasks->RedMask = 0x7C00; // 0x7C00; + BitMasks->GreenMask = 0x03E0; // 0x7C00 >> 5; + BitMasks->BlueMask = 0x001F; // 0x7C00 >> 10; + BitMasks->ReservedMask = 0x8000; + break; + default: + /* Unsupported BPP */ + UNIMPLEMENTED; + RtlZeroMemory(BitMasks, sizeof(*BitMasks)); + } + } + else + { + /* Copy the pixel masks */ + RtlCopyMemory(BitMasks, PixelMasks, sizeof(*BitMasks)); + } + } + else + { + /* Palettized modes don't use masks */ + RtlZeroMemory(BitMasks, sizeof(*BitMasks)); + } + #if DBG VidFbPrintFramebufferInfo(); + { + ULONG BppFromMasks = + PixelBitmasksToBpp(BitMasks->RedMask, + BitMasks->GreenMask, + BitMasks->BlueMask, + BitMasks->ReservedMask); + TRACE("BitsPerPixel = %lu , BppFromMasks = %lu\n", BitsPerPixel, BppFromMasks); + //ASSERT(BitsPerPixel == BppFromMasks); + } #endif return TRUE; diff --git a/boot/freeldr/freeldr/arch/vidfb.h b/boot/freeldr/freeldr/arch/vidfb.h index b409692da7d..1540713b7a8 100644 --- a/boot/freeldr/freeldr/arch/vidfb.h +++ b/boot/freeldr/freeldr/arch/vidfb.h @@ -7,6 +7,49 @@ #pragma once +#include "twidbits.h" + +/** + * @brief + * Physical format of an RGB pixel, specified with per-component bit-masks. + * A bit being set defines those used for the given color component, such + * as Red, Green, Blue, or Reserved. + * + * @note + * Supports up to 32 bits-per-pixel deep pixels. + **/ +typedef struct _PIXEL_BITMASK +{ + ULONG RedMask; + ULONG GreenMask; + ULONG BlueMask; + ULONG ReservedMask; +} PIXEL_BITMASK, *PPIXEL_BITMASK; + + +/** + * @brief + * Calculates the number of bits per pixel ("PixelDepth") for + * the given pixel format, given by the pixel color masks. + * + * @remark + * See UEFI Spec Rev.2.10 Section 12.9 "Graphics Output Protocol": + * example code "GetPixelElementSize()" function. + **/ +FORCEINLINE +ULONG +PixelBitmasksToBpp( + _In_ ULONG RedMask, + _In_ ULONG GreenMask, + _In_ ULONG BlueMask, + _In_ ULONG ReservedMask) +{ + ULONG CompoundMask = (RedMask | GreenMask | BlueMask | ReservedMask); + /* Alternatively, the calculation could be done by finding the highest + * bit set in the combined pixel color masks, if they are packed together. */ + return CountNumberOfBits(CompoundMask); // FindHighestSetBit(CompoundMask); +} + BOOLEAN VidFbInitializeVideo( _In_ ULONG_PTR BaseAddress, @@ -14,7 +57,8 @@ VidFbInitializeVideo( _In_ UINT32 ScreenWidth, _In_ UINT32 ScreenHeight, _In_ UINT32 PixelsPerScanLine, - _In_ UINT32 BitsPerPixel); + _In_ UINT32 BitsPerPixel, + _In_opt_ PPIXEL_BITMASK PixelMasks); VOID VidFbClearScreenColor(