SchOS/boot/uefi/efi.h
Nexoniarz eab13241a0 hwreport: describe a machine onto a USB stick, from a Linux-style /sys
hwreport writes one folder to the first read-write FAT mount, or mounts
the first USB stick that takes vfat itself and unmounts it afterwards:
summary.txt (machine, firmware, CPU, memory, every PCI and USB device
with its driver or "-- no driver"), dmesg.txt, lspci.txt in the shape of
lspci -nnk -xxx, lsusb.txt, dmi.txt, memmap.txt, the ACPI tables as raw
.dat files iasl -d reads, and copies of /proc. Serials, the UUID and the
MSDM/SLIC tables (a Windows licence key) are left out.

It reads /sys, which is new: sysfs at Linux's paths and in Linux's
formats -- /sys/bus/pci/devices with uevent, config and resource,
/sys/bus/usb/devices, /sys/firmware/acpi/tables, dmi/tables, memmap
and efi, /sys/class/dmi/id. toybox's lspci and lsusb work unmodified
against it. The USB core now keeps a table of attached devices for it.

The machine's name comes from SMBIOS (kernel/dev/dmi.c). The UEFI loader
hands over the entry point from the configuration table, the kernel
falls back to the 0xF0000 scan, and the log gets Linux's "DMI:" line.

Two bugs found on the way:

- The loader handed over at most 40 memory-map entries, in firmware
  order. A laptop's UEFI map is 60-150 descriptors with RAM above 4 GB
  listed last, so that is what was dropped. The entries now have their
  own area at 0x31000 with room for 256, are sorted before merging, and
  the kernel's 32 usable regions keep the largest when there are more.
- MemTotal and sysinfo's totalram were PHYSTOP, an address, which counts
  the PCI hole: a 6 GB machine reported 8. Now the usable RAM, summed.

usb.ids moves to /usr/share/hwdata, where lsusb looks for it, and
pci.ids joins it (`make pci-ids`, BSD-3-Clause like usb.ids).

Checked under QEMU q35: the report written to a FAT32 stick, fsck.fat
clean, DSDT.dat disassembled by iasl on the host, lspci -k and lsusb
showing names, 6 GB of RAM seen as 6137 MB.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01S1giNvrHTrPMPw7V6x1kQ4
2026-10-23 12:54:11 +02:00

342 lines
13 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;
void *LoadImage;
void *StartImage;
void *Exit;
void *UnloadImage;
EFI_STATUS (*ExitBootServices)(EFI_HANDLE ImageHandle, UINTN MapKey);
void *GetNextMonotonicCount;
EFI_STATUS (*Stall)(UINTN Microseconds);
void *SetWatchdogTimer;
void *ConnectController;
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;
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;
void *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;
// --- 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;
#endif /* SCHOS_EFI_H */