gst hdr

在 GStreamer 中解析视频流的 HDR(高动态范围)信息并将其传递给 DRM 显示模块,涉及以下几个步骤:

解析 HDR 元数据:从视频流中提取 HDR 元数据(如亮度、色彩空间等)。

配置 DRM 显示模块:将 HDR 元数据传递给 DRM 显示模块,以正确显示 HDR 内容。

以下是详细的实现步骤和示例代码:

  1. 解析 HDR 元数据

HDR 元数据通常嵌入在视频流的 SEI(Supplemental Enhancement Information)或容器格式(如 MP4、MKV)中。GStreamer 提供了插件和工具来解析这些元数据。

1.1 使用 gst-discoverer 检查 HDR 信息

gst-discoverer 是 GStreamer 提供的一个工具,用于分析媒体文件的元数据。

gst-discoverer-1.0 video_with_hdr.mp4

输出示例:

HDR Metadata:
  Mastering Display Color Volume:
    Red: (0.6800, 0.3200)
    Green: (0.2650, 0.6900)
    Blue: (0.1500, 0.0600)
    White Point: (0.3127, 0.3290)
    Max Luminance: 1000.0000 cd/m²
    Min Luminance: 0.0010 cd/

1.2 使用 gst-plugins-bad 解析 HDR 元数据

gst-plugins-bad 提供了 h264parse、h265parse 等插件,可以解析 HDR 元数据。

示例 Pipeline

gst-launch-1.0 filesrc location=video_with_hdr.mp4 ! qtdemux ! h265parse ! avdec_h265 ! videoconvert ! autovideosink

qtdemux:解复用 MP4 容器。

h265parse:解析 H.265 视频流中的 HDR 元数据。

avdec_h265:解码 H.265 视频流。

videoconvert:转换视频格式。

autovideosink:自动选择视频输出。

  1. 配置 DRM 显示模块

DRM 显示模块需要接收 HDR 元数据,以正确配置显示器的 HDR 模式。

2.1 使用 libdrm 设置 HDR 元数据

DRM 提供了 drmModeCreatePropertyBlob 和 drmModeObjectSetProperty 等函数,用于设置 HDR 元数据。

示例代码

#include <xf86drm.h>
#include <xf86drmMode.h>

void set_hdr_metadata(int fd, uint32_t connector_id, uint32_t blob_id) {
    drmModeObjectSetProperty(fd, connector_id, DRM_MODE_OBJECT_CONNECTOR,
                             DRM_MODE_PROP_HDR_METADATA, blob_id);
}

int main() {
    int fd = drmOpen("card0", NULL);
    if (fd < 0) {
        fprintf(stderr, "Failed to open DRM device\n");
        return -1;
    }

    // 创建 HDR 元数据 Blob
    struct hdr_metadata hdr_meta = {
        .max_cll = 1000,
        .max_fall = 400,
        .min_dml = 0.001,
        .max_dml = 1000,
    };

    uint32_t blob_id;
    drmModeCreatePropertyBlob(fd, &hdr_meta, sizeof(hdr_meta), &blob_id);

    // 设置 HDR 元数据
    set_hdr_metadata(fd, connector_id, blob_id);

    drmClose(fd);
    return 0;
}

drmModeCreatePropertyBlob:创建 HDR 元数据的 Blob。

drmModeObjectSetProperty:将 HDR 元数据设置到 DRM 连接器。

HDR_OUTPUT_METADATA:

userspace/app can get HDR cap info from sink device EDID, and then send it to driver, driver will packet it in SDP of HDMI then send it to sink device
drm driver will pack the sink-HDR-metadata + DRM hardware capability to sink device
Driver will pack sink-device-HDR + video/game HDR + hardware HDR info into HDMI/DP info packet
在这里插入图片描述

struct rk_hdr_metadata_infoframe {
    /**
     * @eotf: Electro-Optical Transfer Function (EOTF)
     * used in the stream.
     */
    unsigned char eotf;  // 0: SDR-Gamma, 1: HDR-Gamma, 2: SMPTE2084, 3: HLG
    /**
     * @metadata_type: Static_Metadata_Descriptor_ID.
     */
    unsigned char metadata_type;
    /**
     * @display_primaries: Color Primaries of the Data.
     * These are coded as unsigned 16-bit values in units of
     * 0.00002, where 0x0000 represents zero and 0xC350
     * represents 1.0000.
     * @display_primaries.x: X cordinate of color primary.
     * @display_primaries.y: Y cordinate of color primary.
     */
    struct {
        unsigned short x, y;
    } display_primaries[3];
    /**
     * @white_point: White Point of Colorspace Data.
     * These are coded as unsigned 16-bit values in units of
     * 0.00002, where 0x0000 represents zero and 0xC350
     * represents 1.0000.
     * @white_point.x: X cordinate of whitepoint of color primary.
     * @white_point.y: Y cordinate of whitepoint of color primary.
     */
    struct {
        unsigned short x, y;
    } white_point;
    /**
     * @max_display_mastering_luminance: Max Mastering Display Luminance.
     * This value is coded as an unsigned 16-bit value in units of 1 cd/m2,
     * where 0x0001 represents 1 cd/m2 and 0xFFFF represents 65535 cd/m2.
     */
    unsigned short max_display_mastering_luminance;
    /**
     * @min_display_mastering_luminance: Min Mastering Display Luminance.
     * This value is coded as an unsigned 16-bit value in units of
     * 0.0001 cd/m2, where 0x0001 represents 0.0001 cd/m2 and 0xFFFF
     * represents 6.5535 cd/m2.
     */
    unsigned short min_display_mastering_luminance;
    /**
     * @max_cll: Max Content Light Level.
     * This value is coded as an unsigned 16-bit value in units of 1 cd/m2,
     * where 0x0001 represents 1 cd/m2 and 0xFFFF represents 65535 cd/m2.
     */
    unsigned short max_cll;
    /**
     * @max_fall: Max Frame Average Light Level.
     * This value is coded as an unsigned 16-bit value in units of 1 cd/m2,
     * where 0x0001 represents 1 cd/m2 and 0xFFFF represents 65535 cd/m2.
     */
    unsigned short max_fall;
};
  1. GStreamer 和 DRM 的集成

将 GStreamer 解析的 HDR 元数据传递给 DRM 显示模块,可以通过自定义 GStreamer 插件或应用程序逻辑实现。

3.1 自定义 GStreamer 插件

编写一个 GStreamer 插件,解析 HDR 元数据并通过 DRM API 设置到显示模块。

示例插件代码
#include <gst/gst.h>
#include <xf86drm.h>
#include <xf86drmMode.h>

static void set_hdr_metadata(int fd, uint32_t connector_id, uint32_t blob_id) {
    drmModeObjectSetProperty(fd, connector_id, DRM_MODE_OBJECT_CONNECTOR,
                             DRM_MODE_PROP_HDR_METADATA, blob_id);
}

static GstFlowReturn transform_ip(GstBaseTransform *trans, GstBuffer *buf) {
    // 解析 HDR 元数据
    GstVideoMeta *meta = gst_buffer_get_video_meta(buf);
    if (meta && meta->hdr_metadata) {
        // 设置 HDR 元数据到 DRM
        int fd = drmOpen("card0", NULL);
        if (fd < 0) {
            GST_ERROR("Failed to open DRM device");
            return GST_FLOW_ERROR;
        }

        uint32_t blob_id;
        drmModeCreatePropertyBlob(fd, meta->hdr_metadata, sizeof(*meta->hdr_metadata), &blob_id);
        set_hdr_metadata(fd, connector_id, blob_id);

        drmClose(fd);
    }

    return GST_FLOW_OK;
}

static gboolean plugin_init(GstPlugin *plugin) {
    return gst_element_register(plugin, "hdr_drm_sink", GST_RANK_NONE, GST_TYPE_BASE_TRANSFORM);
}

GST_PLUGIN_DEFINE(
    GST_VERSION_MAJOR,
    GST_VERSION_MINOR,
    hdr_drm_sink,
    "HDR DRM Sink",
    plugin_init,
    "1.0",
    "LGPL",
    "GStreamer",
    "<https://gstreamer.freedesktop.org>"
)

transform_ip:在视频帧处理期间解析 HDR 元数据并设置到 DRM。
plugin_init:注册自定义插件。
在这里插入图片描述
3.2 使用 GStreamer 应用程序

在 GStreamer 应用程序中解析 HDR 元数据,并通过 DRM API 设置到显示模块。
示例应用程序代码

#include <gst/gst.h>
#include <xf86drm.h>
#include <xf86drmMode.h>

static void on_pad_added(GstElement *element, GstPad *pad, gpointer data) {
    GstPad *sinkpad = gst_element_get_static_pad(element, "sink");
    gst_pad_link(pad, sinkpad);
    gst_object_unref(sinkpad);
}

int main(int argc, char *argv[]) {
    gst_init(&argc, &argv);

    GstElement *pipeline = gst_pipeline_new("hdr-pipeline");
    GstElement *src = gst_element_factory_make("filesrc", "src");
    GstElement *demux = gst_element_factory_make("qtdemux", "demux");
    GstElement *parse = gst_element_factory_make("h265parse", "parse");
    GstElement *decode = gst_element_factory_make("avdec_h265", "decode");
    GstElement *sink = gst_element_factory_make("autovideosink", "sink");

    gst_bin_add_many(GST_BIN(pipeline), src, demux, parse, decode, sink, NULL);
    gst_element_link_many(src, demux, parse, decode, sink, NULL);

    g_signal_connect(demux, "pad-added", G_CALLBACK(on_pad_added), parse);

    gst_element_set_state(pipeline, GST_STATE_PLAYING);

    GstBus *bus = gst_element_get_bus(pipeline);
    GstMessage *msg = gst_bus_timed_pop_filtered(bus, GST_CLOCK_TIME_NONE, GST_MESSAGE_ERROR | GST_MESSAGE_EOS);

    if (msg != NULL) {
        gst_message_unref(msg);
    }

    gst_object_unref(bus);
    gst_element_set_state(pipeline, GST_STATE_NULL);
    gst_object_unref(pipeline);

    return 0;
}
  1. 小结

使用 GStreamer 解析视频流中的 HDR 元数据。

通过 DRM API 将 HDR 元数据设置到显示模块。

可以通过自定义 GStreamer 插件或应用程序逻辑实现 HDR 元数据的传递和显示配置。

/** @file The application to show the Boot Manager Menu. Copyright (c) 2011 - 2021, Intel Corporation. All rights reserved.<BR> SPDX-License-Identifier: BSD-2-Clause-Patent **/ #include "BootManagerMenu.h" EFI_HII_HANDLE gStringPackHandle; BOOLEAN mModeInitialized = FALSE; // // Boot video resolution and text mode. // UINT32 mBootHorizontalResolution = 0; UINT32 mBootVerticalResolution = 0; UINT32 mBootTextModeColumn = 0; UINT32 mBootTextModeRow = 0; // // BIOS setup video resolution and text mode. // UINT32 mSetupTextModeColumn = 0; UINT32 mSetupTextModeRow = 0; UINT32 mSetupHorizontalResolution = 0; UINT32 mSetupVerticalResolution = 0; /** Prints a unicode string to the default console, at the supplied cursor position, using L"%s" format. @param Column The cursor position to print the string at. @param Row The cursor position to print the string at @param String String pointer. @return Length of string printed to the console **/ UINTN PrintStringAt ( IN UINTN Column, IN UINTN Row, IN CHAR16 *String ) { UINTN ScreenWidth; UINTN ScreenRows; CHAR16 *TurncateString; EFI_STATUS Status; UINTN ShowingLength; gST->ConOut->SetCursorPosition (gST->ConOut, Column, Row); gST->ConOut->QueryMode ( gST->ConOut, gST->ConOut->Mode->Mode, &ScreenWidth, &ScreenRows ); if ((Column > (ScreenWidth - 1)) || (Row > (ScreenRows - 1))) { return 0; } if ((StrLen (String) + Column) > (ScreenWidth - 1)) { // // | - ScreenWidth - | // ...Column..................... // TurncateString length should leave one character for draw box and // require one character for string end. // ShowingLength = ScreenWidth - Column - 1; TurncateString = AllocatePool ((ShowingLength + 1) * sizeof (CHAR16)); if (TurncateString == NULL) { return 0; } Status = StrnCpyS (TurncateString, ShowingLength + 1, String, ShowingLength - 3); if (EFI_ERROR (Status)) { FreePool (TurncateString); return 0; } *(TurncateString + ShowingLength - 3) = L'.'; *(TurncateString + ShowingLength - 2) = L'.'; *(TurncateString + ShowingLength - 1) = L'.'; *(TurncateString + ShowingLength) = L'\0'; ShowingLength = Print (L"%s", TurncateString); FreePool (TurncateString); return ShowingLength; } else { return Print (L"%s", String); } } /** Prints a character to the default console, at the supplied cursor position, using L"%c" format. @param Column The cursor position to print the string at. @param Row The cursor position to print the string at. @param Character Character to print. @return Length of string printed to the console. **/ UINTN PrintCharAt ( IN UINTN Column, IN UINTN Row, CHAR16 Character ) { UINTN ScreenWidth; UINTN ScreenRows; gST->ConOut->SetCursorPosition (gST->ConOut, Column, Row); gST->ConOut->QueryMode ( gST->ConOut, gST->ConOut->Mode->Mode, &ScreenWidth, &ScreenRows ); if ((Column > (ScreenWidth - 1)) || (Row > (ScreenRows - 1))) { return 0; } return Print (L"%c", Character); } /** Count the storage space of a Unicode string which uses current language to get from input string ID. @param StringId The input string to be counted. @return Storage space for the input string. **/ UINTN GetLineWidth ( IN EFI_STRING_ID StringId ) { UINTN Index; UINTN IncrementValue; EFI_STRING String; UINTN LineWidth; LineWidth = 0; String = HiiGetString (gStringPackHandle, StringId, NULL); if (String != NULL) { Index = 0; IncrementValue = 1; do { // // Advance to the null-terminator or to the first width directive // for ( ; (String[Index] != NARROW_CHAR) && (String[Index] != WIDE_CHAR) && (String[Index] != 0); Index++, LineWidth = LineWidth + IncrementValue ) { } // // We hit the null-terminator, we now have a count // if (String[Index] == 0) { break; } // // We encountered a narrow directive - strip it from the size calculation since it doesn't get printed // and also set the flag that determines what we increment by.(if narrow, increment by 1, if wide increment by 2) // if (String[Index] == NARROW_CHAR) { // // Skip to the next character // Index++; IncrementValue = 1; } else { // // Skip to the next character // Index++; IncrementValue = 2; } } while (String[Index] != 0); FreePool (String); } return LineWidth; } /** This function uses calculate the boot menu location, size and scroll bar information. @param BootMenuData The boot menu data to be processed. @return EFI_SUCCESS calculate boot menu information successful. @retval EFI_INVALID_PARAMETER Input parameter is invalid **/ EFI_STATUS InitializeBootMenuScreen ( IN OUT BOOT_MENU_POPUP_DATA *BootMenuData ) { UINTN MaxStrWidth; UINTN StrWidth; UINTN Index; UINTN Column; UINTN Row; UINTN MaxPrintRows; UINTN UnSelectableItmes; if (BootMenuData == NULL) { return EFI_INVALID_PARAMETER; } // // Get maximum string width // MaxStrWidth = 0; for (Index = 0; Index < TITLE_TOKEN_COUNT; Index++) { StrWidth = GetLineWidth (BootMenuData->TitleToken[Index]); MaxStrWidth = MaxStrWidth > StrWidth ? MaxStrWidth : StrWidth; } for (Index = 0; Index < BootMenuData->ItemCount; Index++) { StrWidth = GetLineWidth (BootMenuData->PtrTokens[Index]); MaxStrWidth = MaxStrWidth > StrWidth ? MaxStrWidth : StrWidth; } for (Index = 0; Index < HELP_TOKEN_COUNT; Index++) { StrWidth = GetLineWidth (BootMenuData->HelpToken[Index]); MaxStrWidth = MaxStrWidth > StrWidth ? MaxStrWidth : StrWidth; } // // query current row and column to calculate boot menu location // gST->ConOut->QueryMode ( gST->ConOut, gST->ConOut->Mode->Mode, &Column, &Row ); MaxPrintRows = Row - 6; UnSelectableItmes = TITLE_TOKEN_COUNT + 2 + HELP_TOKEN_COUNT + 2; if (MaxStrWidth + 8 > Column) { BootMenuData->MenuScreen.Width = Column; } else { BootMenuData->MenuScreen.Width = MaxStrWidth + 8; } if (BootMenuData->ItemCount + UnSelectableItmes > MaxPrintRows) { BootMenuData->MenuScreen.Height = MaxPrintRows; BootMenuData->ScrollBarControl.HasScrollBar = TRUE; BootMenuData->ScrollBarControl.ItemCountPerScreen = MaxPrintRows - UnSelectableItmes; BootMenuData->ScrollBarControl.FirstItem = 0; BootMenuData->ScrollBarControl.LastItem = MaxPrintRows - UnSelectableItmes - 1; } else { BootMenuData->MenuScreen.Height = BootMenuData->ItemCount + UnSelectableItmes; BootMenuData->ScrollBarControl.HasScrollBar = FALSE; BootMenuData->ScrollBarControl.ItemCountPerScreen = BootMenuData->ItemCount; BootMenuData->ScrollBarControl.FirstItem = 0; BootMenuData->ScrollBarControl.LastItem = BootMenuData->ItemCount - 1; } BootMenuData->MenuScreen.StartCol = (Column - BootMenuData->MenuScreen.Width) / 2; BootMenuData->MenuScreen.StartRow = (Row - BootMenuData->MenuScreen.Height) / 2; return EFI_SUCCESS; } /** This function uses check boot option is wheher setup application or no @param BootOption Pointer to EFI_BOOT_MANAGER_LOAD_OPTION array. @retval TRUE This boot option is setup application. @retval FALSE This boot options isn't setup application **/ BOOLEAN IsBootManagerMenu ( IN EFI_BOOT_MANAGER_LOAD_OPTION *BootOption ) { EFI_STATUS Status; EFI_BOOT_MANAGER_LOAD_OPTION BootManagerMenu; Status = EfiBootManagerGetBootManagerMenu (&BootManagerMenu); if (!EFI_ERROR (Status)) { EfiBootManagerFreeLoadOption (&BootManagerMenu); } return (BOOLEAN)(!EFI_ERROR (Status) && (BootOption->OptionNumber == BootManagerMenu.OptionNumber)); } /** Return whether to ignore the boot option. @param BootOption Pointer to EFI_BOOT_MANAGER_LOAD_OPTION to check. @retval TRUE Ignore the boot option. @retval FALSE Do not ignore the boot option. **/ BOOLEAN IgnoreBootOption ( IN EFI_BOOT_MANAGER_LOAD_OPTION *BootOption ) { EFI_STATUS Status; EFI_DEVICE_PATH_PROTOCOL *ImageDevicePath; // // Ignore myself. // Status = gBS->HandleProtocol (gImageHandle, &gEfiLoadedImageDevicePathProtocolGuid, (VOID **)&ImageDevicePath); ASSERT_EFI_ERROR (Status); if (CompareMem (BootOption->FilePath, ImageDevicePath, GetDevicePathSize (ImageDevicePath)) == 0) { return TRUE; } // // Do not ignore Boot Manager Menu. // if (IsBootManagerMenu (BootOption)) { return FALSE; } // // Ignore the hidden/inactive boot option. // if (((BootOption->Attributes & LOAD_OPTION_HIDDEN) != 0) || ((BootOption->Attributes & LOAD_OPTION_ACTIVE) == 0)) { return TRUE; } return FALSE; } /** This function uses to initialize boot menu data @param BootOption Pointer to EFI_BOOT_MANAGER_LOAD_OPTION array. @param BootOptionCount Number of boot option. @param BootMenuData The Input BootMenuData to be initialized. @retval EFI_SUCCESS Initialize boot menu data successful. @retval EFI_INVALID_PARAMETER Input parameter is invalid. **/ EFI_STATUS InitializeBootMenuData ( IN EFI_BOOT_MANAGER_LOAD_OPTION *BootOption, IN UINTN BootOptionCount, OUT BOOT_MENU_POPUP_DATA *BootMenuData ) { UINTN Index; UINTN StrIndex; if ((BootOption == NULL) || (BootMenuData == NULL)) { return EFI_INVALID_PARAMETER; } BootMenuData->TitleToken[0] = STRING_TOKEN (STR_BOOT_POPUP_MENU_TITLE_STRING); BootMenuData->PtrTokens = AllocateZeroPool (BootOptionCount * sizeof (EFI_STRING_ID)); ASSERT (BootMenuData->PtrTokens != NULL); // // Skip boot option which created by BootNext Variable // for (StrIndex = 0, Index = 0; Index < BootOptionCount; Index++) { if (IgnoreBootOption (&BootOption[Index])) { continue; } ASSERT (BootOption[Index].Description != NULL); BootMenuData->PtrTokens[StrIndex++] = HiiSetString ( gStringPackHandle, 0, BootOption[Index].Description, NULL ); } BootMenuData->ItemCount = StrIndex; BootMenuData->HelpToken[0] = STRING_TOKEN (STR_BOOT_POPUP_MENU_HELP1_STRING); BootMenuData->HelpToken[1] = STRING_TOKEN (STR_BOOT_POPUP_MENU_HELP2_STRING); BootMenuData->HelpToken[2] = STRING_TOKEN (STR_BOOT_POPUP_MENU_HELP3_STRING); InitializeBootMenuScreen (BootMenuData); BootMenuData->SelectItem = 0; return EFI_SUCCESS; } /** This function uses input select item to highlight selected item and set current selected item in BootMenuData @param WantSelectItem The user wants to select item. @param BootMenuData The boot menu data to be processed @return EFI_SUCCESS Highlight selected item and update current selected item successful @retval EFI_INVALID_PARAMETER Input parameter is invalid **/ EFI_STATUS BootMenuSelectItem ( IN UINTN WantSelectItem, IN OUT BOOT_MENU_POPUP_DATA *BootMenuData ) { INT32 SavedAttribute; EFI_STRING String; UINTN StartCol; UINTN StartRow; UINTN PrintCol; UINTN PrintRow; UINTN TopShadeNum; UINTN LowShadeNum; UINTN FirstItem; UINTN LastItem; UINTN ItemCountPerScreen; UINTN Index; BOOLEAN RePaintItems; if ((BootMenuData == NULL) || (WantSelectItem >= BootMenuData->ItemCount)) { return EFI_INVALID_PARAMETER; } ASSERT (BootMenuData->ItemCount != 0); SavedAttribute = gST->ConOut->Mode->Attribute; RePaintItems = FALSE; StartCol = BootMenuData->MenuScreen.StartCol; StartRow = BootMenuData->MenuScreen.StartRow; // // print selectable items again and adjust scroll bar if need // if (BootMenuData->ScrollBarControl.HasScrollBar && ((WantSelectItem < BootMenuData->ScrollBarControl.FirstItem) || (WantSelectItem > BootMenuData->ScrollBarControl.LastItem) || (WantSelectItem == BootMenuData->SelectItem))) { ItemCountPerScreen = BootMenuData->ScrollBarControl.ItemCountPerScreen; // // Set first item and last item // if (WantSelectItem < BootMenuData->ScrollBarControl.FirstItem) { BootMenuData->ScrollBarControl.FirstItem = WantSelectItem; BootMenuData->ScrollBarControl.LastItem = WantSelectItem + ItemCountPerScreen - 1; } else if (WantSelectItem > BootMenuData->ScrollBarControl.LastItem) { BootMenuData->ScrollBarControl.FirstItem = WantSelectItem - ItemCountPerScreen + 1; BootMenuData->ScrollBarControl.LastItem = WantSelectItem; } gST->ConOut->SetAttribute (gST->ConOut, EFI_WHITE | EFI_BACKGROUND_BLUE); FirstItem = BootMenuData->ScrollBarControl.FirstItem; LastItem = BootMenuData->ScrollBarControl.LastItem; TopShadeNum = 0; if (FirstItem != 0) { TopShadeNum = (FirstItem * ItemCountPerScreen) / BootMenuData->ItemCount; if ((FirstItem * ItemCountPerScreen) % BootMenuData->ItemCount != 0) { TopShadeNum++; } PrintCol = StartCol + BootMenuData->MenuScreen.Width - 2; PrintRow = StartRow + TITLE_TOKEN_COUNT + 2; for (Index = 0; Index < TopShadeNum; Index++, PrintRow++) { PrintCharAt (PrintCol, PrintRow, BLOCKELEMENT_LIGHT_SHADE); } } LowShadeNum = 0; if (LastItem != BootMenuData->ItemCount - 1) { LowShadeNum = ((BootMenuData->ItemCount - 1 - LastItem) * ItemCountPerScreen) / BootMenuData->ItemCount; if (((BootMenuData->ItemCount - 1 - LastItem) * ItemCountPerScreen) % BootMenuData->ItemCount != 0) { LowShadeNum++; } PrintCol = StartCol + BootMenuData->MenuScreen.Width - 2; PrintRow = StartRow + TITLE_TOKEN_COUNT + 2 + ItemCountPerScreen - LowShadeNum; for (Index = 0; Index < LowShadeNum; Index++, PrintRow++) { PrintCharAt (PrintCol, PrintRow, BLOCKELEMENT_LIGHT_SHADE); } } PrintCol = StartCol + BootMenuData->MenuScreen.Width - 2; PrintRow = StartRow + TITLE_TOKEN_COUNT + 2 + TopShadeNum; for (Index = TopShadeNum; Index < ItemCountPerScreen - LowShadeNum; Index++, PrintRow++) { PrintCharAt (PrintCol, PrintRow, BLOCKELEMENT_FULL_BLOCK); } // // Clear selectable items first // PrintCol = StartCol + 1; PrintRow = StartRow + TITLE_TOKEN_COUNT + 2; String = AllocateZeroPool ((BootMenuData->MenuScreen.Width - 2) * sizeof (CHAR16)); ASSERT (String != NULL); for (Index = 0; Index < BootMenuData->MenuScreen.Width - 3; Index++) { String[Index] = 0x20; } for (Index = 0; Index < ItemCountPerScreen; Index++) { PrintStringAt (PrintCol, PrintRow + Index, String); } FreePool (String); // // print selectable items // for (Index = 0; Index < ItemCountPerScreen; Index++, PrintRow++) { String = HiiGetString (gStringPackHandle, BootMenuData->PtrTokens[Index + FirstItem], NULL); PrintStringAt (PrintCol, PrintRow, String); FreePool (String); } RePaintItems = TRUE; } // // if Want Select and selected item isn't the same and doesn't re-draw selectable // items, clear select item // FirstItem = BootMenuData->ScrollBarControl.FirstItem; if ((WantSelectItem != BootMenuData->SelectItem) && !RePaintItems) { gST->ConOut->SetAttribute (gST->ConOut, EFI_WHITE | EFI_BACKGROUND_BLUE); String = HiiGetString (gStringPackHandle, BootMenuData->PtrTokens[BootMenuData->SelectItem], NULL); PrintCol = StartCol + 1; PrintRow = StartRow + 3 + BootMenuData->SelectItem - FirstItem; PrintStringAt (PrintCol, PrintRow, String); FreePool (String); } // // Print want to select item // gST->ConOut->SetAttribute (gST->ConOut, EFI_WHITE | EFI_BACKGROUND_BLACK); String = HiiGetString (gStringPackHandle, BootMenuData->PtrTokens[WantSelectItem], NULL); PrintCol = StartCol + 1; PrintRow = StartRow + TITLE_TOKEN_COUNT + 2 + WantSelectItem - FirstItem; PrintStringAt (PrintCol, PrintRow, String); FreePool (String); gST->ConOut->SetAttribute (gST->ConOut, SavedAttribute); BootMenuData->SelectItem = WantSelectItem; return EFI_SUCCESS; } /** This function uses to draw boot popup menu @param BootMenuData The Input BootMenuData to be processed. @retval EFI_SUCCESS Draw boot popup menu successful. **/ EFI_STATUS DrawBootPopupMenu ( IN BOOT_MENU_POPUP_DATA *BootMenuData ) { EFI_STRING String; UINTN Index; UINTN Width; UINTN StartCol; UINTN StartRow; UINTN PrintRow; UINTN PrintCol; UINTN LineWidth; INT32 SavedAttribute; UINTN ItemCountPerScreen; gST->ConOut->ClearScreen (gST->ConOut); SavedAttribute = gST->ConOut->Mode->Attribute; gST->ConOut->SetAttribute (gST->ConOut, EFI_WHITE | EFI_BACKGROUND_BLUE); Width = BootMenuData->MenuScreen.Width; StartCol = BootMenuData->MenuScreen.StartCol; StartRow = BootMenuData->MenuScreen.StartRow; ItemCountPerScreen = BootMenuData->ScrollBarControl.ItemCountPerScreen; PrintRow = StartRow; gST->ConOut->EnableCursor (gST->ConOut, FALSE); // // Draw Boot popup menu screen // PrintCharAt (StartCol, PrintRow, BOXDRAW_DOWN_RIGHT); for (Index = 1; Index < Width - 1; Index++) { PrintCharAt (StartCol + Index, PrintRow, BOXDRAW_HORIZONTAL); } PrintCharAt (StartCol + Width - 1, PrintRow, BOXDRAW_DOWN_LEFT); // // Draw the screen for title // String = AllocateZeroPool ((Width - 1) * sizeof (CHAR16)); ASSERT (String != NULL); for (Index = 0; Index < Width - 2; Index++) { String[Index] = 0x20; } for (Index = 0; Index < TITLE_TOKEN_COUNT; Index++) { PrintRow++; PrintCharAt (StartCol, PrintRow, BOXDRAW_VERTICAL); PrintStringAt (StartCol + 1, PrintRow, String); PrintCharAt (StartCol + Width - 1, PrintRow, BOXDRAW_VERTICAL); } PrintRow++; PrintCharAt (StartCol, PrintRow, BOXDRAW_VERTICAL_RIGHT); for (Index = 1; Index < Width - 1; Index++) { PrintCharAt (StartCol + Index, PrintRow, BOXDRAW_HORIZONTAL); } PrintCharAt (StartCol + Width - 1, PrintRow, BOXDRAW_VERTICAL_LEFT); // // Draw screen for selectable items // for (Index = 0; Index < ItemCountPerScreen; Index++) { PrintRow++; PrintCharAt (StartCol, PrintRow, BOXDRAW_VERTICAL); PrintStringAt (StartCol + 1, PrintRow, String); PrintCharAt (StartCol + Width - 1, PrintRow, BOXDRAW_VERTICAL); } PrintRow++; PrintCharAt (StartCol, PrintRow, BOXDRAW_VERTICAL_RIGHT); for (Index = 1; Index < Width - 1; Index++) { PrintCharAt (StartCol + Index, PrintRow, BOXDRAW_HORIZONTAL); } PrintCharAt (StartCol + Width - 1, PrintRow, BOXDRAW_VERTICAL_LEFT); // // Draw screen for Help // for (Index = 0; Index < HELP_TOKEN_COUNT; Index++) { PrintRow++; PrintCharAt (StartCol, PrintRow, BOXDRAW_VERTICAL); PrintStringAt (StartCol + 1, PrintRow, String); PrintCharAt (StartCol + Width - 1, PrintRow, BOXDRAW_VERTICAL); } FreePool (String); PrintRow++; PrintCharAt (StartCol, PrintRow, BOXDRAW_UP_RIGHT); for (Index = 1; Index < Width - 1; Index++) { PrintCharAt (StartCol + Index, PrintRow, BOXDRAW_HORIZONTAL); } PrintCharAt (StartCol + Width - 1, PrintRow, BOXDRAW_UP_LEFT); // // print title strings // PrintRow = StartRow + 1; for (Index = 0; Index < TITLE_TOKEN_COUNT; Index++, PrintRow++) { String = HiiGetString (gStringPackHandle, BootMenuData->TitleToken[Index], NULL); LineWidth = GetLineWidth (BootMenuData->TitleToken[Index]); PrintCol = StartCol + (Width - LineWidth) / 2; PrintStringAt (PrintCol, PrintRow, String); FreePool (String); } // // print selectable items // PrintCol = StartCol + 1; PrintRow = StartRow + TITLE_TOKEN_COUNT + 2; for (Index = 0; Index < ItemCountPerScreen; Index++, PrintRow++) { String = HiiGetString (gStringPackHandle, BootMenuData->PtrTokens[Index], NULL); PrintStringAt (PrintCol, PrintRow, String); FreePool (String); } // // Print Help strings // PrintRow++; for (Index = 0; Index < HELP_TOKEN_COUNT; Index++, PrintRow++) { String = HiiGetString (gStringPackHandle, BootMenuData->HelpToken[Index], NULL); LineWidth = GetLineWidth (BootMenuData->HelpToken[Index]); PrintCol = StartCol + (Width - LineWidth) / 2; PrintStringAt (PrintCol, PrintRow, String); FreePool (String); } // // Print scroll bar if has scroll bar // if (BootMenuData->ScrollBarControl.HasScrollBar) { PrintCol = StartCol + Width - 2; PrintRow = StartRow + 2; PrintCharAt (PrintCol, PrintRow, GEOMETRICSHAPE_UP_TRIANGLE); PrintCharAt (PrintCol + 1, PrintRow, BOXDRAW_VERTICAL); PrintRow += (ItemCountPerScreen + 1); PrintCharAt (PrintCol, PrintRow, GEOMETRICSHAPE_DOWN_TRIANGLE); PrintCharAt (PrintCol + 1, PrintRow, BOXDRAW_VERTICAL); } gST->ConOut->SetAttribute (gST->ConOut, SavedAttribute); // // Print Selected item // BootMenuSelectItem (BootMenuData->SelectItem, BootMenuData); return EFI_SUCCESS; } /** This function uses to boot from selected item @param BootOptions Pointer to EFI_BOOT_MANAGER_LOAD_OPTION array. @param BootOptionCount Number of boot option. @param SelectItem Current selected item. **/ VOID BootFromSelectOption ( IN EFI_BOOT_MANAGER_LOAD_OPTION *BootOptions, IN UINTN BootOptionCount, IN UINTN SelectItem ) { UINTN ItemNum; UINTN Index; ASSERT (BootOptions != NULL); for (ItemNum = 0, Index = 0; Index < BootOptionCount; Index++) { if (IgnoreBootOption (&BootOptions[Index])) { continue; } if (ItemNum++ == SelectItem) { EfiBootManagerBoot (&BootOptions[Index]); break; } } } /** This function will change video resolution and text mode according to defined setup mode or defined boot mode @param IsSetupMode Indicate mode is changed to setup mode or boot mode. @retval EFI_SUCCESS Mode is changed successfully. @retval Others Mode failed to be changed. **/ EFI_STATUS EFIAPI BdsSetConsoleMode ( BOOLEAN IsSetupMode ) { EFI_GRAPHICS_OUTPUT_PROTOCOL *GraphicsOutput; EFI_SIMPLE_TEXT_OUTPUT_PROTOCOL *SimpleTextOut; UINTN SizeOfInfo; EFI_GRAPHICS_OUTPUT_MODE_INFORMATION *Info; UINT32 MaxGopMode; UINT32 MaxTextMode; UINT32 ModeNumber; UINT32 NewHorizontalResolution; UINT32 NewVerticalResolution; UINT32 NewColumns; UINT32 NewRows; UINTN HandleCount; EFI_HANDLE *HandleBuffer; EFI_STATUS Status; UINTN Index; UINTN CurrentColumn; UINTN CurrentRow; MaxGopMode = 0; MaxTextMode = 0; // // Get current video resolution and text mode // Status = gBS->HandleProtocol ( gST->ConsoleOutHandle, &gEfiGraphicsOutputProtocolGuid, (VOID **)&GraphicsOutput ); if (EFI_ERROR (Status)) { GraphicsOutput = NULL; } Status = gBS->HandleProtocol ( gST->ConsoleOutHandle, &gEfiSimpleTextOutProtocolGuid, (VOID **)&SimpleTextOut ); if (EFI_ERROR (Status)) { SimpleTextOut = NULL; } if ((GraphicsOutput == NULL) || (SimpleTextOut == NULL)) { return EFI_UNSUPPORTED; } if (IsSetupMode) { // // The required resolution and text mode is setup mode. // NewHorizontalResolution = mSetupHorizontalResolution; NewVerticalResolution = mSetupVerticalResolution; NewColumns = mSetupTextModeColumn; NewRows = mSetupTextModeRow; } else { // // The required resolution and text mode is boot mode. // NewHorizontalResolution = mBootHorizontalResolution; NewVerticalResolution = mBootVerticalResolution; NewColumns = mBootTextModeColumn; NewRows = mBootTextModeRow; } if (GraphicsOutput != NULL) { MaxGopMode = GraphicsOutput->Mode->MaxMode; } if (SimpleTextOut != NULL) { MaxTextMode = SimpleTextOut->Mode->MaxMode; } // // 1. If current video resolution is same with required video resolution, // video resolution need not be changed. // 1.1. If current text mode is same with required text mode, text mode need not be changed. // 1.2. If current text mode is different from required text mode, text mode need be changed. // 2. If current video resolution is different from required video resolution, we need restart whole console drivers. // for (ModeNumber = 0; ModeNumber < MaxGopMode; ModeNumber++) { Status = GraphicsOutput->QueryMode ( GraphicsOutput, ModeNumber, &SizeOfInfo, &Info ); if (!EFI_ERROR (Status)) { if ((Info->HorizontalResolution == NewHorizontalResolution) && (Info->VerticalResolution == NewVerticalResolution)) { if ((GraphicsOutput->Mode->Info->HorizontalResolution == NewHorizontalResolution) && (GraphicsOutput->Mode->Info->VerticalResolution == NewVerticalResolution)) { // // Current resolution is same with required resolution, check if text mode need be set // Status = SimpleTextOut->QueryMode (SimpleTextOut, SimpleTextOut->Mode->Mode, &CurrentColumn, &CurrentRow); ASSERT_EFI_ERROR (Status); if ((CurrentColumn == NewColumns) && (CurrentRow == NewRows)) { // // If current text mode is same with required text mode. Do nothing // FreePool (Info); return EFI_SUCCESS; } else { // // If current text mode is different from required text mode. Set new video mode // for (Index = 0; Index < MaxTextMode; Index++) { Status = SimpleTextOut->QueryMode (SimpleTextOut, Index, &CurrentColumn, &CurrentRow); if (!EFI_ERROR (Status)) { if ((CurrentColumn == NewColumns) && (CurrentRow == NewRows)) { // // Required text mode is supported, set it. // Status = SimpleTextOut->SetMode (SimpleTextOut, Index); ASSERT_EFI_ERROR (Status); // // Update text mode PCD. // Status = PcdSet32S (PcdConOutColumn, mSetupTextModeColumn); ASSERT_EFI_ERROR (Status); Status = PcdSet32S (PcdConOutRow, mSetupTextModeRow); ASSERT_EFI_ERROR (Status); FreePool (Info); return EFI_SUCCESS; } } } if (Index == MaxTextMode) { // // If required text mode is not supported, return error. // FreePool (Info); return EFI_UNSUPPORTED; } } } else { // // If current video resolution is not same with the new one, set new video resolution. // In this case, the driver which produces simple text out need be restarted. // Status = GraphicsOutput->SetMode (GraphicsOutput, ModeNumber); if (!EFI_ERROR (Status)) { FreePool (Info); break; } } } FreePool (Info); } } if (ModeNumber == MaxGopMode) { // // If the resolution is not supported, return error. // return EFI_UNSUPPORTED; } // // Set PCD to Inform GraphicsConsole to change video resolution. // Set PCD to Inform Consplitter to change text mode. // Status = PcdSet32S (PcdVideoHorizontalResolution, NewHorizontalResolution); ASSERT_EFI_ERROR (Status); Status = PcdSet32S (PcdVideoVerticalResolution, NewVerticalResolution); ASSERT_EFI_ERROR (Status); Status = PcdSet32S (PcdConOutColumn, NewColumns); ASSERT_EFI_ERROR (Status); Status = PcdSet32S (PcdConOutRow, NewRows); ASSERT_EFI_ERROR (Status); // // Video mode is changed, so restart graphics console driver and higher level driver. // Reconnect graphics console driver and higher level driver. // Locate all the handles with GOP protocol and reconnect it. // Status = gBS->LocateHandleBuffer ( ByProtocol, &gEfiSimpleTextOutProtocolGuid, NULL, &HandleCount, &HandleBuffer ); if (!EFI_ERROR (Status)) { for (Index = 0; Index < HandleCount; Index++) { gBS->DisconnectController (HandleBuffer[Index], NULL, NULL); } for (Index = 0; Index < HandleCount; Index++) { gBS->ConnectController (HandleBuffer[Index], NULL, NULL, TRUE); } if (HandleBuffer != NULL) { FreePool (HandleBuffer); } } return EFI_SUCCESS; } /** Display the boot popup menu and allow user select boot item. @param ImageHandle The image handle. @param SystemTable The system table. @retval EFI_SUCCESS Boot from selected boot option, and return success from boot option @retval EFI_NOT_FOUND User select to enter setup or can not find boot option **/ EFI_STATUS EFIAPI BootManagerMenuEntry ( IN EFI_HANDLE ImageHandle, IN EFI_SYSTEM_TABLE *SystemTable ) { EFI_BOOT_MANAGER_LOAD_OPTION *BootOption; UINTN BootOptionCount; EFI_STATUS Status; BOOT_MENU_POPUP_DATA BootMenuData; UINTN Index; EFI_INPUT_KEY Key; BOOLEAN ExitApplication; UINTN SelectItem; EFI_BOOT_LOGO_PROTOCOL *BootLogo; EFI_GRAPHICS_OUTPUT_PROTOCOL *GraphicsOutput; EFI_SIMPLE_TEXT_OUTPUT_PROTOCOL *SimpleTextOut; UINTN BootTextColumn; UINTN BootTextRow; EFI_SIMPLE_TEXT_INPUT_EX_PROTOCOL *TextInputEx; Status = gBS->LocateProtocol( &gEfiSimpleTextInputExProtocolGuid, NULL, (VOID **)&TextInputEx ); if (EFI_ERROR(Status)) { TextInputEx = NULL; } // // Set Logo status invalid when boot manager menu is launched // BootLogo = NULL; Status = gBS->LocateProtocol (&gEfiBootLogoProtocolGuid, NULL, (VOID **)&BootLogo); if (!EFI_ERROR (Status) && (BootLogo != NULL)) { Status = BootLogo->SetBootLogo (BootLogo, NULL, 0, 0, 0, 0); ASSERT_EFI_ERROR (Status); } gBS->SetWatchdogTimer (0x0000, 0x0000, 0x0000, NULL); gStringPackHandle = HiiAddPackages ( &gEfiCallerIdGuid, gImageHandle, BootManagerMenuAppStrings, NULL ); ASSERT (gStringPackHandle != NULL); // // Connect all prior to entering the platform setup menu. // EfiBootManagerConnectAll (); EfiBootManagerRefreshAllBootOption (); BootOption = EfiBootManagerGetLoadOptions (&BootOptionCount, LoadOptionTypeBoot); if (!mModeInitialized) { // // After the console is ready, get current video resolution // and text mode before launching setup at first time. // Status = gBS->HandleProtocol ( gST->ConsoleOutHandle, &gEfiGraphicsOutputProtocolGuid, (VOID **)&GraphicsOutput ); if (EFI_ERROR (Status)) { GraphicsOutput = NULL; } Status = gBS->HandleProtocol ( gST->ConsoleOutHandle, &gEfiSimpleTextOutProtocolGuid, (VOID **)&SimpleTextOut ); if (EFI_ERROR (Status)) { SimpleTextOut = NULL; } if (GraphicsOutput != NULL) { // // Get current video resolution and text mode. // mBootHorizontalResolution = GraphicsOutput->Mode->Info->HorizontalResolution; mBootVerticalResolution = GraphicsOutput->Mode->Info->VerticalResolution; } if (SimpleTextOut != NULL) { Status = SimpleTextOut->QueryMode ( SimpleTextOut, SimpleTextOut->Mode->Mode, &BootTextColumn, &BootTextRow ); mBootTextModeColumn = (UINT32)BootTextColumn; mBootTextModeRow = (UINT32)BootTextRow; } // // Get user defined text mode for setup. // mSetupHorizontalResolution = PcdGet32 (PcdSetupVideoHorizontalResolution); mSetupVerticalResolution = PcdGet32 (PcdSetupVideoVerticalResolution); mSetupTextModeColumn = PcdGet32 (PcdSetupConOutColumn); mSetupTextModeRow = PcdGet32 (PcdSetupConOutRow); mModeInitialized = TRUE; } // // Set back to conventional setup resolution // BdsSetConsoleMode (TRUE); // // Initialize Boot menu data // Status = InitializeBootMenuData (BootOption, BootOptionCount, &BootMenuData); // // According to boot menu data to draw boot popup menu // DrawBootPopupMenu (&BootMenuData); // // check user input to determine want to re-draw or boot from user selected item // ExitApplication = FALSE; EFI_KEY_DATA KeyData; BOOLEAN UseEx = (TextInputEx != NULL); while (!ExitApplication) { gBS->WaitForEvent (1, &gST->ConIn->WaitForKey, &Index); if (UseEx){ Key=KeyData.Key; } else{ Status = gST->ConIn->ReadKeyStroke (gST->ConIn, &Key); } if (!EFI_ERROR (Status)) { if (UseEx){ if ((Key.UnicodeChar == 0x4C || Key.UnicodeChar == 0x6C) && (KeyData.KeyState.KeyShiftState & (EFI_LEFT_CONTROL_PRESSED | EFI_RIGHT_CONTROL_PRESSED))) { gRT->ResetSystem (EfiResetShutdown, EFI_SUCCESS, 0, NULL); CpuDeadLoop (); } } switch (Key.UnicodeChar) { case CHAR_NULL: switch (Key.ScanCode) { case SCAN_UP: SelectItem = BootMenuData.SelectItem == 0 ? BootMenuData.ItemCount - 1 : BootMenuData.SelectItem - 1; BootMenuSelectItem (SelectItem, &BootMenuData); break; case SCAN_DOWN: SelectItem = BootMenuData.SelectItem == BootMenuData.ItemCount - 1 ? 0 : BootMenuData.SelectItem + 1; BootMenuSelectItem (SelectItem, &BootMenuData); break; case SCAN_ESC: gST->ConOut->ClearScreen (gST->ConOut); ExitApplication = TRUE; // // Set boot resolution for normal boot // BdsSetConsoleMode (FALSE); break; default: break; } break; case CHAR_CARRIAGE_RETURN: gST->ConOut->ClearScreen (gST->ConOut); // // Set boot resolution for normal boot // BdsSetConsoleMode (FALSE); BootFromSelectOption (BootOption, BootOptionCount, BootMenuData.SelectItem); // // Back to boot manager menu again, set back to setup resolution // BdsSetConsoleMode (TRUE); DrawBootPopupMenu (&BootMenuData); break; default: break; } } } EfiBootManagerFreeLoadOptions (BootOption, BootOptionCount); FreePool (BootMenuData.PtrTokens); HiiRemovePackages (gStringPackHandle); return Status; } 帮我查下问题
08-16
<think>我们正在处理一个关于UEFI编程中未定义标识符gRT的问题。根据用户提供的引用和之前对话,我们需要提供解决方案。用户已经明确表示需要解决“未定义标识符gRT”的问题,并且我们之前已经给出过详细的回答。现在,我们根据用户提供的引用(特别是引用[3])来进一步解释系统表,并整合之前的解决方案。 引用[3]提到了系统表(System Table)的传递过程,以及它是如何从内核传递到用户空间的。在UEFI中,系统表是UEFI应用程序和驱动程序获取核心服务(包括运行时服务)的关键数据结构。其中,运行时服务表(gRT)就是系统表的一部分。 因此,我们可以从系统表的角度来补充说明如何获取gRT。 ### 解决方案补充(通过系统表获取运行时服务) 除了之前提到的使用`UefiRuntimeServicesTableLib`库的方法,我们还可以通过系统表(System Table)直接获取运行时服务表。在UEFI程序的入口函数中,我们会接收到系统表指针(`SystemTable`),通过它我们可以访问运行时服务。 #### 1. **在入口函数中获取系统表** 在UEFI模块(应用程序或驱动)的入口函数中,会接收到`SystemTable`参数,我们可以通过它来访问运行时服务: ```c EFI_STATUS UefiMain ( IN EFI_HANDLE ImageHandle, IN EFI_SYSTEM_TABLE *SystemTable // 系统表指针 ) { // 通过系统表获取运行时服务表 EFI_RUNTIME_SERVICES *gRT = SystemTable->RuntimeServices; // 现在可以使用gRT EFI_STATUS Status = gRT->GetTime(&Time, NULL); // ... } ``` #### 2. **全局变量定义** 如果需要在多个函数中使用gRT,可以将其定义为全局变量: ```c EFI_RUNTIME_SERVICES *gRT = NULL; EFI_STATUS UefiMain ( IN EFI_HANDLE ImageHandle, IN EFI_SYSTEM_TABLE *SystemTable ) { gRT = SystemTable->RuntimeServices; // 其他代码... } ``` #### 3. **注意事项** - 这种方法不需要链接`UefiRuntimeServicesTableLib`库,但需要确保在入口函数中正确初始化全局变量`gRT`。 - 在驱动程序或应用程序的入口函数被调用时,系统表是有效的,因此可以安全地使用。 ### 整合之前的解决方案 我们之前提供的解决方案(使用库)仍然是推荐的方式,因为它更简单且符合EDK II的编程规范。但是,了解通过系统表直接获取的方法有助于深入理解UEFI的运行机制。 ### 完整步骤回顾 #### 方法1:使用标准库(推荐) 1. 包含头文件: ```c #include <Library/UefiRuntimeServicesTableLib.h> ``` 2. 在INF文件中添加库依赖: ```inf [LibraryClasses] UefiRuntimeServicesTableLib ``` 3. 直接使用`gRT`全局变量。 #### 方法2:通过系统表获取 1. 在入口函数中通过系统表获取运行时服务表指针,并赋值给全局变量(如果需要全局使用)。 2. 使用该指针调用运行时服务。 ### 调试建议 - 如果使用全局变量方式,确保在入口函数中初始化后再使用。 - 检查系统表指针是否有效(非NULL)。 ### 相关问题 1. 如何正确使用UEFI系统表(System Table)? 2. UEFI运行时服务在操作系统运行后如何调用? 3. 在UEFI驱动中如何安全地使用全局变量? 4. UEFI应用程序的入口函数参数有哪些? ### 参考 [^1]: C++ Primer 第5章学习 —— “语句” [^2]: Thymeleaf的主要特点 [^3]: UEFI基础服务:系统表</think>### 解决“未定义标识符 gRT”的完整方案 在 UEFI 开发中,`gRT` 是 **UEFI 运行时服务表**的核心全局变量。该错误通常由以下原因引起,以下是系统化的解决方案: --- #### 1. **包含必需的头文件** ```c #include <Uefi.h> // UEFI基础头文件 #include <Library/UefiRuntimeServicesTableLib.h> // 包含gRT声明[^3] ``` #### 2. **链接运行时服务库** 在模块的 `.inf` 配置文件中添加依赖: ```inf [LibraryClasses] UefiRuntimeServicesTableLib # 提供gRT定义 UefiBootServicesTableLib # 提供gBS定义 ``` #### 3. **正确使用 gRT** ```c EFI_STATUS Status; CHAR16 VariableName[] = L"BootCount"; UINT32 BootCount = 0; // 示例:读取UEFI变量 Status = gRT->GetVariable( VariableName, // 变量名 &gEfiGlobalVariableGuid, // GUID NULL, // 属性(可选) sizeof(BootCount), // 缓冲区大小 &BootCount // 数据指针 ); if (EFI_ERROR(Status)) { Print(L"读取变量失败: %r\n", Status); } ``` --- ### 错误原因分析与解决方案 | 原因 | 现象 | 解决方案 | |------|------|----------| | **头文件缺失** | 编译器无法识别`gRT` | 添加 `#include <Library/UefiRuntimeServicesTableLib.h>` | | **库未链接** | 链接阶段报未定义符号 | 在INF文件的 `[LibraryClasses]` 添加 `UefiRuntimeServicesTableLib` | | **作用域错误** | 在非全局作用域声明同名变量 | 检查是否有局部变量覆盖全局`gRT` | | **系统表未初始化** | `gRT` 为 `NULL` | 确认入口函数接收了 `EFI_SYSTEM_TABLE*` 参数[^3] | --- ### gRT 核心功能详解 通过 `gRT` 可访问 UEFI 运行时服务: ```c // 获取系统时间 EFI_TIME CurrentTime; gRT->GetTime(&CurrentTime, NULL); // 重启系统 gRT->ResetSystem(EfiResetCold, EFI_SUCCESS, 0, NULL); // 读写NVRAM变量 gRT->SetVariable(L"LastBoot", ...); ``` > ⚠️ **关键区别**: > - `gBS` (Boot Services):仅UEFI启动阶段可用 > - `gRT` (Runtime Services):操作系统运行后仍可调用 --- ### 调试技巧 1. **验证 gRT 有效性**: ```c if (gRT == NULL) { Print(L"错误:gRT未初始化!\n"); return EFI_NOT_READY; } ``` 2. **检查协议依赖**: ```inf [Depex] gEfiUefiRuntimeServicesTableGuid # 运行时服务依赖 ``` 3. **构建验证**: ```bash build -p YourPlatform.dsc -m YourModule.inf ``` 确认输出中包含: ``` Processing library: UefiRuntimeServicesTableLib ``` --- ### 替代方案:手动获取 gRT 若无法使用标准库,可通过系统表获取: ```c EFI_RUNTIME_SERVICES *gRT; EFI_STATUS Status = gBS->LocateProtocol( &gEfiRuntimeServicesTableGuid, NULL, (VOID**)&gRT ); ``` > ⚠️ 此方法需确保 `gBS` 已初始化,增加复杂度[^3]。 --- ### 常见问题解答 **Q1: 为什么 ResetSystem() 调用无效?** 需检查固件支持标志: ```c if (gRT->Hdr.Revision & EFI_RUNTIME_SERVICES_SUPPORTED_RESET) { gRT->ResetSystem(EfiResetWarm, 0, 0, NULL); } ``` **Q2: 操作系统运行后如何调用 gRT?** 需满足: 1. 使用 `EFI_RUNTIME_DRIVER` 类型 2. 声明 `RUNTIME_DRIVER` 而非 `DRIVER` 3. 在 `Unload()` 中注册退出处理函数 --- ### 相关问题 1. UEFI 驱动和应用程序如何访问系统表(System Table)? 2. 在 UEFI 中读写 NVRAM 变量有哪些安全注意事项? 3. 如何实现 UEFI 运行时服务的多线程调用? 4. `gBS` 和 `gRT` 在操作系统启动后的行为差异是什么?
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值