SchOS/boot/uefi/efi.h
Nexoniarz dee3affa3b Boot manager: Niveus window, keys and wells, Instrument Sans, pointer, other systems, working power entries
The F8 menu is redrawn in the Niveus language: a window with three lights,
bevelled keys with a lit edge, wells for panels, anti-aliased corners and a
soft shadow, set in Instrument Sans rasterised into include/gfx/nvfont.h
(tools/python/mknvfont.py, make nvfont). Scale doubles on screens 1400 px tall
or more. Lists taller than the screen scroll.

The keyboard still drives everything, and a mouse, touchpad or tablet does
too: both UEFI pointer protocols are polled, hover moves the highlight, a
click activates. The cursor stays hidden until the pointer has really done
something, since the firmware installs a pointer protocol with or without a
mouse. The 'keep this resolution?' countdown is one ten-second budget however
many times the pointer moves.

Loaders found at well-known paths on any volume (Windows, Ubuntu, Debian,
Fedora, Arch, openSUSE, systemd-boot, rEFInd, other BOOTX64.EFI) are listed
and chain-loaded with LoadImage/StartImage. 'Reboot to firmware' used to halt;
it now sets OsIndications and restarts, and Restart and Shut Down call
ResetSystem.

Checked under QEMU+OVMF: menu, resolution list (incl. 2560x1600 at double
scale), system information, shut down, firmware setup, chain-loading a copy of
the loader from a second disk, and a full boot of the ISO to the login prompt.
Pointer hover and click were checked only with synthetic events: in this setup
a USB mouse or tablet delivered no event to the loader (one USB I/O handle
appeared but no pointer protocol came from it), and why was not established.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01EVRhxi9zpdW2uDUN4FVcao
2026-10-26 15:31:39 +01:00

431 lines
18 KiB
C

// SPDX-License-Identifier: GPL-2.0-or-later
#pragma once
#ifndef SCHOS_EFI_H
#define SCHOS_EFI_H
// The parts of UEFI this loader actually uses, written from the
// specification.
//
// Not gnu-efi and not EDK II. Both are large dependencies for something this
// small, and both raise a licensing question the project does not need to
// answer -- see docs/licensing.md. What is here is the handful of structures
// and function pointers needed to read a file, ask for the framebuffer, take
// the memory map and leave: perhaps two hundred lines against tens of
// thousands.
//
// Everything is called through function pointers in tables the firmware hands
// us, using the Microsoft x64 calling convention. clang's
// --target=x86_64-unknown-windows produces that natively, which is why no
// __attribute__((ms_abi)) appears anywhere below.
//
// Fields this loader never touches are left as `void *` padding rather than
// spelled out. The offsets are what matter and they are preserved exactly;
// naming a field we do not use would only invite it to drift.
typedef unsigned char UINT8;
typedef unsigned short UINT16;
typedef unsigned int UINT32;
typedef unsigned long long UINT64;
typedef long long INTN;
typedef unsigned long long UINTN;
typedef UINT16 CHAR16;
typedef UINT8 BOOLEAN;
typedef void *EFI_HANDLE;
typedef UINTN EFI_STATUS;
typedef UINT64 EFI_PHYSICAL_ADDRESS;
typedef UINT64 EFI_VIRTUAL_ADDRESS;
#define EFI_SUCCESS 0
#define EFI_BUFFER_TOO_SMALL ((EFI_STATUS)(0x8000000000000000ULL | 5))
#define EFI_ERROR(s) (((INTN)(s)) < 0)
typedef struct {
UINT32 Data1;
UINT16 Data2;
UINT16 Data3;
UINT8 Data4[8];
} EFI_GUID;
// --- memory ---------------------------------------------------------------
typedef enum {
AllocateAnyPages,
AllocateMaxAddress,
AllocateAddress
} EFI_ALLOCATE_TYPE;
// Only the types this loader has to reason about are named. The rule it
// applies is simply which of them are free for the kernel to use.
#define EfiReservedMemoryType 0
#define EfiLoaderCode 1
#define EfiLoaderData 2
#define EfiBootServicesCode 3
#define EfiBootServicesData 4
#define EfiRuntimeServicesCode 5
#define EfiRuntimeServicesData 6
#define EfiConventionalMemory 7
#define EfiUnusableMemory 8
#define EfiACPIReclaimMemory 9
#define EfiACPIMemoryNVS 10
#define EfiMemoryMappedIO 11
#define EfiMemoryMappedIOPortSpace 12
#define EfiPalCode 13
#define EfiPersistentMemory 14
typedef struct {
UINT32 Type;
UINT32 Pad;
EFI_PHYSICAL_ADDRESS PhysicalStart;
EFI_VIRTUAL_ADDRESS VirtualStart;
UINT64 NumberOfPages;
UINT64 Attribute;
} EFI_MEMORY_DESCRIPTOR;
// --- simple text output ---------------------------------------------------
typedef struct EFI_SIMPLE_TEXT_OUTPUT_PROTOCOL {
void *Reset;
EFI_STATUS (*OutputString)(struct EFI_SIMPLE_TEXT_OUTPUT_PROTOCOL *This,
CHAR16 *String);
void *TestString;
void *QueryMode;
void *SetMode;
void *SetAttribute;
EFI_STATUS (*ClearScreen)(struct EFI_SIMPLE_TEXT_OUTPUT_PROTOCOL *This);
void *SetCursorPosition;
void *EnableCursor;
void *Mode;
} EFI_SIMPLE_TEXT_OUTPUT_PROTOCOL;
// --- console input ---------------------------------------------------------
typedef struct {
UINT16 ScanCode;
UINT16 UnicodeChar;
} EFI_INPUT_KEY;
struct EFI_SIMPLE_TEXT_INPUT_PROTOCOL;
typedef EFI_STATUS (*EFI_INPUT_RESET)(
struct EFI_SIMPLE_TEXT_INPUT_PROTOCOL *This, unsigned char ExtendedVerification);
// Returns EFI_NOT_READY when nothing has been typed, which is what makes a
// timed prompt possible without blocking the boot.
typedef EFI_STATUS (*EFI_INPUT_READ_KEY)(
struct EFI_SIMPLE_TEXT_INPUT_PROTOCOL *This, EFI_INPUT_KEY *Key);
typedef struct EFI_SIMPLE_TEXT_INPUT_PROTOCOL {
EFI_INPUT_RESET Reset;
EFI_INPUT_READ_KEY ReadKeyStroke;
void *WaitForKey;
} EFI_SIMPLE_TEXT_INPUT_PROTOCOL;
// --- boot services --------------------------------------------------------
typedef struct {
char Hdr[24];
void *RaiseTPL;
void *RestoreTPL;
EFI_STATUS (*AllocatePages)(EFI_ALLOCATE_TYPE Type, UINTN MemoryType,
UINTN Pages, EFI_PHYSICAL_ADDRESS *Memory);
EFI_STATUS (*FreePages)(EFI_PHYSICAL_ADDRESS Memory, UINTN Pages);
EFI_STATUS (*GetMemoryMap)(UINTN *MemoryMapSize,
EFI_MEMORY_DESCRIPTOR *MemoryMap,
UINTN *MapKey, UINTN *DescriptorSize,
UINT32 *DescriptorVersion);
EFI_STATUS (*AllocatePool)(UINTN PoolType, UINTN Size, void **Buffer);
EFI_STATUS (*FreePool)(void *Buffer);
void *CreateEvent;
void *SetTimer;
void *WaitForEvent;
void *SignalEvent;
void *CloseEvent;
void *CheckEvent;
void *InstallProtocolInterface;
void *ReinstallProtocolInterface;
void *UninstallProtocolInterface;
EFI_STATUS (*HandleProtocol)(EFI_HANDLE Handle, EFI_GUID *Protocol,
void **Interface);
void *Reserved;
void *RegisterProtocolNotify;
void *LocateHandle;
void *LocateDevicePath;
void *InstallConfigurationTable;
EFI_STATUS (*LoadImage)(BOOLEAN BootPolicy, EFI_HANDLE ParentImage,
void *DevicePath, void *SourceBuffer,
UINTN SourceSize, EFI_HANDLE *ImageHandle);
EFI_STATUS (*StartImage)(EFI_HANDLE ImageHandle, UINTN *ExitDataSize,
CHAR16 **ExitData);
void *Exit;
EFI_STATUS (*UnloadImage)(EFI_HANDLE ImageHandle);
EFI_STATUS (*ExitBootServices)(EFI_HANDLE ImageHandle, UINTN MapKey);
void *GetNextMonotonicCount;
EFI_STATUS (*Stall)(UINTN Microseconds);
void *SetWatchdogTimer;
/* Recursive = TRUE asks for every driver that will bind, down the tree. */
EFI_STATUS (*ConnectController)(EFI_HANDLE Controller, EFI_HANDLE *DriverImage,
void *RemainingDevicePath, BOOLEAN Recursive);
void *DisconnectController;
void *OpenProtocol;
void *CloseProtocol;
void *OpenProtocolInformation;
void *ProtocolsPerHandle;
/* SearchType 2 is ByProtocol: every handle that speaks a given protocol,
* which is how the loader finds the displays LocateProtocol does not
* return -- it answers with the first one and there is no second call. */
EFI_STATUS (*LocateHandleBuffer)(UINTN SearchType, EFI_GUID *Protocol,
void *SearchKey, UINTN *NoHandles,
EFI_HANDLE **Buffer);
EFI_STATUS (*LocateProtocol)(EFI_GUID *Protocol, void *Registration,
void **Interface);
} EFI_BOOT_SERVICES;
// --- runtime services -----------------------------------------------------
//
// Only the two things the boot manager needs: variables, to ask the firmware to
// show its own setup on the next start, and ResetSystem, to restart or power
// off. The slots before them are spelled out as padding so the offsets hold.
#define EfiResetCold 0
#define EfiResetShutdown 2
#define EFI_VARIABLE_NON_VOLATILE 0x1
#define EFI_VARIABLE_BOOTSERVICE_ACCESS 0x2
#define EFI_VARIABLE_RUNTIME_ACCESS 0x4
#define EFI_GLOBAL_VARIABLE_GUID \
{ 0x8BE4DF61, 0x93CA, 0x11D2, { 0xAA, 0x0D, 0x00, 0xE0, 0x98, 0x03, 0x2B, 0x8C } }
/* OsIndications bit 0: "boot into the firmware's own user interface". */
#define EFI_OS_INDICATIONS_BOOT_TO_FW_UI 0x1ULL
typedef struct {
char Hdr[24];
void *GetTime;
void *SetTime;
void *GetWakeupTime;
void *SetWakeupTime;
void *SetVirtualAddressMap;
void *ConvertPointer;
EFI_STATUS (*GetVariable)(CHAR16 *Name, EFI_GUID *Vendor, UINT32 *Attributes,
UINTN *DataSize, void *Data);
void *GetNextVariableName;
EFI_STATUS (*SetVariable)(CHAR16 *Name, EFI_GUID *Vendor, UINT32 Attributes,
UINTN DataSize, void *Data);
void *GetNextHighMonotonicCount;
void (*ResetSystem)(UINTN ResetType, EFI_STATUS Status, UINTN DataSize,
void *ResetData);
} EFI_RUNTIME_SERVICES;
typedef struct {
char Hdr[24];
CHAR16 *FirmwareVendor;
UINT32 FirmwareRevision;
EFI_HANDLE ConsoleInHandle;
EFI_SIMPLE_TEXT_INPUT_PROTOCOL *ConIn;
EFI_HANDLE ConsoleOutHandle;
EFI_SIMPLE_TEXT_OUTPUT_PROTOCOL *ConOut;
EFI_HANDLE StandardErrorHandle;
EFI_SIMPLE_TEXT_OUTPUT_PROTOCOL *StdErr;
EFI_RUNTIME_SERVICES *RuntimeServices;
EFI_BOOT_SERVICES *BootServices;
UINTN NumberOfTableEntries;
void *ConfigurationTable;
} EFI_SYSTEM_TABLE;
// --- configuration tables -------------------------------------------------
//
// Where UEFI publishes things a BIOS left lying in low memory. The ACPI RSDP is
// the one that matters here: the kernel finds it by scanning the EBDA and
// 0xE0000-0x100000, which is a BIOS convention that firmware is under no
// obligation to honour -- and OVMF does not.
#define EFI_ACPI_20_TABLE_GUID \
{ 0x8868E871, 0xE4F1, 0x11D3, { 0xBC, 0x22, 0x00, 0x80, 0xC7, 0x3C, 0x88, 0x81 } }
#define EFI_ACPI_10_TABLE_GUID \
{ 0xEB9D2D30, 0x2D88, 0x11D3, { 0x9A, 0x16, 0x00, 0x90, 0x27, 0x3F, 0xC1, 0x4D } }
#define SMBIOS_TABLE_GUID \
{ 0xEB9D2D31, 0x2D88, 0x11D3, { 0x9A, 0x16, 0x00, 0x90, 0x27, 0x3F, 0xC1, 0x4D } }
#define SMBIOS3_TABLE_GUID \
{ 0xF2FD1544, 0x9794, 0x4A2C, { 0x99, 0x2E, 0xE5, 0xBB, 0xCF, 0x20, 0xE3, 0x94 } }
typedef struct {
EFI_GUID VendorGuid;
void *VendorTable;
} EFI_CONFIGURATION_TABLE;
// --- device path ----------------------------------------------------------
//
// Only ever asked whether a handle *has* one: a protocol installed by a real
// device's driver sits on a handle with a path to that device, and a virtual
// one -- the console splitter's GOP -- sits on a handle with none.
#define EFI_DEVICE_PATH_PROTOCOL_GUID \
{ 0x09576E91, 0x6D3F, 0x11D2, { 0x8E, 0x39, 0x00, 0xA0, 0xC9, 0x69, 0x72, 0x3B } }
// --- loaded image ---------------------------------------------------------
#define EFI_LOADED_IMAGE_PROTOCOL_GUID \
{ 0x5B1B31A1, 0x9562, 0x11D2, { 0x8E, 0x3F, 0x00, 0xA0, 0xC9, 0x69, 0x72, 0x3B } }
typedef struct {
UINT32 Revision;
EFI_HANDLE ParentHandle;
void *SystemTable;
EFI_HANDLE DeviceHandle; // the volume this image was loaded from
void *FilePath;
void *Reserved;
UINT32 LoadOptionsSize;
void *LoadOptions;
void *ImageBase;
UINT64 ImageSize;
UINTN ImageCodeType;
UINTN ImageDataType;
void *Unload;
} EFI_LOADED_IMAGE_PROTOCOL;
// --- file system ----------------------------------------------------------
#define EFI_SIMPLE_FILE_SYSTEM_PROTOCOL_GUID \
{ 0x0964E5B22, 0x6459, 0x11D2, { 0x8E, 0x39, 0x00, 0xA0, 0xC9, 0x69, 0x72, 0x3B } }
#define EFI_FILE_MODE_READ 0x0000000000000001ULL
typedef struct EFI_FILE_PROTOCOL {
UINT64 Revision;
EFI_STATUS (*Open)(struct EFI_FILE_PROTOCOL *This,
struct EFI_FILE_PROTOCOL **NewHandle,
CHAR16 *FileName, UINT64 OpenMode, UINT64 Attributes);
EFI_STATUS (*Close)(struct EFI_FILE_PROTOCOL *This);
void *Delete;
EFI_STATUS (*Read)(struct EFI_FILE_PROTOCOL *This, UINTN *BufferSize,
void *Buffer);
void *Write;
EFI_STATUS (*GetPosition)(struct EFI_FILE_PROTOCOL *This, UINT64 *Position);
EFI_STATUS (*SetPosition)(struct EFI_FILE_PROTOCOL *This, UINT64 Position);
void *GetInfo;
void *SetInfo;
void *Flush;
} EFI_FILE_PROTOCOL;
typedef struct EFI_SIMPLE_FILE_SYSTEM_PROTOCOL {
UINT64 Revision;
EFI_STATUS (*OpenVolume)(struct EFI_SIMPLE_FILE_SYSTEM_PROTOCOL *This,
EFI_FILE_PROTOCOL **Root);
} EFI_SIMPLE_FILE_SYSTEM_PROTOCOL;
// --- graphics output ------------------------------------------------------
#define EFI_GRAPHICS_OUTPUT_PROTOCOL_GUID \
{ 0x9042A9DE, 0x23DC, 0x4A38, { 0x96, 0xFB, 0x7A, 0xDE, 0xD0, 0x80, 0x51, 0x6A } }
#define PixelRedGreenBlueReserved8BitPerColor 0
#define PixelBlueGreenRedReserved8BitPerColor 1
#define PixelBitMask 2
#define PixelBltOnly 3
typedef struct {
UINT32 Version;
UINT32 HorizontalResolution;
UINT32 VerticalResolution;
UINT32 PixelFormat;
UINT32 PixelInformation[4];
UINT32 PixelsPerScanLine;
} EFI_GRAPHICS_OUTPUT_MODE_INFORMATION;
typedef struct {
// MaxMode first, then Mode. That is the specification's order and getting
// it backwards is invisible until something reads them: Info,
// SizeOfInfo and FrameBufferBase all sit at the same offsets either way,
// because both fields are UINT32. It was backwards here, so the first code
// to ask how many modes existed was told the current mode number instead --
// zero, on a machine with plenty of them.
UINT32 MaxMode;
UINT32 Mode;
EFI_GRAPHICS_OUTPUT_MODE_INFORMATION *Info;
UINTN SizeOfInfo;
EFI_PHYSICAL_ADDRESS FrameBufferBase;
UINTN FrameBufferSize;
} EFI_GRAPHICS_OUTPUT_PROTOCOL_MODE;
struct EFI_GRAPHICS_OUTPUT_PROTOCOL;
// Ask what a mode is, and switch to one. Declared properly rather than as
// void*, because choosing a resolution means walking every mode the firmware
// offers and asking each one its size -- the same shape as the BIOS path's
// VBE enumeration, and for the same reason: a table of numbers can only
// describe hardware somebody already tested.
typedef EFI_STATUS (*EFI_GOP_QUERY_MODE)(
struct EFI_GRAPHICS_OUTPUT_PROTOCOL *This, UINT32 ModeNumber,
UINTN *SizeOfInfo, EFI_GRAPHICS_OUTPUT_MODE_INFORMATION **Info);
typedef EFI_STATUS (*EFI_GOP_SET_MODE)(
struct EFI_GRAPHICS_OUTPUT_PROTOCOL *This, UINT32 ModeNumber);
/* Blt is how you draw on a GOP that has no linear framebuffer to write into --
* and there is one of those on every machine with a virtio-gpu, because the
* device has no aperture. It is also the only way to put pixels on a *second*
* display: the loader draws into the primary's framebuffer directly and then
* hands that same memory to the other GOP's Blt as a source buffer. */
struct EFI_GRAPHICS_OUTPUT_PROTOCOL;
#define ByProtocol 2
#define EfiBltVideoFill 0
#define EfiBltBufferToVideo 2
typedef EFI_STATUS (*EFI_GOP_BLT)(struct EFI_GRAPHICS_OUTPUT_PROTOCOL *This,
void *BltBuffer, UINTN BltOperation,
UINTN SourceX, UINTN SourceY,
UINTN DestinationX, UINTN DestinationY,
UINTN Width, UINTN Height, UINTN Delta);
typedef struct EFI_GRAPHICS_OUTPUT_PROTOCOL {
EFI_GOP_QUERY_MODE QueryMode;
EFI_GOP_SET_MODE SetMode;
EFI_GOP_BLT Blt;
EFI_GRAPHICS_OUTPUT_PROTOCOL_MODE *Mode;
} EFI_GRAPHICS_OUTPUT_PROTOCOL;
// --- pointers -------------------------------------------------------------
//
// Two protocols, because firmware offers whichever the device is: a mouse or
// touchpad reports movement (SIMPLE_POINTER), a tablet -- QEMU's, and most
// touchscreens -- reports where it is (ABSOLUTE_POINTER). Both are polled.
#define EFI_SIMPLE_POINTER_PROTOCOL_GUID \
{ 0x31878C87, 0x0B75, 0x11D5, { 0x9A, 0x4F, 0x00, 0x90, 0x27, 0x3F, 0xC1, 0x4D } }
#define EFI_ABSOLUTE_POINTER_PROTOCOL_GUID \
{ 0x8D59D32B, 0xC655, 0x4AE9, { 0x9B, 0x15, 0xF2, 0x59, 0x04, 0x99, 0x2A, 0x43 } }
#define EFI_NOT_READY ((EFI_STATUS)(0x8000000000000000ULL | 6))
typedef struct { int RelativeMovementX, RelativeMovementY, RelativeMovementZ;
BOOLEAN LeftButton, RightButton; } EFI_SIMPLE_POINTER_STATE;
typedef struct { UINT64 ResolutionX, ResolutionY, ResolutionZ;
BOOLEAN LeftButton, RightButton; } EFI_SIMPLE_POINTER_MODE;
typedef struct EFI_SIMPLE_POINTER_PROTOCOL {
EFI_STATUS (*Reset)(struct EFI_SIMPLE_POINTER_PROTOCOL *This, BOOLEAN Extended);
EFI_STATUS (*GetState)(struct EFI_SIMPLE_POINTER_PROTOCOL *This,
EFI_SIMPLE_POINTER_STATE *State);
void *WaitForInput;
EFI_SIMPLE_POINTER_MODE *Mode;
} EFI_SIMPLE_POINTER_PROTOCOL;
typedef struct { UINT64 CurrentX, CurrentY, CurrentZ; UINT32 ActiveButtons; }
EFI_ABSOLUTE_POINTER_STATE;
typedef struct { UINT64 AbsoluteMinX, AbsoluteMinY, AbsoluteMinZ,
AbsoluteMaxX, AbsoluteMaxY, AbsoluteMaxZ;
UINT32 Attributes; } EFI_ABSOLUTE_POINTER_MODE;
typedef struct EFI_ABSOLUTE_POINTER_PROTOCOL {
EFI_STATUS (*Reset)(struct EFI_ABSOLUTE_POINTER_PROTOCOL *This, BOOLEAN Extended);
EFI_STATUS (*GetState)(struct EFI_ABSOLUTE_POINTER_PROTOCOL *This,
EFI_ABSOLUTE_POINTER_STATE *State);
void *WaitForInput;
EFI_ABSOLUTE_POINTER_MODE *Mode;
} EFI_ABSOLUTE_POINTER_PROTOCOL;
#endif /* SCHOS_EFI_H */