docs: Initial Limine protocol draft

This commit is contained in:
mintsuki 2022-03-22 05:43:38 +01:00
parent b2238c152e
commit 2fa2fb2220

137
PROTOCOL.md Normal file
View File

@ -0,0 +1,137 @@
# The Limine Boot Protocol
The Limine boot protocol is a modern, minimal, fast, and extensible boot
protocol, with a focus on backwards and forwards compatibility,
created from the experienced gained by working on the
[stivale boot protocols](https://github.com/stivale).
This file serves as the official centralised collection of features that
the Limine boot protocol is composed of. Other bootloaders may support extra
unofficial features, but it is strongly recommended to avoid fragmentation
and submit new features by opening a pull request to this repository.
## Features
The protocol is centered around the concept of request/response - collectively
named "features" - where the kernel requests some action or information from
the bootloader, and the bootloader responds accordingly, if it is capable of
doing so.
In C terms, a feature is composed of 2 structure: the request, and the response.
A request has 3 mandatory members at the beginning of the structure:
```c
struct limine_example_request {
uint64_t id[4];
uint64_t revision;
struct limine_example_response *response;
... optional members follow ...
};
```
* `id` - The ID of the request. This is an 8-byte aligned magic number that the
bootloader will scan for inside the executable file to find requests. Requests
may be located anywhere inside the executable as long as they are 8-byte
aligned. There may only be 1 of the same request. The bootloader will refuse
to boot an executable with multiple of the same request IDs.
* `revision` - The revision of the request that the kernel provides. This is
bumped whenever new members or functionality are added to the request structure.
Bootloaders process requests in a backwards compatible manner, *always*. This
means that if the bootloader does not support the revision of the request,
it will process the request as if were the highest revision that the bootloader
supports.
* `response` - This field is filled in by the bootloader at load time, with a
pointer to the response structure, if the request was successfully processed.
If the request is unsupported or was not successfully processed, this field
is *left untouched*, meaning that if it was set to `NULL`, it will stay that
way.
A response has only 1 mandatory member at the beginning of the structure:
```c
struct limine_example_response {
uint64_t revision;
... optional members follow ...
};
```
* `revision` - Like for requests, bootloaders will instead mark responses with a
revision number. This revision is not coupled between requests and responses,
as they are bumped individually when new members are added or functionality is
changed. Bootloaders will set the revision to the one they provide, and this is
*always backwards compatible*, meaning higher revisions support all that lower
revisions do.
This is all there is to features. For a list of official Limine features, read
the "Feature List" section below.
## Executable memory layout
The protocol mandates kernels to load themselves at or above
`0xffffffff80000000`. Lower half kernels are *not supported*.
At handoff, the kernel will be properly loaded and mapped with appropriate
MMU permissions at the requested virtual memory address (provided it is at
or above `0xffffffff80000000`).
No specific physical memory placement is guaranteed. In order to determine
where the kernel is loaded in physical memory, see the Kernel Address feature
below.
Alongside the loaded kernel, the bootloader will set up memory mappings as such:
```
Base Physical Address - Size -> Virtual address
0x0000000000001000 - 4 GiB plus any additional memory map entry -> 0x0000000000001000
0x0000000000000000 - 4 GiB plus any additional memory map entry -> HHDM start
```
Where HHDM start is returned by the Higher Half Direct Map feature (see below).
These mappings are supervisor, read, write, execute (-rwx).
The bootloader page tables are in bootloader-reclaimable memory (see Memory Map
feature below), and their specific layout is undefined as long as they provide
the above memory mappings.
If the kernel is a position independent executable, the bootloader is free to
relocate it as it sees fit, potentially performing KASLR (as specified by the
config).
## Entry machine state
### x86_64
`rip` will be the entry point as defined as part of the executable file format,
unless the an Entry Point feature is requested (see below), in which case,
the value of `rip` is going to be taken from there.
At entry all segment registers are loaded as 64 bit code/data segments, limits
and bases are ignored since this is 64-bit mode.
The GDT register is loaded to point to a GDT, in bootloader-reserved memory,
with at least the following entries, starting at offset 0:
- Null descriptor
- 16-bit code descriptor. Base = `0`, limit = `0xffff`. Readable.
- 16-bit data descriptor. Base = `0`, limit = `0xffff`. Writable.
- 32-bit code descriptor. Base = `0`, limit = `0xffffffff`. Readable.
- 32-bit data descriptor. Base = `0`, limit = `0xffffffff`. Writable.
- 64-bit code descriptor. Base and limit irrelevant. Readable.
- 64-bit data descriptor. Base and limit irrelevant. Writable.
The IDT is in an undefined state. Kernel must load its own.
IF flag, VM flag, and direction flag are cleared on entry. Other flags undefined.
PG is enabled (`cr0`), PE is enabled (`cr0`), PAE is enabled (`cr4`),
LME is enabled (`EFER`).
If 5-level paging is requested and available, then 5-level paging is enabled
(LA57 bit in `cr4`).
The NX bit will be enabled (NX bit in `EFER`).
The A20 gate is opened.
Legacy PIC and IO APIC IRQs are all masked.
If booted by EFI/UEFI, boot services are exited.
`rsp` is set to point to a stack, in bootloader-reserved memory, which is
at least 8KiB (8192 bytes) in size. An invalid return address of 0 is pushed
to the stack before jumping to the kernel.
All other general purpose registers are set to 0.