This commit is contained in:
Michał Kopeć
2026-05-02 22:10:37 +02:00
parent 927ef7baf8
commit 8db59f5fed
14 changed files with 10584 additions and 0 deletions
+12
View File
@@ -27,6 +27,13 @@
DEFINE SOURCE_DEBUG_ENABLE = FALSE
DEFINE PS2_KEYBOARD_ENABLE = FALSE
#
# Use the Dasharo graphical FORM_DISPLAY_ENGINE provider in place of
# MdeModulePkg's text-mode DisplayEngineDxe. Set to FALSE to fall back
# to the stock text-mode setup browser.
#
DEFINE USE_GRAPHICAL_SETUP = TRUE
#
# SBL: UEFI payload for Slim Bootloader
# COREBOOT: UEFI payload for coreboot
@@ -799,6 +806,7 @@ OrderedCollectionLib|MdePkg/Library/BaseOrderedCollectionRedBlackTreeLib/BaseOrd
<LibraryClasses>
NULL|MdeModulePkg/Library/DeviceManagerUiLib/DeviceManagerUiLib.inf
NULL|DasharoModulePkg/Library/DasharoSystemFeaturesUiLib/DasharoSystemFeaturesUiLib.inf
NULL|DasharoPayloadPkg/Library/DasharoSaveExitUiLib/DasharoSaveExitUiLib.inf
NULL|MdeModulePkg/Library/BootManagerUiLib/BootManagerUiLib.inf
NULL|MdeModulePkg/Library/BootMaintenanceManagerUiLib/BootMaintenanceManagerUiLib.inf
}
@@ -863,7 +871,11 @@ OrderedCollectionLib|MdePkg/Library/BaseOrderedCollectionRedBlackTreeLib/BaseOrd
MdeModulePkg/Universal/MemoryTest/NullMemoryTestDxe/NullMemoryTestDxe.inf
MdeModulePkg/Universal/HiiDatabaseDxe/HiiDatabaseDxe.inf
MdeModulePkg/Universal/SetupBrowserDxe/SetupBrowserDxe.inf
!if $(USE_GRAPHICAL_SETUP) == TRUE
DasharoPayloadPkg/GraphicsDisplayEngineDxe/GraphicsDisplayEngineDxe.inf
!else
MdeModulePkg/Universal/DisplayEngineDxe/DisplayEngineDxe.inf
!endif
DasharoPayloadPkg/BlSupportDxe/BlSupportDxe.inf
CrScreenshotDxe/CrScreenshotDxe.inf {
<BuildOptions>
+4
View File
@@ -151,7 +151,11 @@ INF MdeModulePkg/Universal/DevicePathDxe/DevicePathDxe.inf
INF MdeModulePkg/Universal/MemoryTest/NullMemoryTestDxe/NullMemoryTestDxe.inf
INF MdeModulePkg/Universal/HiiDatabaseDxe/HiiDatabaseDxe.inf
INF MdeModulePkg/Universal/SetupBrowserDxe/SetupBrowserDxe.inf
!if $(USE_GRAPHICAL_SETUP) == TRUE
INF DasharoPayloadPkg/GraphicsDisplayEngineDxe/GraphicsDisplayEngineDxe.inf
!else
INF MdeModulePkg/Universal/DisplayEngineDxe/DisplayEngineDxe.inf
!endif
INF DasharoPayloadPkg/BlSupportDxe/BlSupportDxe.inf
INF MdeModulePkg/Universal/SmbiosDxe/SmbiosDxe.inf
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,181 @@
/** @file
Dasharo graphical display engine — shared internal definitions.
Copyright (c) 2026, 3mdeb Sp. z o.o. All rights reserved.<BR>
SPDX-License-Identifier: BSD-2-Clause-Patent
**/
#ifndef GRAPHICS_DISPLAY_ENGINE_H_
#define GRAPHICS_DISPLAY_ENGINE_H_
#include <Uefi.h>
#include <Library/BaseLib.h>
#include <Library/BaseMemoryLib.h>
#include <Library/DebugLib.h>
#include <Library/HiiLib.h>
#include <Library/MemoryAllocationLib.h>
#include <Library/PrintLib.h>
#include <Library/UefiBootServicesTableLib.h>
#include <Library/UefiRuntimeServicesTableLib.h>
#include <Protocol/DisplayProtocol.h>
#include <Protocol/FormBrowserEx2.h>
#include <Protocol/GraphicsOutput.h>
#include <Protocol/SimpleTextIn.h>
#include <Protocol/SimpleTextOut.h>
#include <Uefi/UefiInternalFormRepresentation.h>
//
// Layout constants. Sizes that aren't fixed pixels are percentages of the
// active GOP mode and are recomputed on every FormDisplay call so that
// resolution changes take effect immediately.
//
#define GFX_FONT_SIZE_PERCENT 2 // body font (% of vertical res)
#define GFX_HEADER_PERCENT 7 // brand band (logo + product line)
#define GFX_TABBAR_PERCENT 5 // numbered horizontal tab strip
#define GFX_PAGEHEADER_LINES 4 // breadcrumb + section + title + spacer
#define GFX_FOOTER_PERCENT 5 // hot-key band
// Base values, scaled at runtime by mState.Scale (resolution-dependent).
// All are referenced as `GFX_PADDING_PX` etc. at use sites; the macros
// below resolve to a runtime expression that incorporates mState.Scale,
// so simple uses like `2 * GFX_PADDING_PX` continue to compose correctly.
#define GFX_PADDING_PX_BASE 8u
#define GFX_HIGHLIGHT_INSET_PX_BASE 3u // gap above/below row highlight
#define GFX_SCROLLBAR_RESERVE_PX_BASE 8u // right strip kept clear of row
// fills so the scroll indicator
// stays visible across redraws
#define GFX_PADDING_PX (GFX_PADDING_PX_BASE * mState.Scale)
#define GFX_HIGHLIGHT_INSET_PX (GFX_HIGHLIGHT_INSET_PX_BASE * mState.Scale)
#define GFX_SCROLLBAR_RESERVE_PX (GFX_SCROLLBAR_RESERVE_PX_BASE * mState.Scale)
//
// Panel colors (used by Blt fills). Stored BGR + reserved to match
// EFI_GRAPHICS_OUTPUT_BLT_PIXEL — Blt is pixel-format agnostic.
//
#define GFX_COLOR(R, G, B) ((EFI_GRAPHICS_OUTPUT_BLT_PIXEL){ (B), (G), (R), 0 })
#define GFX_BG GFX_COLOR (0x05, 0x09, 0x14) // near-black, slight blue
#define GFX_PANEL GFX_COLOR (0x0C, 0x14, 0x22) // header/footer bands
#define GFX_PANEL_ALT GFX_COLOR (0x16, 0x1F, 0x30) // separator rules, popup title
#define GFX_HIGHLIGHT GFX_COLOR (0x10, 0x60, 0x55) // selected row bg (teal-tinted)
#define GFX_HIGHLIGHT_DIM GFX_COLOR (0x33, 0x3D, 0x52) // selected-but-not-actionable row
// — bright enough to be obviously
// a highlight even on rows with no text
#define GFX_ACCENT GFX_COLOR (0x14, 0xB8, 0xA6) // primary accent (active indicators)
#define GFX_TEXT GFX_COLOR (0xFF, 0xFF, 0xFF) // (legacy; text uses TC_*)
#define GFX_TEXT_DIM GFX_COLOR (0x55, 0x60, 0x70) // outlines, separators
//
// Text colors as RGB triplets — fed straight into GfxSetTextColor().
//
#define TC_WHITE 0xFF, 0xFF, 0xFF // primary
#define TC_DIM 0x80, 0x88, 0x95 // descriptions, breadcrumbs
#define TC_DIMMER 0x55, 0x60, 0x70 // hardly-there labels
#define TC_TEAL 0x14, 0xB8, 0xA6 // active accents
#define TC_AMBER 0xF5, 0x9E, 0x0B // warnings
#define TC_RED 0xEF, 0x44, 0x4F // errors
typedef struct {
EFI_GRAPHICS_OUTPUT_PROTOCOL *Gop; // NULL => no graphics at all
BOOLEAN SsfnReady; // direct framebuffer text usable
UINT32 ScreenW;
UINT32 ScreenH;
UINTN FontPx;
UINTN Scale; // 1, 2, 3 — driven by ScreenH
EFI_SIMPLE_TEXT_OUTPUT_PROTOCOL **SerialOutputs;
UINTN SerialCount;
// The form we treat as "Main" — recorded the first time TabsCaptureFromForm
// commits to a tab source, used to special-case rendering (system info
// cards instead of plain REF list) and tab navigation (L/R wrapping).
EFI_GUID MainFormSetGuid;
UINT16 MainFormId;
BOOLEAN MainFormIdentified;
} GFX_DISPLAY_ENGINE_STATE;
extern GFX_DISPLAY_ENGINE_STATE mState;
//
// Cached top-level navigation tabs. Captured from the REF statements on the
// first form that contains REFs (typically the front page). Tab destinations
// are referenced by FormSet GUID + Form ID, which stay valid across
// FormDisplay calls (statement pointers do not).
//
#define MAX_TABS 16
typedef struct {
EFI_GUID FormSetGuid;
UINT16 FormId;
EFI_HII_HANDLE HiiHandle; // kept for diagnostic / future use
EFI_STRING_ID Prompt; // (likewise — string is cached below)
CHAR16 Name[64]; // resolved at capture time (StringIds
// can be invalidated when UiApp
// rebuilds the front page)
} TAB_ENTRY;
//
// Output.c — protocol discovery, font init, drawing primitives, serial mirror.
//
EFI_STATUS GfxOutputInit (VOID);
VOID GfxFillRect (IN UINTN X, IN UINTN Y, IN UINTN W, IN UINTN H,
IN EFI_GRAPHICS_OUTPUT_BLT_PIXEL Color);
VOID GfxClearScreen (IN EFI_GRAPHICS_OUTPUT_BLT_PIXEL Color);
VOID GfxDrawText (IN UINTN X, IN UINTN Y, IN CONST CHAR8 *Utf8);
VOID GfxDrawTextW (IN UINTN X, IN UINTN Y, IN CONST CHAR16 *Wide);
// Sets the foreground color used by all subsequent text draws until next call.
VOID GfxSetTextColor (IN UINT8 R, IN UINT8 G, IN UINT8 B);
// Truncates with an ellipsis if the string would render wider than MaxWidthPx.
VOID GfxDrawTextWClipped (IN UINTN X, IN UINTN Y, IN UINTN MaxWidthPx,
IN CONST CHAR16 *Wide);
// Renders Wide so its right edge sits at RightX (text grows leftward).
VOID GfxDrawTextWRightAligned (IN UINTN RightX, IN UINTN Y,
IN CONST CHAR16 *Wide);
// Like GfxDrawTextWRightAligned but truncates with an ellipsis when the
// rendered width would exceed MaxWidthPx.
VOID GfxDrawTextWRightAlignedClipped (IN UINTN RightX, IN UINTN Y,
IN UINTN MaxWidthPx,
IN CONST CHAR16 *Wide);
// Renders Wide centered horizontally on CenterX.
VOID GfxDrawTextWCentered (IN UINTN CenterX, IN UINTN Y,
IN CONST CHAR16 *Wide);
// Measures the rendered pixel width of Wide using the active font.
UINTN GfxMeasureTextW (IN CONST CHAR16 *Wide);
// Draws an outlined rectangle (1 px border, no fill).
VOID GfxDrawRectOutline (IN UINTN X, IN UINTN Y, IN UINTN W, IN UINTN H,
IN EFI_GRAPHICS_OUTPUT_BLT_PIXEL Color);
VOID SerialOut (IN CONST CHAR16 *Text);
VOID SerialOutLine (IN CONST CHAR16 *Text);
//
// SystemInfo.c — SMBIOS-derived hardware summary + RTC clock formatting.
//
typedef struct {
CHAR16 SystemVendor[64];
CHAR16 SystemProduct[64];
CHAR16 BiosVersion[64];
CHAR16 CpuBrand[80];
UINT64 TotalRamMB;
BOOLEAN Collected;
} SYSTEM_INFO;
extern SYSTEM_INFO mSysInfo;
VOID SystemInfoCollect (VOID);
VOID SystemInfoFormatTime (OUT CHAR16 *Buf, IN UINTN BufSize);
VOID SystemInfoFormatSummary (OUT CHAR16 *Buf, IN UINTN BufSize);
//
// Tabs.c — top-level navigation tab cache.
//
VOID TabsCaptureFromForm (IN FORM_DISPLAY_ENGINE_FORM *Form);
UINTN TabsCount (VOID);
TAB_ENTRY *TabsGet (IN UINTN Index);
INTN TabsFindActive (IN FORM_DISPLAY_ENGINE_FORM *Form);
// Renders the horizontal tab bar across the strip [TopY .. TopY+BarH).
VOID TabsRenderHorizontal (IN UINTN TopY, IN UINTN BarH,
IN INTN ActiveIndex);
#endif // GRAPHICS_DISPLAY_ENGINE_H_
@@ -0,0 +1,59 @@
## @file
# Dasharo graphical FORM_DISPLAY_ENGINE_PROTOCOL provider.
#
# Renders the HII setup menu graphically on GOP while mirroring an in-sync
# text view to serial-only consoles. Falls back to plain text when no GOP
# is available.
#
# Copyright (c) 2026, 3mdeb Sp. z o.o. All rights reserved.<BR>
# SPDX-License-Identifier: BSD-2-Clause-Patent
##
[Defines]
INF_VERSION = 0x00010005
BASE_NAME = GraphicsDisplayEngineDxe
FILE_GUID = 7C5A0C11-2A5D-4F0A-9E8E-5CF6B1E1A401
MODULE_TYPE = DXE_DRIVER
VERSION_STRING = 1.0
ENTRY_POINT = GraphicsDisplayEngineEntry
[Sources]
GraphicsDisplayEngine.c
GraphicsDisplayEngine.h
Output.c
SystemInfo.c
Tabs.c
[Packages]
MdePkg/MdePkg.dec
MdeModulePkg/MdeModulePkg.dec
DasharoPayloadPkg/DasharoPayloadPkg.dec
[LibraryClasses]
UefiDriverEntryPoint
UefiBootServicesTableLib
UefiRuntimeServicesTableLib
DebugLib
BaseMemoryLib
BaseLib
PrintLib
HiiLib
MemoryAllocationLib
DevicePathLib
[Protocols]
gEdkiiFormDisplayEngineProtocolGuid ## PRODUCES
gEdkiiFormBrowserEx2ProtocolGuid ## CONSUMES
gEfiGraphicsOutputProtocolGuid ## SOMETIMES_CONSUMES
gEfiSimpleTextOutProtocolGuid ## CONSUMES
gEfiDevicePathProtocolGuid ## CONSUMES
gEfiSimpleTextInputExProtocolGuid ## CONSUMES
[Guids]
gEfiSmbios3TableGuid ## SOMETIMES_CONSUMES
gEfiSmbiosTableGuid ## SOMETIMES_CONSUMES
[Depex]
gEfiHiiDatabaseProtocolGuid AND
gEfiHiiConfigRoutingProtocolGuid AND
gEdkiiFormBrowserEx2ProtocolGuid
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,266 @@
/** @file
SMBIOS-derived system information rendered in the graphical setup header
(vendor / product, firmware version, CPU brand, total RAM) plus a helper
to format the current real-time clock.
The lookup walks gST->ConfigurationTable for the SMBIOS 3.0 entry point
(falling back to the 2.x entry point) and iterates structures linearly,
pulling strings by their 1-based index from the trailing string list.
Cached on first call — SMBIOS doesn't change at runtime.
Copyright (c) 2026, 3mdeb Sp. z o.o. All rights reserved.<BR>
SPDX-License-Identifier: BSD-2-Clause-Patent
**/
#include "GraphicsDisplayEngine.h"
#include <Guid/SmBios.h>
#include <IndustryStandard/SmBios.h>
SYSTEM_INFO mSysInfo;
STATIC
BOOLEAN
FindSmbiosTable (
OUT VOID **TableBase,
OUT UINT32 *TableLen
)
{
UINTN i;
// Prefer the 64-bit entry point; otherwise fall back to legacy.
for (i = 0; i < gST->NumberOfTableEntries; i++) {
if (CompareGuid (&gST->ConfigurationTable[i].VendorGuid, &gEfiSmbios3TableGuid)) {
SMBIOS_TABLE_3_0_ENTRY_POINT *Ep = gST->ConfigurationTable[i].VendorTable;
*TableBase = (VOID *)(UINTN)Ep->TableAddress;
*TableLen = Ep->TableMaximumSize;
return TRUE;
}
}
for (i = 0; i < gST->NumberOfTableEntries; i++) {
if (CompareGuid (&gST->ConfigurationTable[i].VendorGuid, &gEfiSmbiosTableGuid)) {
SMBIOS_TABLE_ENTRY_POINT *Ep = gST->ConfigurationTable[i].VendorTable;
*TableBase = (VOID *)(UINTN)Ep->TableAddress;
*TableLen = Ep->TableLength;
return TRUE;
}
}
return FALSE;
}
// Fetches the Index-th string (1-based) from the trailing string list of an
// SMBIOS structure. Returns NULL if the index is 0 or out of range.
STATIC
CONST CHAR8 *
SmbiosString (
IN SMBIOS_STRUCTURE *Struct,
IN UINT8 Index
)
{
CONST CHAR8 *p;
UINT8 n;
if ((Index == 0) || (Struct->Length < sizeof (SMBIOS_STRUCTURE))) {
return NULL;
}
p = (CONST CHAR8 *)Struct + Struct->Length;
for (n = 1; n < Index; n++) {
while (*p != 0) {
p++;
}
p++;
if (*p == 0) {
return NULL; // walked off the end of the string list
}
}
return *p == 0 ? NULL : p;
}
STATIC
SMBIOS_STRUCTURE *
NextStructure (
IN SMBIOS_STRUCTURE *Cur
)
{
CONST UINT8 *p = (CONST UINT8 *)Cur + Cur->Length;
// String list ends with a double-NUL.
while ((p[0] != 0) || (p[1] != 0)) {
p++;
}
return (SMBIOS_STRUCTURE *)(p + 2);
}
STATIC
VOID
AsciiToWide (
IN CONST CHAR8 *Src,
OUT CHAR16 *Dst,
IN UINTN DstChars
)
{
UINTN i;
if ((Src == NULL) || (DstChars == 0)) {
if (DstChars > 0) {
Dst[0] = L'\0';
}
return;
}
for (i = 0; (i < DstChars - 1) && (Src[i] != 0); i++) {
Dst[i] = (CHAR16)(UINT8)Src[i];
}
Dst[i] = L'\0';
}
VOID
SystemInfoCollect (
VOID
)
{
VOID *TableBase;
UINT32 TableLen;
SMBIOS_STRUCTURE *s;
UINT8 *End;
if (mSysInfo.Collected) {
return;
}
mSysInfo.Collected = TRUE;
StrCpyS (mSysInfo.SystemVendor, ARRAY_SIZE (mSysInfo.SystemVendor), L"?");
StrCpyS (mSysInfo.SystemProduct, ARRAY_SIZE (mSysInfo.SystemProduct), L"?");
StrCpyS (mSysInfo.BiosVersion, ARRAY_SIZE (mSysInfo.BiosVersion), L"?");
StrCpyS (mSysInfo.CpuBrand, ARRAY_SIZE (mSysInfo.CpuBrand), L"?");
mSysInfo.TotalRamMB = 0;
if (!FindSmbiosTable (&TableBase, &TableLen)) {
DEBUG ((DEBUG_ERROR, "SystemInfo: no SMBIOS table found in ConfigurationTable\n"));
return;
}
DEBUG ((
DEBUG_ERROR,
"SystemInfo: SMBIOS at 0x%p, len=0x%x\n",
TableBase, (UINTN)TableLen
));
s = (SMBIOS_STRUCTURE *)TableBase;
End = (UINT8 *)TableBase + TableLen;
while (((UINT8 *)s + sizeof (SMBIOS_STRUCTURE)) <= End) {
if (s->Length < sizeof (SMBIOS_STRUCTURE)) {
break; // malformed
}
switch (s->Type) {
case 0: { // BIOS Information
SMBIOS_TABLE_TYPE0 *t = (SMBIOS_TABLE_TYPE0 *)s;
AsciiToWide (
SmbiosString (s, t->BiosVersion),
mSysInfo.BiosVersion, ARRAY_SIZE (mSysInfo.BiosVersion)
);
break;
}
case 1: { // System Information
SMBIOS_TABLE_TYPE1 *t = (SMBIOS_TABLE_TYPE1 *)s;
AsciiToWide (
SmbiosString (s, t->Manufacturer),
mSysInfo.SystemVendor, ARRAY_SIZE (mSysInfo.SystemVendor)
);
AsciiToWide (
SmbiosString (s, t->ProductName),
mSysInfo.SystemProduct, ARRAY_SIZE (mSysInfo.SystemProduct)
);
break;
}
case 4: { // Processor Information
SMBIOS_TABLE_TYPE4 *t = (SMBIOS_TABLE_TYPE4 *)s;
AsciiToWide (
SmbiosString (s, t->ProcessorVersion),
mSysInfo.CpuBrand, ARRAY_SIZE (mSysInfo.CpuBrand)
);
break;
}
case 17: { // Memory Device — sum sizes across DIMMs.
SMBIOS_TABLE_TYPE17 *t = (SMBIOS_TABLE_TYPE17 *)s;
UINT64 SizeMB = 0;
if ((t->Size != 0xFFFF) && (t->Size != 0)) {
if (t->Size == 0x7FFF) {
SizeMB = t->ExtendedSize & 0x7FFFFFFF;
} else if ((t->Size & 0x8000) != 0) {
SizeMB = (t->Size & 0x7FFF) / 1024; // KB → MB (rounds down)
} else {
SizeMB = t->Size; // already MB
}
}
mSysInfo.TotalRamMB += SizeMB;
break;
}
default:
break;
}
if (s->Type == SMBIOS_TYPE_END_OF_TABLE) {
break;
}
s = NextStructure (s);
if ((UINT8 *)s >= End) {
break;
}
}
DEBUG ((
DEBUG_ERROR,
"SystemInfo: vendor=\"%s\" product=\"%s\" bios=\"%s\" cpu=\"%s\" ram=%lu MB\n",
mSysInfo.SystemVendor,
mSysInfo.SystemProduct,
mSysInfo.BiosVersion,
mSysInfo.CpuBrand,
mSysInfo.TotalRamMB
));
}
VOID
SystemInfoFormatTime (
OUT CHAR16 *Buf,
IN UINTN BufSize
)
{
EFI_TIME T;
if (EFI_ERROR (gRT->GetTime (&T, NULL))) {
StrCpyS (Buf, BufSize / sizeof (CHAR16), L"--:--:--");
return;
}
UnicodeSPrint (
Buf, BufSize,
L"%04u-%02u-%02u %02u:%02u:%02u",
(UINT32)T.Year, (UINT32)T.Month, (UINT32)T.Day,
(UINT32)T.Hour, (UINT32)T.Minute, (UINT32)T.Second
);
}
VOID
SystemInfoFormatSummary (
OUT CHAR16 *Buf,
IN UINTN BufSize
)
{
if (mSysInfo.TotalRamMB > 0) {
UnicodeSPrint (
Buf, BufSize,
L"PLATFORM %s \x00B7 FW %s \x00B7 CPU %s \x00B7 RAM %u GiB",
mSysInfo.SystemProduct,
mSysInfo.BiosVersion,
mSysInfo.CpuBrand,
(UINT32)((mSysInfo.TotalRamMB + 1023) / 1024)
);
} else {
UnicodeSPrint (
Buf, BufSize,
L"PLATFORM %s \x00B7 FW %s \x00B7 CPU %s",
mSysInfo.SystemProduct,
mSysInfo.BiosVersion,
mSysInfo.CpuBrand
);
}
}
@@ -0,0 +1,299 @@
/** @file
Top-level navigation tabs for the graphical setup menu.
The display engine receives forms one at a time via FormBrowser2; it has
no inherent notion of "siblings" or a tab strip. To present persistent
tabs we snapshot the REF statements from the first form that contains
them (typically the front page) and render that snapshot on every form.
Statement pointers are owned by the form browser and are only valid for
the duration of one FormDisplay call, so we cache by FormSet GUID +
Form ID + HII handle + prompt StringId — all of which remain stable.
Copyright (c) 2026, 3mdeb Sp. z o.o. All rights reserved.<BR>
SPDX-License-Identifier: BSD-2-Clause-Patent
**/
#include "GraphicsDisplayEngine.h"
STATIC TAB_ENTRY mTabs[MAX_TABS];
STATIC UINTN mTabCount;
// Tabs that should always be pinned to the right end of the strip, in the
// order listed here. Matched against the destination FormSetGuid of each
// captured REF. Add new GUIDs (e.g. for additional "wrap-up" tabs) at the
// end — entries are moved to the rightmost slots in this order.
STATIC EFI_GUID mTrailingTabs[] = {
// DasharoSaveExitUiLib: { 0x5b7f9a21, 0x3c44, 0x4e89, ... }
{ 0x5b7f9a21, 0x3c44, 0x4e89, { 0xb1, 0xa6, 0xd8, 0xc7, 0xf2, 0xe5, 0xa9, 0x01 } },
};
STATIC
VOID
PinTrailingTabs (
VOID
)
{
UINTN Pin;
UINTN i;
UINTN TargetSlot;
if (mTabCount < 2) {
return;
}
// For each "trailing" GUID in registration order, find the tab in the
// current array (skipping Main at index 0) and rotate it into the
// appropriate slot near the end. We process front-to-back through
// mTrailingTabs so multiple trailing tabs preserve their relative order.
for (Pin = 0; Pin < ARRAY_SIZE (mTrailingTabs); Pin++) {
TargetSlot = mTabCount - 1 - (ARRAY_SIZE (mTrailingTabs) - 1 - Pin);
if (TargetSlot >= mTabCount) {
continue;
}
for (i = 1; i < mTabCount; i++) {
if (!CompareGuid (&mTabs[i].FormSetGuid, &mTrailingTabs[Pin])) {
continue;
}
if (i == TargetSlot) {
break;
}
// Rotate mTabs[i] to TargetSlot. If TargetSlot > i, shift the
// intermediate range left; otherwise shift right. (For our cases
// TargetSlot is always >= i so we only ever shift left.)
{
TAB_ENTRY Tmp = mTabs[i];
if (TargetSlot > i) {
UINTN k;
for (k = i; k < TargetSlot; k++) {
mTabs[k] = mTabs[k + 1];
}
} else {
UINTN k;
for (k = i; k > TargetSlot; k--) {
mTabs[k] = mTabs[k - 1];
}
}
mTabs[TargetSlot] = Tmp;
}
break;
}
}
}
STATIC
BOOLEAN
IsRefOpcode (
IN UINT8 OpCode
)
{
return (OpCode == EFI_IFR_REF_OP);
}
STATIC
VOID
ExtractRef (
IN FORM_DISPLAY_ENGINE_STATEMENT *Stmt,
IN EFI_HII_HANDLE HiiHandle,
IN EFI_GUID *CurrentFormSetGuid,
OUT TAB_ENTRY *Out
)
{
EFI_IFR_REF *Ref = (EFI_IFR_REF *)Stmt->OpCode;
CHAR16 *Resolved;
Out->FormId = Ref->FormId;
Out->HiiHandle = HiiHandle;
Out->Prompt = Ref->Question.Header.Prompt;
// Resolve the prompt string NOW and cache the literal — UiApp rebuilds
// the Front Page on every entry, which can retire the StringIds we
// captured. Holding the string itself keeps tabs stable across rebuilds.
Resolved = HiiGetString (HiiHandle, Out->Prompt, NULL);
if (Resolved != NULL) {
StrCpyS (Out->Name, ARRAY_SIZE (Out->Name), Resolved);
FreePool (Resolved);
} else {
Out->Name[0] = L'\0';
}
// REF3 and REF4 carry an explicit destination FormSetId — that's how
// cross-formset entries on the Front Page are encoded (HiiCreateGotoExOpCode
// emits REF3 with FormId=0 and a FormSetId pointing at the target formset).
if (Ref->Header.Length >= sizeof (EFI_IFR_REF3)) {
EFI_IFR_REF3 *Ref3 = (EFI_IFR_REF3 *)Stmt->OpCode;
CopyMem (&Out->FormSetGuid, &Ref3->FormSetId, sizeof (EFI_GUID));
} else {
CopyMem (&Out->FormSetGuid, CurrentFormSetGuid, sizeof (EFI_GUID));
}
}
VOID
TabsCaptureFromForm (
IN FORM_DISPLAY_ENGINE_FORM *Form
)
{
LIST_ENTRY *Link;
FORM_DISPLAY_ENGINE_STATEMENT *Stmt;
UINTN RefsTotal;
// One-shot capture: the first form we see with enough REFs becomes the
// tab source for the rest of the session. We deliberately do NOT replace
// the cache when the user navigates into a sub-menu (which itself may
// have several REFs); doing so would make the tab strip mutate as the
// user drills down, which is the opposite of what tabs are for.
if (mTabCount > 0) {
return;
}
RefsTotal = 0;
for (Link = GetFirstNode (&Form->StatementListHead);
!IsNull (&Form->StatementListHead, Link);
Link = GetNextNode (&Form->StatementListHead, Link))
{
Stmt = FORM_DISPLAY_ENGINE_STATEMENT_FROM_LINK (Link);
if ((Stmt->OpCode != NULL) && IsRefOpcode (Stmt->OpCode->OpCode)) {
RefsTotal++;
}
}
DEBUG ((
DEBUG_ERROR,
"Tabs: form FormId=0x%x has %u REFs\n",
Form->FormId, (UINT32)RefsTotal
));
if (RefsTotal < 2) {
return; // not a tab-source form
}
// Record this form as the canonical "Main" form so the rendering path
// can recognize when we're on it and switch into the system-info card
// layout (vs the plain REF list).
CopyMem (&mState.MainFormSetGuid, &Form->FormSetGuid, sizeof (EFI_GUID));
mState.MainFormId = Form->FormId;
mState.MainFormIdentified = TRUE;
// Tab 01 is a synthetic "Main" entry pointing at the form we're capturing
// from. It carries the actual FormSetGuid + FormId so navigation back to
// it via L/R works through the same FindRefForTab + multi-step exit
// path; the literal "Main" label sidesteps the form's own title.
CopyMem (&mTabs[0].FormSetGuid, &mState.MainFormSetGuid, sizeof (EFI_GUID));
mTabs[0].FormId = mState.MainFormId;
mTabs[0].HiiHandle = Form->HiiHandle;
mTabs[0].Prompt = 0;
StrCpyS (mTabs[0].Name, ARRAY_SIZE (mTabs[0].Name), L"Main");
mTabCount = 1;
for (Link = GetFirstNode (&Form->StatementListHead);
!IsNull (&Form->StatementListHead, Link) && (mTabCount < MAX_TABS);
Link = GetNextNode (&Form->StatementListHead, Link))
{
Stmt = FORM_DISPLAY_ENGINE_STATEMENT_FROM_LINK (Link);
if ((Stmt->OpCode == NULL) || !IsRefOpcode (Stmt->OpCode->OpCode)) {
continue;
}
ExtractRef (Stmt, Form->HiiHandle, &Form->FormSetGuid, &mTabs[mTabCount]);
mTabCount++;
}
PinTrailingTabs ();
}
UINTN
TabsCount (
VOID
)
{
return mTabCount;
}
TAB_ENTRY *
TabsGet (
IN UINTN Index
)
{
if (Index >= mTabCount) {
return NULL;
}
return &mTabs[Index];
}
INTN
TabsFindActive (
IN FORM_DISPLAY_ENGINE_FORM *Form
)
{
UINTN i;
// Match on FormSetGuid first; that anchors us to the right top-level
// section even when the user has drilled into a sub-form. The cached
// FormId is only required to match if it's non-zero — cross-formset
// tab entries store FormId=0 (meaning "the formset's main form") and
// we want them to highlight while the user is anywhere inside that
// formset, not just on its main form.
for (i = 0; i < mTabCount; i++) {
if (!CompareGuid (&mTabs[i].FormSetGuid, &Form->FormSetGuid)) {
continue;
}
if ((mTabs[i].FormId == 0) || (mTabs[i].FormId == Form->FormId)) {
return (INTN)i;
}
}
return -1;
}
VOID
TabsRenderHorizontal (
IN UINTN TopY,
IN UINTN BarH,
IN INTN ActiveIndex
)
{
UINTN TabW;
UINTN x;
UINTN yText;
UINTN i;
CHAR16 NumPrefix[8];
UINTN PrefixW;
if (mTabCount == 0) {
return;
}
TabW = mState.ScreenW / mTabCount;
yText = TopY + (BarH - mState.FontPx) / 2;
for (i = 0; i < mTabCount; i++) {
x = i * TabW;
// Number prefix is always dim — it's purely for orientation, the
// emphasis belongs on the name.
UnicodeSPrint (NumPrefix, sizeof (NumPrefix), L"%02u ", (UINT32)(i + 1));
GfxSetTextColor (TC_DIMMER);
GfxDrawTextW (x + 2 * GFX_PADDING_PX, yText, NumPrefix);
PrefixW = GfxMeasureTextW (NumPrefix);
// Active: bright white. Inactive: dim — visible but recedes.
if ((INTN)i == ActiveIndex) {
GfxSetTextColor (TC_WHITE);
} else {
GfxSetTextColor (TC_DIM);
}
GfxDrawTextWClipped (
x + 2 * GFX_PADDING_PX + PrefixW,
yText,
TabW - 3 * GFX_PADDING_PX - PrefixW,
mTabs[i].Name[0] != L'\0' ? mTabs[i].Name : L"?"
);
if ((INTN)i == ActiveIndex) {
GfxFillRect (
x + GFX_PADDING_PX,
TopY + BarH - 3,
TabW - 2 * GFX_PADDING_PX, 3,
GFX_ACCENT
);
}
}
GfxSetTextColor (TC_WHITE);
}
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,246 @@
/** @file
Dasharo Save & Exit UI library.
Publishes a small HII formset on the Front Page (via classguid =
gEfiIfrFrontPageGuid) containing action items for Save / Discard /
Reset. Each action's question ID is dispatched to a browser action
request inside the config-access callback.
Copyright (c) 2026, 3mdeb Sp. z o.o. All rights reserved.<BR>
SPDX-License-Identifier: BSD-2-Clause-Patent
**/
#include "DasharoSaveExit.h"
#include <Uefi.h>
#include <Library/BaseLib.h>
#include <Library/DebugLib.h>
#include <Library/DevicePathLib.h>
#include <Library/HiiLib.h>
#include <Library/MemoryAllocationLib.h>
#include <Library/UefiBootServicesTableLib.h>
#include <Library/UefiRuntimeServicesTableLib.h>
#include <Protocol/DevicePath.h>
#include <Protocol/FormBrowserEx.h>
#include <Protocol/FormBrowserEx2.h>
#include <Protocol/HiiConfigAccess.h>
#include <Guid/MdeModuleHii.h>
extern UINT8 DasharoSaveExitUiLibStrings[];
#define DASHARO_SAVE_EXIT_PRIVATE_DATA_SIGNATURE SIGNATURE_32 ('D', 'S', 'E', 'X')
typedef struct {
UINTN Signature;
EFI_HII_HANDLE HiiHandle;
EFI_HANDLE DriverHandle;
EFI_HII_CONFIG_ACCESS_PROTOCOL ConfigAccess;
} DASHARO_SAVE_EXIT_PRIVATE_DATA;
STATIC EFI_GUID mDasharoSaveExitGuid = DASHARO_SAVE_EXIT_GUID;
STATIC
EFI_STATUS
EFIAPI
ExtractConfig (
IN CONST EFI_HII_CONFIG_ACCESS_PROTOCOL *This,
IN CONST EFI_STRING Request,
OUT EFI_STRING *Progress,
OUT EFI_STRING *Results
)
{
if (Progress != NULL) {
*Progress = (EFI_STRING)Request;
}
// No varstore — actions only.
return EFI_NOT_FOUND;
}
STATIC
EFI_STATUS
EFIAPI
RouteConfig (
IN CONST EFI_HII_CONFIG_ACCESS_PROTOCOL *This,
IN CONST EFI_STRING Configuration,
OUT EFI_STRING *Progress
)
{
if (Progress != NULL) {
*Progress = (EFI_STRING)Configuration;
}
return EFI_NOT_FOUND;
}
STATIC
EFI_STATUS
EFIAPI
Callback (
IN CONST EFI_HII_CONFIG_ACCESS_PROTOCOL *This,
IN EFI_BROWSER_ACTION Action,
IN EFI_QUESTION_ID QuestionId,
IN UINT8 Type,
IN OUT EFI_IFR_TYPE_VALUE *Value,
OUT EFI_BROWSER_ACTION_REQUEST *ActionRequest
)
{
if (Action != EFI_BROWSER_ACTION_CHANGED) {
return EFI_UNSUPPORTED;
}
switch (QuestionId) {
case KEY_SAVE_AND_EXIT: {
// We need a real save-then-reset, system-wide. ExecuteAction with
// SystemLevel scope walks every dirty formset's pending values and
// commits them via ConfigRouting; ResetSystem then reboots.
EDKII_FORM_BROWSER_EXTENSION2_PROTOCOL *FormBrowserEx2;
EFI_STATUS LookupStatus;
LookupStatus = gBS->LocateProtocol (
&gEdkiiFormBrowserEx2ProtocolGuid,
NULL,
(VOID **)&FormBrowserEx2
);
if (!EFI_ERROR (LookupStatus) && (FormBrowserEx2 != NULL)) {
if (FormBrowserEx2->SetScope != NULL) {
FormBrowserEx2->SetScope (SystemLevel);
}
if (FormBrowserEx2->ExecuteAction != NULL) {
FormBrowserEx2->ExecuteAction (BROWSER_ACTION_SUBMIT, 0);
}
}
gRT->ResetSystem (EfiResetCold, EFI_SUCCESS, 0, NULL);
// Unreachable.
return EFI_SUCCESS;
}
case KEY_DISCARD_AND_EXIT: {
// FORM_DISCARD_EXIT only exits the current form (back to Front
// Page). To exit setup entirely we drive the discard + exit
// through ExecuteAction at SystemLevel scope, which sets the
// browser's gExitRequired flag.
EDKII_FORM_BROWSER_EXTENSION2_PROTOCOL *FormBrowserEx2;
EFI_STATUS LookupStatus;
LookupStatus = gBS->LocateProtocol (
&gEdkiiFormBrowserEx2ProtocolGuid,
NULL,
(VOID **)&FormBrowserEx2
);
if (!EFI_ERROR (LookupStatus) && (FormBrowserEx2 != NULL)) {
if (FormBrowserEx2->SetScope != NULL) {
FormBrowserEx2->SetScope (SystemLevel);
}
if (FormBrowserEx2->ExecuteAction != NULL) {
FormBrowserEx2->ExecuteAction (
BROWSER_ACTION_DISCARD | BROWSER_ACTION_EXIT,
0
);
}
}
*ActionRequest = EFI_BROWSER_ACTION_REQUEST_EXIT;
return EFI_SUCCESS;
}
case KEY_SAVE:
*ActionRequest = EFI_BROWSER_ACTION_REQUEST_FORM_APPLY;
return EFI_SUCCESS;
case KEY_DISCARD:
*ActionRequest = EFI_BROWSER_ACTION_REQUEST_FORM_DISCARD;
return EFI_SUCCESS;
case KEY_RESET_SYSTEM:
// Hard reset — discards any unsaved changes by definition.
gRT->ResetSystem (EfiResetCold, EFI_SUCCESS, 0, NULL);
// Unreachable.
return EFI_SUCCESS;
}
return EFI_UNSUPPORTED;
}
STATIC DASHARO_SAVE_EXIT_PRIVATE_DATA mPrivate = {
DASHARO_SAVE_EXIT_PRIVATE_DATA_SIGNATURE,
NULL,
NULL,
{
ExtractConfig,
RouteConfig,
Callback
}
};
STATIC HII_VENDOR_DEVICE_PATH mHiiVendorDevicePath = {
{
{
HARDWARE_DEVICE_PATH,
HW_VENDOR_DP,
{
(UINT8)(sizeof (VENDOR_DEVICE_PATH)),
(UINT8)((sizeof (VENDOR_DEVICE_PATH)) >> 8)
}
},
DASHARO_SAVE_EXIT_GUID
},
{
END_DEVICE_PATH_TYPE,
END_ENTIRE_DEVICE_PATH_SUBTYPE,
{
(UINT8)(END_DEVICE_PATH_LENGTH),
(UINT8)((END_DEVICE_PATH_LENGTH) >> 8)
}
}
};
EFI_STATUS
EFIAPI
DasharoSaveExitUiLibConstructor (
IN EFI_HANDLE ImageHandle,
IN EFI_SYSTEM_TABLE *SystemTable
)
{
EFI_STATUS Status;
Status = gBS->InstallMultipleProtocolInterfaces (
&mPrivate.DriverHandle,
&gEfiDevicePathProtocolGuid, &mHiiVendorDevicePath,
&gEfiHiiConfigAccessProtocolGuid, &mPrivate.ConfigAccess,
NULL
);
ASSERT_EFI_ERROR (Status);
mPrivate.HiiHandle = HiiAddPackages (
&mDasharoSaveExitGuid,
mPrivate.DriverHandle,
DasharoSaveExitVfrBin,
DasharoSaveExitUiLibStrings,
NULL
);
ASSERT (mPrivate.HiiHandle != NULL);
return EFI_SUCCESS;
}
EFI_STATUS
EFIAPI
DasharoSaveExitUiLibDestructor (
IN EFI_HANDLE ImageHandle,
IN EFI_SYSTEM_TABLE *SystemTable
)
{
if (mPrivate.HiiHandle != NULL) {
HiiRemovePackages (mPrivate.HiiHandle);
}
if (mPrivate.DriverHandle != NULL) {
gBS->UninstallMultipleProtocolInterfaces (
mPrivate.DriverHandle,
&gEfiDevicePathProtocolGuid, &mHiiVendorDevicePath,
&gEfiHiiConfigAccessProtocolGuid, &mPrivate.ConfigAccess,
NULL
);
}
return EFI_SUCCESS;
}
@@ -0,0 +1,37 @@
/** @file
Dasharo Save & Exit UI library — header shared between the VFR and the C
glue. Defines the formset GUID, form / question IDs, and the local
HII_VENDOR_DEVICE_PATH used to publish the formset.
Copyright (c) 2026, 3mdeb Sp. z o.o. All rights reserved.<BR>
SPDX-License-Identifier: BSD-2-Clause-Patent
**/
#ifndef _DASHARO_SAVE_EXIT_H_
#define _DASHARO_SAVE_EXIT_H_
#define DASHARO_SAVE_EXIT_GUID \
{ 0x5b7f9a21, 0x3c44, 0x4e89, { 0xb1, 0xa6, 0xd8, 0xc7, 0xf2, 0xe5, 0xa9, 0x01 } }
#define DASHARO_SAVE_EXIT_FORM_ID 0x1
// Question IDs for the action items. Browser invokes our config-access
// callback with these on user activation.
#define KEY_SAVE_AND_EXIT 0x1001
#define KEY_DISCARD_AND_EXIT 0x1002
#define KEY_SAVE 0x1003
#define KEY_DISCARD 0x1004
#define KEY_RESET_SYSTEM 0x1005
#ifndef VFRCOMPILE
#include <Protocol/DevicePath.h>
extern UINT8 DasharoSaveExitVfrBin[];
typedef struct {
VENDOR_DEVICE_PATH VendorDevicePath;
EFI_DEVICE_PATH_PROTOCOL End;
} HII_VENDOR_DEVICE_PATH;
#endif
#endif
@@ -0,0 +1,28 @@
/** @file
Dasharo Save & Exit UI library — string table.
Copyright (c) 2026, 3mdeb Sp. z o.o. All rights reserved.<BR>
SPDX-License-Identifier: BSD-2-Clause-Patent
**/
#langdef en-US "English"
#string STR_SAVE_EXIT_TITLE #language en-US "Save & Exit"
#string STR_SAVE_EXIT_HELP #language en-US "Save / discard / reset options"
#string STR_SAVE_AND_EXIT #language en-US "Save changes and exit (reboot)"
#string STR_SAVE_AND_EXIT_HELP #language en-US "Save all changes and reboot to apply them."
#string STR_DISCARD_AND_EXIT #language en-US "Discard changes and exit"
#string STR_DISCARD_AND_EXIT_HELP #language en-US "Discard all unsaved changes and exit setup."
#string STR_SAVE #language en-US "Save changes"
#string STR_SAVE_HELP #language en-US "Save all changes; remain in setup. Most settings only take effect after a reboot."
#string STR_DISCARD #language en-US "Discard changes"
#string STR_DISCARD_HELP #language en-US "Discard all unsaved changes; remain in setup."
#string STR_RESET_SYSTEM #language en-US "Reset system"
#string STR_RESET_SYSTEM_HELP #language en-US "Reboot the system. Unsaved changes are lost."
#string STR_EMPTY #language en-US ""
@@ -0,0 +1,53 @@
## @file
# Dasharo Save & Exit UI library — adds the Save & Exit tab to the front
# page (via classguid = gEfiIfrFrontPageGuid) with action items for
# save / discard / reset.
#
# Copyright (c) 2026, 3mdeb Sp. z o.o. All rights reserved.<BR>
# SPDX-License-Identifier: BSD-2-Clause-Patent
##
[Defines]
INF_VERSION = 0x00010005
BASE_NAME = DasharoSaveExitUiLib
FILE_GUID = 8FA1B244-9CDF-4A2E-AA34-5B7E89432101
MODULE_TYPE = DXE_DRIVER
VERSION_STRING = 1.0
LIBRARY_CLASS = NULL|DXE_DRIVER UEFI_APPLICATION
CONSTRUCTOR = DasharoSaveExitUiLibConstructor
DESTRUCTOR = DasharoSaveExitUiLibDestructor
[Sources]
DasharoSaveExit.h
DasharoSaveExit.c
DasharoSaveExitVfr.vfr
DasharoSaveExitStrings.uni
[Packages]
MdePkg/MdePkg.dec
MdeModulePkg/MdeModulePkg.dec
DasharoPayloadPkg/DasharoPayloadPkg.dec
[LibraryClasses]
BaseLib
BaseMemoryLib
DebugLib
DevicePathLib
HiiLib
MemoryAllocationLib
PrintLib
UefiBootServicesTableLib
UefiRuntimeServicesTableLib
UefiHiiServicesLib
[Guids]
gEfiIfrFrontPageGuid ## CONSUMES ## GUID (classguid for placement on the front page)
gEfiIfrTianoGuid ## CONSUMES ## GUID (extended IFR opcodes)
[Protocols]
gEfiDevicePathProtocolGuid ## PRODUCES
gEfiHiiConfigAccessProtocolGuid ## PRODUCES
gEdkiiFormBrowserEx2ProtocolGuid ## CONSUMES
[Depex]
gEfiHiiDatabaseProtocolGuid
@@ -0,0 +1,70 @@
/** @file
Dasharo Save & Exit formset.
Provides the action items that show up on the Save & Exit tab. Each item
is a `text` opcode with INTERACTIVE flag + unique question ID; the
config-access callback in DasharoSaveExit.c maps each ID to a browser
action request (or, for Reset System, calls gRT->ResetSystem directly).
Copyright (c) 2026, 3mdeb Sp. z o.o. All rights reserved.<BR>
SPDX-License-Identifier: BSD-2-Clause-Patent
**/
#include "DasharoSaveExit.h"
formset
guid = DASHARO_SAVE_EXIT_GUID,
title = STRING_TOKEN(STR_SAVE_EXIT_TITLE),
help = STRING_TOKEN(STR_SAVE_EXIT_HELP),
classguid = gEfiIfrFrontPageGuid,
form formid = DASHARO_SAVE_EXIT_FORM_ID,
title = STRING_TOKEN(STR_SAVE_EXIT_TITLE);
action
questionid = KEY_SAVE_AND_EXIT,
prompt = STRING_TOKEN(STR_SAVE_AND_EXIT),
help = STRING_TOKEN(STR_SAVE_AND_EXIT_HELP),
flags = INTERACTIVE,
config = STRING_TOKEN(STR_EMPTY),
endaction;
action
questionid = KEY_DISCARD_AND_EXIT,
prompt = STRING_TOKEN(STR_DISCARD_AND_EXIT),
help = STRING_TOKEN(STR_DISCARD_AND_EXIT_HELP),
flags = INTERACTIVE,
config = STRING_TOKEN(STR_EMPTY),
endaction;
subtitle text = STRING_TOKEN(STR_EMPTY);
action
questionid = KEY_SAVE,
prompt = STRING_TOKEN(STR_SAVE),
help = STRING_TOKEN(STR_SAVE_HELP),
flags = INTERACTIVE,
config = STRING_TOKEN(STR_EMPTY),
endaction;
action
questionid = KEY_DISCARD,
prompt = STRING_TOKEN(STR_DISCARD),
help = STRING_TOKEN(STR_DISCARD_HELP),
flags = INTERACTIVE,
config = STRING_TOKEN(STR_EMPTY),
endaction;
subtitle text = STRING_TOKEN(STR_EMPTY);
action
questionid = KEY_RESET_SYSTEM,
prompt = STRING_TOKEN(STR_RESET_SYSTEM),
help = STRING_TOKEN(STR_RESET_SYSTEM_HELP),
flags = INTERACTIVE,
config = STRING_TOKEN(STR_EMPTY),
endaction;
endform;
endformset;