Skip to content

UltraOS/UltraProtocol

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

40 Commits
 
 
 
 
 
 

Repository files navigation

Introduction

Ultra loader protocol defines a set of services and interfaces used by a bootloader to load a kernel/binary file.

The protocol doesn't require any kind of sections residing inside the kernel binary and fully relies on a loader-specific configuration file to define all the necessary load options and parameters. This allows all parameters to be changed dynamically without recompiling the kernel, and lets the same binary stay compatible with Ultra and any other protocol at the same time.

Protocol Description

After handoff, the kernel is provided with a boot context that has the following structure:

struct ultra_boot_context {
    uint8_t protocol_major;
    uint8_t protocol_minor;
    uint16_t reserved;

    uint32_t attribute_count;
    struct ultra_attribute_header attributes[];
};
  • protocol_major - major version of the protocol, valid versions start at 1
  • protocol_minor - minor version of the protocol, valid versions start at 0
  • attribute_count - the number of attributes in the attributes array
  • attributes - a contiguous array of attributes provided by the loader

The following C macro can be used to retrieve the next attribute from current:

#define ULTRA_NEXT_ATTRIBUTE(current) ((struct ultra_attribute_header*)(((uint8_t*)(current)) + (current)->size))

ultra_attribute_header and other structures are described in the following sections.

The kernel is also provided with a protocol magic number ULTRA_MAGIC, which is defined as:
0x554c5442 - 'ULTB' in ASCII

The kernel entrypoint ends up looking something like this in C:

void kernel_entry(struct ultra_boot_context *ctx, uint32_t magic)

The way the kernel receives this magic number is platform defined and is discussed in later sections.

If the kernel detects an invalid magic number, the ultra_boot_context* must be considered invalid, and it's recommended that the kernel aborts boot.


Protocol Features And Options

Current protocol version is defined as 1.0.

This section defines various options and features defined by the protocol. The actual way to enable/set an option is loader specific and is defined by its configuration file format.

Binary Options

Defines various kernel binary related options.

  • binary - (string) - shorthand alias for binary/path
  • binary/path - (string) - specifies the path to the kernel in a loader-defined format, if applicable.
  • binary/allocate-anywhere (bool, optional, default=false) - allows the kernel physical memory mappings to be allocated anywhere in memory. Only valid for 64 bit higher-half kernels.

Page Table Options

Defines various options related to the page table properties set up at handover.

  • page-table/levels - (integer, optional, default=4) - specifies the desired depth of the page table
  • page-table/constraint - (string, optional, default="maximum") - specifies the constraint for the "levels" value, one of "maximum", "at-least", "exactly"
  • page-table/null-guard - (bool, optional, default=false) - if set to true, the first physical page is not identity mapped to prevent accidental NULL accesses.

Command Line

Defines a command line to give to the kernel. This attribute is not generated if cmdline is set to null.

  • cmdline (string, optional, default=null) - kernel command line, passed via ultra_command_line_attribute.

Binary Stack

Defines various options related to the address passed in the arch-specific SP register on handover.

  • stack (string/unsigned) - shorthand alias for stack/size. Also allows a literal "auto".
  • stack/size (unsigned, optional, default=16K) - page aligned size of the stack to allocate.
  • stack/allocate-at (string/unsigned, default=<implementation-defined>) - address where to allocate the stack

Video Mode & Framebuffer

Defines options related to the video-mode set by the loader before handover.

If an exact resolution is requested (both width and height), the loader sets that resolution and fails if no such mode exists. Otherwise the loader picks an appropriate mode on its own: the display's native resolution if it can be determined (e.g. via EDID), otherwise the mode the firmware is already in if there is one, otherwise the highest available mode.

  • video-mode - (string, optional, default="auto") - shorthand. "auto" lets the loader pick an appropriate mode as described above, null or "unset" request no framebuffer at all, in which case ultra_framebuffer_attribute is not generated.
  • video-mode/width - (unsigned, optional) - requests a specific framebuffer width. If set, height must be set too.
  • video-mode/height - (unsigned, optional) - requests a specific framebuffer height. If set, width must be set too.
  • video-mode/bpp (unsigned, optional, default=32) - requests a specific framebuffer bits per pixel value. If unset, the loader prefers 32 but falls back to the best available.
  • video-mode/format (string, optional, default="auto") - requests a specific framebuffer format, one of "auto", "rgb888", "bgr888", "rgbx8888", "xrgb8888" (any case). Always matched exactly if specified.

Kernel Modules

Ultra protocol offers different types of modules: classic file-backed modules as well as raw RAM allocations.

The following options must be used to request a module:

  • kernel-as-module - (bool, optional, default=false) - requests the loader to pass the full kernel binary as a separate module, this can be used for parsing additional debug information, enforcing memory protection for program headers from an ELF file or any other purpose.
  • module - (string) - shorthand alias for module/path
  • module/type (string, optional, default="file") - one of "file" or "memory". File modules require a path argument and simply make the loader preload a file from disk into RAM. Memory modules are used to request general purpose contiguous zeroed memory allocations without any backing, and can be used for bootstrapping kernel allocators or any other purpose.
  • module/size (unsigned/string, optional*, default="auto") - defines the size of the module. Mandatory for "memory" modules. Can be used to truncate or extend "file" modules, if this is bigger than the file size, the rest of the memory is zeroed.
  • module/name - (string, optional, default=<implementation-defined>) - name of the module that the kernel receives
  • module/description - (string, optional, default=null) - free-form description of the module that the kernel receives via the description field of ultra_module_info_attribute. No description is generated if unset or empty.
  • module/path - (string, optional*) - specifies the path for the module to load in a loader-defined format, if applicable
  • module/load-at (unsigned/string, optional, default="anywhere") - specifies the load address for the module or "anywhere"

Each directive generates a separate ultra_module_info_attribute

Miscellaneous

  • higher-half-exclusive - (bool, optional, default=false) - if set to true, no identity mappings are provided for lower half. All loader-provided attribute address fields are relocated to higher half if present. Only applicable for higher-half kernels.
  • setup-apm - (bool, optional, default=false) - if set to true, requests the loader to set up a 32-bit protected mode interface for the APM. The kernel is provided with ultra_apm_attribute in case of successful installation.
  • pass-uefi-info - (bool, optional, default=false) - if set to true and the loader was booted via UEFI, requests the loader to pass the UEFI system table and firmware memory map to the kernel via ultra_uefi_info_attribute. Silently ignored on non-UEFI platforms.

State After Handoff

This section describes the system state for each supported architecture that system software must expect after gaining control.

x86

Page size is defined as 4096 bytes.

  • A20 - enabled
  • R/EFLAGS - zeroed, reserved bit 1 set
  • IDTR - contents are unspecified
  • GDTR - set to a valid GDT with at least ring 0 flat code/data descriptors
  • Long/protected mode - set as determined by the kernel binary
  • CS - set to a flat ring 0 code segment
  • DS, ES, FS, GS, SS - set to a flat ring 0 data segment

i386

Higher half is defined as 0xC000'0000
The kernel is considered higher half if it wants to be loaded at or above 0xC010'0000

  • Paging enabled
  • EIP - set to the entrypoint as specified by the kernel binary
  • EAX, ECX, EDX, EBX, EBP, ESI, EDI - zeroed
  • ESP - set to a valid stack pointer as determined by the configuration, aligned according to SysV ABI
  • *ESP+8 - ULTRA_MAGIC
  • *ESP+4 - ultra_boot_context*
virtual address physical address length of the mapping
0x0000'0000 0x0000'0000 up to 3 GiB
0xC000'0000 0x0000'0000 up to 1 GiB

Only usable RAM within these windows is mapped (up to 3 GiB in the identity map and 1 GiB in the direct map). Reserved and device (MMIO) memory is intentionally left unmapped; the kernel must map such regions itself with the appropriate caching type.

The first mapping is not provided for the higher-half-exclusive mode.

AMD64

Higher half is defined as 0xFF00'0000'0000'0000 or 0xFFFF'8000'0000'0000, depending on page-table/levels
The kernel is considered higher half if it wants to be loaded at or above 0xFFFF'FFFF'8000'0000

  • RIP - set to the entrypoint as specified by the kernel binary
  • RDI - ultra_boot_context*
  • RSI - ULTRA_MAGIC
  • RAX, RCX, RDX, RBX, RBP, R8, R9, R10, R11, R12, R13, R14, R15 - zeroed
  • RSP - set to a valid stack pointer as determined by the configuration, aligned according to SysV ABI
  • CR3 - a valid address of a PML4 with the following mappings:

Higher half base address depends on the value of page-table/levels with the following conditions:

  • 0xFF00'0000'0000'0000 - 'levels' = 5 with a matching constraint
  • 0xFFFF'8000'0000'0000 - 'levels' = 4 with a matching constraint
virtual address physical address length of the mapping
0x0000'0000'0000'0000 0x0000'0000'0000'0000 all usable RAM
0xFFFF'8000'0000'0000 OR
0xFF00'0000'0000'0000
0x0000'0000'0000'0000 all usable RAM
0xFFFF'FFFF'8000'0000 ????????????????????? ?????????????????????

The first two mappings cover all usable RAM regions from the memory map, both at their physical address and at the higher-half direct map base. Reserved and device (MMIO) memory is intentionally left unmapped; the kernel must map such regions itself with the appropriate caching type.

The first mapping is not provided for the higher-half-exclusive mode.

For higher half kernels loaded with allocate-anywhere set to true the last mapping contains the kernel binary mappings with an arbitrary physical base picked by the loader. For all other kernels it is a direct mapping of the first 2 GiB of physical ram.


Address pointed to by CR3 is located somewhere within ULTRA_MEMORY_TYPE_LOADER_RECLAIMABLE. Whether the memory is mapped using 4K/2M/4M/1G pages is unspecified.

The contents of all other registers are unspecified.

AARCH64

The state of all registers not listed here is unspecified.
The kernel should not assume that any device memory is actually mapped as such.
Normal memory is guaranteed to be accessible.

  • MMU enabled
  • Data and instruction caches enabled (SCTLR_ELx.{C, I} set)
  • Exception level 1 OR 2
    • If EL2: FEAT_VHE is supported and HCR_EL2.{E2H, TGE} are set to {1, 1}
  • Current EL stack alignment checking is enabled
  • SPSel set to SP_ELx
  • DAIF set to 0b1111
  • NZCV set to 0b000

All mapped Normal memory (the identity and direct-map windows) is mapped as Normal, Inner & Outer Write-Back cacheable, Inner Shareable, using AttrIndx=0.

MAIR_ELx is set to the following guaranteed layout so that the kernel can map device memory before establishing its own translation tables:

AttrIndx Memory type
0 Normal, Inner & Outer Write-Back cacheable (RA/WA)
1 Device-nGnRnE
2 Normal, Inner & Outer Non-cacheable

The kernel is responsible for mapping device memory (MMIO, framebuffer, etc.) itself, using AttrIndx=1 or AttrIndx=2 from the layout above, or by reprogramming MAIR_ELx and its translation tables entirely.

  • PC - set to the entrypoint as specified by the kernel binary
  • x0 - ultra_boot_context*
  • x1 - ULTRA_MAGIC
  • x2..x29 - zeroed
  • SP - set to a valid stack pointer as determined by the configuration, aligned according to the aarch64 ABI
  • TTBR{0,1}_ELx - a valid address of a page table with the following mappings:

Higher half is defined as 0xFFF0'0000'0000'0000 or 0xFFFF'0000'0000'0000, depending on page-table/levels
The kernel is considered higher half if it wants to be loaded at or above 0xFFFF'FFFF'8000'0000

Higher half base address depends on the value of page-table/levels with the following conditions:

  • 0xFFF0'0000'0000'0000 - 'levels' = 5 with a matching constraint
  • 0xFFFF'0000'0000'0000 - 'levels' = 4 with a matching constraint
virtual address physical address length of the mapping
0x0000'0000'0000'0000 0x0000'0000'0000'0000 all usable RAM
0xFFFF'0000'0000'0000 OR
0xFFF0'0000'0000'0000
0x0000'0000'0000'0000 all usable RAM
0xFFFF'FFFF'8000'0000 ????????????????????? ?????????????????????

The first two mappings cover all usable RAM regions from the memory map, both at their physical address and at the higher-half direct map base. Reserved and device (MMIO) memory is intentionally left unmapped; the kernel must map such regions itself with the appropriate caching type.

The first mapping is not provided for the higher-half-exclusive mode.

For higher half kernels loaded with allocate-anywhere set to true the last mapping contains the kernel binary mappings with an arbitrary physical base picked by the loader. For all other kernels it is a direct mapping of the first 2 GiB of physical ram.

Address pointed to by TTBR{0/1}_ELx is located somewhere within ULTRA_MEMORY_TYPE_LOADER_RECLAIMABLE.
The granule size, as well as the OA size of the mapped pages is unspecified and is up to the implementation.


Attributes

The way the loader provides various information to the kernel is via attributes.

Guarantees about the attribute array & attributes:

  • All attributes are guaranteed to be aligned on an 8 (or more if required) byte boundary.
  • All attributes and address fields, as well as any loader-allocated memory that might be accessed by the kernel is guaranteed to be within the mapped address space range. E.g. for a 32-bit higher-half-exclusive kernel, all attributes and modules are guaranteed to be allocated under 1 GiB of physical memory so that they can be safely accessed by the kernel.
  • The location of any specific attribute within the array is not fixed unless specified otherwise (in the attribute description).
  • All attributes of the same type are guaranteed to be a contiguous stream.
  • Every attribute type can only appear in the attribute array once unless specified otherwise (in the attribute description).
  • All attribute types are guaranteed to have ultra_attribute_header as the first member, this includes both current & future attributes.
  • It is safe to ignore any unknown attribute types.
  • All currently defined attributes are guaranteed to keep the same layout in the future protocol versions, but might have new members added at the end, which shouldn't affect any valid software as the header size field will be adjusted accordingly.

Every attribute has a distinct type and starts with the following header:

struct ultra_attribute_header {
    uint32_t type;
    uint32_t size;
};
  • type - one of the following values (each type is detailed in the following sections):
#define ULTRA_ATTRIBUTE_INVALID          0
#define ULTRA_ATTRIBUTE_PLATFORM_INFO    1
#define ULTRA_ATTRIBUTE_KERNEL_INFO      2
#define ULTRA_ATTRIBUTE_MEMORY_MAP       3
#define ULTRA_ATTRIBUTE_MODULE_INFO      4
#define ULTRA_ATTRIBUTE_COMMAND_LINE     5
#define ULTRA_ATTRIBUTE_FRAMEBUFFER_INFO 6
#define ULTRA_ATTRIBUTE_APM_INFO         7
#define ULTRA_ATTRIBUTE_UEFI_INFO        8
  • size - size of the entire attribute including the header. This size is often used for calculating the number of entries in a variable length attribute. This isn't always possible, because sometimes the size is increased on purpose to align entries to 8 bytes. Attributes like this are explicitly documented and provide a separate member that indicates the actual size of the variable size field.

Attribute Types

This section describes all currently implemented attribute types and their structure.


ULTRA_ATTRIBUTE_INVALID

Reserved. If encountered, must be considered a fatal error.


ULTRA_ATTRIBUTE_PLATFORM_INFO

This attribute is guaranteed to be the first entry in the attribute array.

This attribute provides various information about the platform and has the following structure:

struct platform_info_attribute {
    struct ultra_attribute_header header;
    uint32_t platform_type;

    uint16_t loader_major;
    uint16_t loader_minor;
    char loader_name[32];

    uint64_t acpi_rsdp_address;
    uint64_t higher_half_base;
    uint8_t page_table_depth;
    uint8_t reserved[7];
    uint64_t dtb_address;
    uint64_t smbios_address;
};
  • header - standard attribute header
  • platform_type - one of the following values:
#define ULTRA_PLATFORM_INVALID 0
#define ULTRA_PLATFORM_BIOS    1
#define ULTRA_PLATFORM_UEFI    2
  • loader_major - major version of the loader
  • loader_minor - minor version of the loader
  • loader_name - null-terminated ASCII string with a human-readable name of the loader
  • acpi_rsdp_address - physical address of the RSDP structure, 0 if not applicable or not present
  • higher_half_base - virtual address of the higher half base
  • page_table_depth - currently configured page table depth
  • reserved - reserved for future use
  • dtb_address - physical address of the device tree blob, 0 if not applicable or not present
  • smbios_address - physical address of the SMBIOS entry point structure (either 32 or 64 bits as indicated by the signature), 0 if not applicable or not present

ULTRA_PLATFORM_INVALID

Reserved. If encountered, must be considered a fatal error.

ULTRA_PLATFORM_BIOS

The kernel was loaded using the BIOS services and platform.

ULTRA_PLATFORM_UEFI

The kernel was loaded using the UEFI services and platform.

Any Other Value

Reserved for future use, must be ignored by the kernel.


ULTRA_ATTRIBUTE_KERNEL_INFO

This attribute is guaranteed to be the second entry in the attribute array.

This attribute provides various information about the kernel binary and has the following structure:

struct ultra_kernel_info_attribute {
    struct ultra_attribute_header header;

    uint64_t physical_base;
    uint64_t virtual_base;
    uint64_t size;

    uint64_t partition_type;

    // only valid if partition_type == ULTRA_PARTITION_TYPE_GPT
    struct ultra_guid disk_guid;

    union {
        // only valid if partition_type == ULTRA_PARTITION_TYPE_GPT
        struct ultra_guid partition_guid;
        // only valid if partition_type == ULTRA_PARTITION_TYPE_PXE_V4
        struct ultra_ipv4_addr pxe_v4;
        // only valid if partition_type == ULTRA_PARTITION_TYPE_PXE_V6
        struct ultra_ipv6_addr pxe_v6;
    };

    uint32_t disk_index;
    uint32_t partition_index;

    char fs_path[ULTRA_PATH_MAX];
};
  • header - standard attribute header
  • physical_base - physical address of the kernel base, page aligned
  • virtual_base - virtual address of the kernel base, page aligned
  • size - number of bytes taken by the kernel, page aligned
  • partition_type - one of the following values:
#define ULTRA_PARTITION_TYPE_INVALID 0
#define ULTRA_PARTITION_TYPE_RAW     1
#define ULTRA_PARTITION_TYPE_MBR     2
#define ULTRA_PARTITION_TYPE_GPT     3
#define ULTRA_PARTITION_TYPE_PXE_V4  4
#define ULTRA_PARTITION_TYPE_PXE_V6  5
  • disk_guid - GUID of the disk that the kernel was loaded from, only valid for ULTRA_PARTITION_TYPE_GPT
  • partition_guid - GUID of the partition that the kernel was loaded from, only valid for ULTRA_PARTITION_TYPE_GPT
  • pxe_server - the IP address of the PXE server the kernel was loaded from, only valid for ULTRA_PARTITION_TYPE_PXE
  • disk_index - index of the disk the kernel was loaded from, always 0 for ULTRA_PARTITION_TYPE_PXE_V{4,6}
  • partition_index - index of the partition the kernel was loaded from, index >= 4 implies EBR partition N - 4 for an MBR disk, always 0 for ULTRA_PARTITION_TYPE_PXE_V{4,6}
  • fs_path - null terminated UTF-8 string, absolute POSIX path to the kernel binary on the partition

ULTRA_PATH_MAX is defined as 256 including the terminating NULL character.

ULTRA_PARTITION_TYPE_INVALID

Reserved. If encountered, must be considered a fatal error.

ULTRA_PARTITION_TYPE_RAW

Unpartitioned device without an MBR/GPT header, the entire disk treated as one file system.

ULTRA_PARTITION_TYPE_MBR

Standard MBR partition, either MBR or EBR.

ULTRA_PARTITION_TYPE_GPT

Standard GPT partition, whether the device also contains a valid MBR is unspecified as GPT always takes precedence.

ultra_guid is defined as:

struct ultra_guid {
    uint32_t data1;
    uint16_t data2;
    uint16_t data3;
    uint8_t  data4[8];
};

Note that the structure is only guaranteed to be 8 byte aligned within ultra_kernel_info_attribute.

ULTRA_PARTITION_TYPE_PXE_V4 and ULTRA_PARTITION_TYPE_PXE_V6

Preboot Execution Environment (network boot) media. The kernel was loaded over the network via TFTP, pxe_v4/pxe_v6 contains the IP address of the server it was loaded from.

struct ultra_ipv4_addr {
    uint8_t addr[4];
};

struct ultra_ipv6_addr {
    uint8_t addr[16];
};

type indicates which member of the union is valid. Note that the structure is only guaranteed to be 8 byte aligned within ultra_kernel_info_attribute.

Any Other Value

Reserved for future use, must be ignored by the kernel.


ULTRA_ATTRIBUTE_MEMORY_MAP

This attribute provides a physical memory map of the entire system. It has the following format:

struct ultra_memory_map_attribute {
    struct ultra_attribute_header header;
    struct ultra_memory_map_entry entries[];
};
  • header - standard attribute header
  • entries - an array of non-overlapping and sorted in ascending order by address entries, where each entry has the following structure:
struct ultra_memory_map_entry {
    uint64_t physical_address;
    uint64_t size;
    uint64_t type;
};
  • physical_address - first byte of the physical range covered by this entry
  • size - size of this range
  • type - one of the following values:
#define ULTRA_MEMORY_TYPE_INVALID            0x00000000
#define ULTRA_MEMORY_TYPE_FREE               0x00000001
#define ULTRA_MEMORY_TYPE_RESERVED           0x00000002
#define ULTRA_MEMORY_TYPE_ACPI_RECLAIMABLE   0x00000003
#define ULTRA_MEMORY_TYPE_ACPI_NVS           0x00000004
#define ULTRA_MEMORY_TYPE_LOADER_RECLAIMABLE 0xFFFF0001
#define ULTRA_MEMORY_TYPE_MODULE             0xFFFF0002
#define ULTRA_MEMORY_TYPE_KERNEL_STACK       0xFFFF0003
#define ULTRA_MEMORY_TYPE_KERNEL_BINARY      0xFFFF0004

ULTRA_MEMORY_TYPE_INVALID

Reserved. If encountered, must be considered a fatal error.

ULTRA_MEMORY_TYPE_FREE

General purpose memory free for use by the kernel.

ULTRA_MEMORY_TYPE_RESERVED

Memory reserved by the firmware.

ULTRA_MEMORY_TYPE_ACPI_RECLAIMABLE

Memory tagged as ACPI reclaimable by the firmware. Usually contains ACPI tables.

ULTRA_MEMORY_TYPE_ACPI_NVS

Same as MEMORY_TYPE_RESERVED. Consult the ACPI specification for more information.

ULTRA_MEMORY_TYPE_LOADER_RECLAIMABLE

Memory reserved by the bootloader. Contains temporary GDT, all attributes, memory map itself, loader code, and everything else the loader has allocated the memory for. The actual location of all the aforementioned structures within this range is unspecified.

Can be reclaimed by the kernel when it no longer needs the loader-provided structures.

ULTRA_MEMORY_TYPE_MODULE

Memory region containing one or more kernel modules

ULTRA_MEMORY_TYPE_KERNEL_STACK

Memory region reserved by the loader for the kernel stack. Not necessarily the same value as SP for higher half kernels.

ULTRA_MEMORY_TYPE_KERNEL_BINARY

Memory region reserved by the loader for the loaded kernel binary (not the ELF copy).

Any Other Value

Reserved for future use. Must be considered same as ULTRA_MEMORY_TYPE_RESERVED if encountered by the kernel.

The number of memory map entries can be calculated using the following C macro:

#define ULTRA_MEMORY_MAP_ENTRY_COUNT(header) ((((header).size) - sizeof(struct ultra_attribute_header)) / sizeof(struct ultra_memory_map_entry))

ULTRA_ATTRIBUTE_MODULE_INFO

Every loaded kernel module gets a respective attribute of this type, this means ultra_boot_context must contain as many attributes of this type as there are modules.

This attribute provides information necessary to locate a kernel module in memory and has the following structure:

struct ultra_module_info_attribute {
    struct ultra_attribute_header header;
    uint32_t reserved;
    uint32_t type;
    char name[64];
    uint64_t address;
    uint64_t size;
    char description[];
};
  • header - standard attribute header
  • reserved - for use by future versions of the protocol, must be ignored
  • type - type of this module, can be one of the following values:
#define ULTRA_MODULE_TYPE_INVALID 0
#define ULTRA_MODULE_TYPE_FILE    1
#define ULTRA_MODULE_TYPE_MEMORY  2

ULTRA_MODULE_TYPE_INVALID

Reserved. If encountered, must be considered a fatal error.

ULTRA_MODULE_TYPE_FILE

File-backed module.

ULTRA_MODULE_TYPE_MEMORY

Contiguous zeroed memory module.

  • name - null-terminated ASCII name of the module, as specified in the configuration file or "__KERNEL__" for the autogenerated kernel binary module (if enabled)
  • address - address of the loaded module, page aligned
  • size - size of the module as specified in module/size, the actual size in RAM is this value rounded up to page size
  • description - optional null-terminated ASCII description of the module, as specified in the configuration file

A module only carries a description if one was provided in the configuration file. Whether a description is present can be tested with the following C macro:

#define ULTRA_MODULE_HAS_DESCRIPTION(header) ((((header).size) - sizeof(struct ultra_module_info_attribute)) > 0)

Note that the length of the description cannot be derived from header.size because the attribute is padded for alignment, so str{n}len() or similar must be used to determine it.


ULTRA_ATTRIBUTE_COMMAND_LINE

This attribute forwards the configuration file command line to the kernel if one was provided and has the following structure:

struct ultra_command_line_attribute {
    struct ultra_attribute_header header;
    char text[];
};
  • header - standard attribute header
  • text - null terminated ASCII command line, as specified in the configuration file

Length of the command line is not artificially limited in any way but must fit in the header.size field, which is 32 bits wide. Note that header.size is not necessarily equal to the length of the command line because of alignment reasons.


ULTRA_ATTRIBUTE_FRAMEBUFFER_INFO

This attribute provides framebuffer information if one was requested and has the following structure:

struct ultra_framebuffer_attribute {
    struct ultra_attribute_header header;
    struct ultra_framebuffer fb;
};
  • header - standard attribute header
  • fb - describes the allocated framebuffer and has the following structure:
struct ultra_framebuffer {
    uint32_t width;
    uint32_t height;
    uint32_t pitch;
    uint16_t bpp;
    uint16_t format;
    uint64_t physical_address;
};
  • width - the number of visible pixels per row in the framebuffer
  • height - the number of rows of visible pixels in the framebuffer
  • pitch - the number of bytes that each row takes in memory
  • bpp - width in bits of each visible pixel
  • format - the format of the allocated framebuffer, and is one of the following values:
#define ULTRA_FB_FORMAT_INVALID  0
#define ULTRA_FB_FORMAT_RGB888   1
#define ULTRA_FB_FORMAT_BGR888   2
#define ULTRA_FB_FORMAT_RGBX8888 3
#define ULTRA_FB_FORMAT_XRGB8888 4

Framebuffer format types are very similar to DRM_FORMAT_* and are defined as follows:

ULTRA_FB_FORMAT_INVALID

Reserved. If encountered, must be considered a fatal error.

ULTRA_FB_FORMAT_RGB888

Standard RGB format.

Layout of each pixel (low to high memory address):

bits 0 ... 8 8 ... 16 16 ... 24
color BLUE GREEN RED

bpp must be set to 24.

ULTRA_FB_FORMAT_BGR888

Standard BGR format.

Layout of each pixel (low to high memory address):

bits 0 ... 8 8 ... 16 16 ... 24
color RED GREEN BLUE

bpp must be set to 24.

ULTRA_FB_FORMAT_RGBX8888

Standard RGBX format padded to 32 bits.

Layout of each pixel (low to high memory address):

bits 0 ... 8 8 ... 16 16 ... 24 24 ... 32
color UNUSED BLUE GREEN RED

bpp must be set to 32.

ULTRA_FB_FORMAT_XRGB8888

Standard XRGB format padded to 32 bits.

Layout of each pixel (low to high memory address):

bits 0 ... 8 8 ... 16 16 ... 24 24 ... 32
color BLUE GREEN RED UNUSED

bpp must be set to 32.

  • physical_address - address of the allocated framebuffer

ULTRA_ATTRIBUTE_APM_INFO

This attribute provides APM information if one was requested (via setup-apm) and has the following structure:

struct ultra_apm_attribute {
    struct ultra_attribute_header header;
    struct ultra_apm_info info;
};
  • header - standard attribute header
  • info - describes the established APM interface and the general APM info, and has the following structure:
struct ultra_apm_info {
    uint16_t version;
    uint16_t flags;

    uint16_t pm_code_segment;
    uint16_t pm_code_segment_length;
    uint32_t pm_offset;

    uint16_t rm_code_segment;
    uint16_t rm_code_segment_length;

    uint16_t data_segment;
    uint16_t data_segment_length;
};
  • version - the APM version number, as reported by the installation check call
  • flags - the APM flags, as reported by the installation check call
  • pm_code_segment - the 32-bit code segment
  • pm_code_segment_length - the length of the 32-bit code segment
  • pm_offset - offset to the entry point into the APM BIOS
  • rm_code_segment - the 16-bit code segment
  • rm_code_segment_length - the length of the 16-bit code segment
  • data_segment - the 32-bit data segment
  • data_segment_length - the length of the data segment

For more information, consult the APM BIOS Specification.


ULTRA_ATTRIBUTE_UEFI_INFO

This attribute provides the UEFI system table and firmware memory map to the kernel, and is only generated on the UEFI platform when requested via pass-uefi-info. It has the following structure:

struct ultra_uefi_info_attribute {
    struct ultra_attribute_header header;

    uint64_t system_table_address;

    uint32_t descriptor_size;
    uint32_t descriptor_version;

    uint32_t firmware_width;
    uint32_t reserved;

    uint8_t memory_descriptors[];
};
  • header - standard attribute header
  • system_table_address - physical address of the EFI_SYSTEM_TABLE
  • descriptor_size - size in bytes of a single EFI_MEMORY_DESCRIPTOR in memory_descriptors, as reported by GetMemoryMap(). This may be larger than sizeof(EFI_MEMORY_DESCRIPTOR), so always use this value (never sizeof) to stride the array.
  • descriptor_version - version of the EFI_MEMORY_DESCRIPTOR structure, as reported by GetMemoryMap()
  • firmware_width - the native width of the UEFI firmware in bits, either 32 or 64. A kernel loaded at a different width than the firmware (e.g. a 64-bit kernel on 32-bit UEFI) must operate in mixed mode to call runtime services.
  • reserved - for use by future versions of the protocol, must be ignored
  • memory_descriptors - the raw firmware memory map, a contiguous array of EFI_MEMORY_DESCRIPTOR entries exactly as returned by GetMemoryMap(), each descriptor_size bytes apart

The number of memory descriptors can be calculated using the following C macro:

#define ULTRA_UEFI_INFO_MEM_DESC_COUNT(info) ((((info).header.size) - sizeof(struct ultra_uefi_info_attribute)) / (info).descriptor_size)

Note that, unlike the other count macros, this one takes the whole attribute (not just its header) as it also needs descriptor_size.

Boot services have already been exited by the time the kernel receives control, so the system table may only be used for runtime services (and the configuration tables reachable through it). Any boot-services-only field (BootServices, the console handles/protocols, etc.) is invalid.

The memory map is the exact one that was passed to ExitBootServices(), so it is suitable for a subsequent SetVirtualAddressMap() call.


Implementation

All structure definitions and macros can be found in the ultra_protocol.h file compatible with both C and C++.

The current reference implementation of the protocol in C can be found at https://github.com/UltraOS/Hyper

About

A generic boot protocol that doesn't require any special in-kernel sections.

Resources

License

Stars

11 stars

Watchers

3 watching

Forks

Releases

No releases published

Packages

 
 
 

Contributors

Languages