protos/limine: Specify and implement paging mode request revision 1

This commit is contained in:
mintsuki 2024-07-18 20:22:25 +02:00
parent 776e46cf44
commit 0a92f64554
7 changed files with 144 additions and 32 deletions

View File

@ -123,9 +123,10 @@ Editor control options:
* `MODULE_CMDLINE` - A command line to be passed to a module. This key can also be specified multiple times. It applies to the module described by the last module key assigned.
* `RESOLUTION` - The resolution to be used. This setting takes the form of `<width>x<height>x<bpp>`. If the resolution is not available, Limine will pick another one automatically. Omitting `<bpp>` will default to 32.
* `KASLR` - For relocatable kernels, if set to `no`, disable kernel address space layout randomisation. KASLR is enabled by default.
* `MAX_PAGING_MODE` - Limit the maximum paging mode to one of the following:
* `MAX_PAGING_MODE`, `MIN_PAGING_MODE` - Limit the maximum and minimum paging modes to one of the following:
- x86-64 and aarch64: `4level`, `5level`.
- riscv64: `sv39`, `sv48`, `sv57`.
* `PAGING_MODE` - Equivalent to setting both `MAX_PAGING_MODE` and `MIN_PAGING_MODE` to the same value.
* multiboot1 and multiboot2 protocols:
* `KERNEL_PATH` - The URI path of the kernel.

View File

@ -574,71 +574,88 @@ struct limine_paging_mode_request {
uint64_t revision;
struct limine_paging_mode_response *response;
uint64_t mode;
uint64_t flags;
/* Request revision 1 and above */
uint64_t max_mode;
uint64_t min_mode;
};
```
Both the `mode` and `flags` fields are architecture-specific.
The `mode`, `max_mode`, and `min_mode` fields take architecture-specific values
as described below.
The `LIMINE_PAGING_MODE_DEFAULT` macro is provided by all architectures to select
the default paging mode (see below).
`mode` is the preferred paging mode by the OS; the bootloader should always aim
to pick this mode unless unavailable or overridden by the user in the bootloader's
configuration file.
`max_mode` is the highest paging mode in numerical order that the OS supports. The
bootloader will refuse to boot the OS if no paging modes of this type or lower
(but equal or greater than `min_mode`) are available.
`min_mode` is the lowest paging mode in numerical order that the OS supports. The
bootloader will refuse to boot the OS if no paging modes of this type or greater
(but equal or lower than `max_mode`) are available.
The `LIMINE_PAGING_MODE_DEFAULT`, `LIMINE_PAGING_MODE_MAX`, and `LIMINE_PAGING_MODE_MIN`
macros are provided by all architectures, where the latter 2 define the maximum and
minimum paging modes supported by the current Limine protocol specification.
If no Paging Mode Request is provided, the values of `mode`, `max_mode`, and `min_mode`
that the bootloader assumes are `LIMINE_PAGING_MODE_DEFAULT`, `LIMINE_PAGING_MODE_DEFAULT`,
and `LIMINE_PAGING_MODE_MIN`, respectively.
If request revision 0 is used, the values of `max_mode` and `min_mode` that the
bootloader assumes are the value of `mode` and `LIMINE_PAGING_MODE_MIN`,
respectively.
Response:
```c
struct limine_paging_mode_response {
uint64_t revision;
uint64_t mode;
uint64_t flags;
};
```
The response indicates which paging mode was actually enabled by the bootloader.
Kernels must be prepared to handle the case where the requested paging mode is
not supported by the hardware.
Kernels must be prepared to handle cases where the provided paging mode is
not supported.
#### x86-64
Values for `mode`:
Values for `mode`, `max_mode`, and `min_mode`:
```c
#define LIMINE_PAGING_MODE_X86_64_4LVL 0
#define LIMINE_PAGING_MODE_X86_64_5LVL 1
#define LIMINE_PAGING_MODE_DEFAULT LIMINE_PAGING_MODE_X86_64_4LVL
#define LIMINE_PAGING_MODE_MIN LIMINE_PAGING_MODE_X86_64_4LVL
#define LIMINE_PAGING_MODE_MAX LIMINE_PAGING_MODE_X86_64_5LVL
```
No `flags` are currently defined.
The default mode (when this request is not provided) is `LIMINE_PAGING_MODE_X86_64_4LVL`.
#### aarch64
Values for `mode`:
Values for `mode`, `max_mode`, and `min_mode`:
```c
#define LIMINE_PAGING_MODE_AARCH64_4LVL 0
#define LIMINE_PAGING_MODE_AARCH64_5LVL 1
#define LIMINE_PAGING_MODE_DEFAULT LIMINE_PAGING_MODE_AARCH64_4LVL
#define LIMINE_PAGING_MODE_MIN LIMINE_PAGING_MODE_AARCH64_4LVL
#define LIMINE_PAGING_MODE_MAX LIMINE_PAGING_MODE_AARCH64_5LVL
```
No `flags` are currently defined.
The default mode (when this request is not provided) is `LIMINE_PAGING_MODE_AARCH64_4LVL`.
#### riscv64
Values for `mode`:
Values for `mode`, `max_mode`, and `min_mode`:
```c
#define LIMINE_PAGING_MODE_RISCV_SV39 0
#define LIMINE_PAGING_MODE_RISCV_SV48 1
#define LIMINE_PAGING_MODE_RISCV_SV57 2
#define LIMINE_PAGING_MODE_DEFAULT LIMINE_PAGING_MODE_RISCV_SV48
#define LIMINE_PAGING_MODE_MIN LIMINE_PAGING_MODE_RISCV_SV39
#define LIMINE_PAGING_MODE_MAX LIMINE_PAGING_MODE_RISCV_SV57
```
No `flags` are currently defined.
The default mode (when this request is not provided) is `LIMINE_PAGING_MODE_RISCV_SV48`.
### SMP (multiprocessor) Feature
ID:

View File

@ -106,7 +106,9 @@ static const char *VALID_KEYS[] = {
"RESOLUTION",
"TEXTMODE",
"KASLR",
"PAGING_MODE",
"MAX_PAGING_MODE",
"MIN_PAGING_MODE",
"DRIVE",
"PARTITION",
"MBR_ID",

View File

@ -15,6 +15,7 @@
#define PAGING_MODE_X86_64_4LVL 0
#define PAGING_MODE_X86_64_5LVL 1
#define PAGING_MODE_MIN PAGING_MODE_X86_64_4LVL
#define PAGING_MODE_MAX PAGING_MODE_X86_64_5LVL
#define paging_mode_va_bits(mode) ((mode) ? 57 : 48)
@ -54,6 +55,7 @@ void map_page(pagemap_t pagemap, uint64_t virt_addr, uint64_t phys_addr, uint64_
#define PAGING_MODE_AARCH64_4LVL 0
#define PAGING_MODE_AARCH64_5LVL 1
#define PAGING_MODE_MIN PAGING_MODE_AARCH64_4LVL
#define PAGING_MODE_MAX PAGING_MODE_AARCH64_5LVL
#define paging_mode_va_bits(mode) ((mode) ? 52 : 48)
@ -95,6 +97,7 @@ void map_page(pagemap_t pagemap, uint64_t virt_addr, uint64_t phys_addr, uint64_
#define PAGING_MODE_RISCV_SV48 9
#define PAGING_MODE_RISCV_SV57 10
#define PAGING_MODE_MIN PAGING_MODE_RISCV_SV39
#define PAGING_MODE_MAX PAGING_MODE_RISCV_SV57
int paging_mode_va_bits(int paging_mode);

View File

@ -454,7 +454,7 @@ noreturn void limine_load(char *config, char *cmdline) {
printv("limine: Requests count: %u\n", requests_count);
// Paging Mode
int paging_mode, max_supported_paging_mode;
int paging_mode, max_supported_paging_mode, min_supported_paging_mode;
#if defined (__x86_64__) || defined (__i386__)
max_supported_paging_mode = PAGING_MODE_X86_64_4LVL;
@ -462,18 +462,28 @@ noreturn void limine_load(char *config, char *cmdline) {
printv("limine: CPU has 5-level paging support\n");
max_supported_paging_mode = PAGING_MODE_X86_64_5LVL;
}
min_supported_paging_mode = PAGING_MODE_X86_64_4LVL;
#elif defined (__aarch64__)
max_supported_paging_mode = PAGING_MODE_AARCH64_4LVL;
min_supported_paging_mode = PAGING_MODE_AARCH64_4LVL;
// TODO(qookie): aarch64 also has optional 5 level paging when using 4K pages
#elif defined (__riscv64)
max_supported_paging_mode = vmm_max_paging_mode();
min_supported_paging_mode = PAGING_MODE_RISCV_SV39;
#else
#error Unknown architecture
#endif
char *user_paging_mode_s = config_get_value(config, 0, "PAGING_MODE");
int user_max_paging_mode = PAGING_MODE_MAX;
char *user_max_paging_mode_s = config_get_value(config, 0, "MAX_PAGING_MODE");
char *user_max_paging_mode_s;
if (user_paging_mode_s != NULL) {
user_max_paging_mode_s = user_paging_mode_s;
} else {
user_max_paging_mode_s = config_get_value(config, 0, "MAX_PAGING_MODE");
}
if (user_max_paging_mode_s != NULL) {
#if defined (__x86_64__) || defined (__i386__)
if (strcasecmp(user_max_paging_mode_s, "4level") == 0) {
@ -501,16 +511,64 @@ noreturn void limine_load(char *config, char *cmdline) {
}
}
int user_min_paging_mode = PAGING_MODE_MIN;
char *user_min_paging_mode_s;
if (user_paging_mode_s != NULL) {
user_min_paging_mode_s = user_paging_mode_s;
} else {
user_min_paging_mode_s = config_get_value(config, 0, "MIN_PAGING_MODE");
}
if (user_min_paging_mode_s != NULL) {
#if defined (__x86_64__) || defined (__i386__)
if (strcasecmp(user_min_paging_mode_s, "4level") == 0) {
user_min_paging_mode = PAGING_MODE_X86_64_4LVL;
} else if (strcasecmp(user_min_paging_mode_s, "5level") == 0) {
user_min_paging_mode = PAGING_MODE_X86_64_5LVL;
}
#elif defined (__aarch64__)
if (strcasecmp(user_min_paging_mode_s, "4level") == 0) {
user_min_paging_mode = PAGING_MODE_AARCH64_4LVL;
} else if (strcasecmp(user_min_paging_mode_s, "5level") == 0) {
user_min_paging_mode = PAGING_MODE_AARCH64_5LVL;
}
#elif defined (__riscv64)
if (strcasecmp(user_min_paging_mode_s, "sv39") == 0) {
user_min_paging_mode = PAGING_MODE_RISCV_SV39;
} else if (strcasecmp(user_min_paging_mode_s, "sv48") == 0) {
user_min_paging_mode = PAGING_MODE_RISCV_SV48;
} else if (strcasecmp(user_min_paging_mode_s, "sv57") == 0) {
user_min_paging_mode = PAGING_MODE_RISCV_SV57;
}
#endif
else {
panic(true, "limine: Invalid MIN_PAGING_MODE: `%s`", user_min_paging_mode_s);
}
}
if (user_max_paging_mode < user_min_paging_mode) {
panic(true, "limine: MAX_PAGING_MODE is lower than MIN_PAGING_MODE");
}
if (user_max_paging_mode < max_supported_paging_mode) {
if (user_max_paging_mode < min_supported_paging_mode) {
panic(true, "limine: User set MAX_PAGING_MODE less than minimum supported paging mode");
}
max_supported_paging_mode = user_max_paging_mode;
}
if (user_min_paging_mode > min_supported_paging_mode) {
if (user_min_paging_mode > max_supported_paging_mode) {
panic(true, "limine: User set MIN_PAGING_MODE greater than maximum supported paging mode");
}
min_supported_paging_mode = user_min_paging_mode;
}
#if defined (__x86_64__) || defined (__i386__)
paging_mode = PAGING_MODE_X86_64_4LVL;
#elif defined (__riscv64)
paging_mode = max_supported_paging_mode >= PAGING_MODE_RISCV_SV48 ? PAGING_MODE_RISCV_SV48 : PAGING_MODE_RISCV_SV39;
#else
paging_mode = max_supported_paging_mode;
#elif defined (__aarch64__)
paging_mode = PAGING_MODE_AARCH64_4LVL;
#endif
#if defined (__riscv64)
@ -530,9 +588,35 @@ FEAT_START
uint64_t target_mode = pm_request->mode;
paging_mode = paging_mode_limine_to_vmm(target_mode);
int kern_min_mode = PAGING_MODE_MIN, kern_max_mode = paging_mode;
if (pm_request->revision >= 1) {
kern_min_mode = (int)paging_mode_limine_to_vmm(pm_request->min_mode);
kern_max_mode = (int)paging_mode_limine_to_vmm(pm_request->max_mode);
}
if (paging_mode > max_supported_paging_mode) {
paging_mode = max_supported_paging_mode;
}
if (paging_mode < min_supported_paging_mode) {
paging_mode = min_supported_paging_mode;
}
if (kern_max_mode < kern_min_mode) {
panic(true, "limine: Kernel's paging max_mode lower than min_mode");
}
if (paging_mode > kern_max_mode) {
if (kern_max_mode < min_supported_paging_mode) {
panic(true, "limine: Kernel's maximum supported paging mode lower than minimum allowable paging mode");
}
paging_mode = kern_max_mode;
}
if (paging_mode < kern_min_mode) {
if (kern_min_mode > max_supported_paging_mode) {
panic(true, "limine: Kernel's minimum supported paging mode higher than maximum allowable paging mode");
}
paging_mode = kern_min_mode;
}
set_paging_mode(paging_mode, kaslr);
paging_mode_set = true;

View File

@ -273,17 +273,20 @@ LIMINE_DEPRECATED_IGNORE_END
#define LIMINE_PAGING_MODE_X86_64_4LVL 0
#define LIMINE_PAGING_MODE_X86_64_5LVL 1
#define LIMINE_PAGING_MODE_MAX LIMINE_PAGING_MODE_X86_64_5LVL
#define LIMINE_PAGING_MODE_MIN LIMINE_PAGING_MODE_X86_64_4LVL
#define LIMINE_PAGING_MODE_DEFAULT LIMINE_PAGING_MODE_X86_64_4LVL
#elif defined (__aarch64__)
#define LIMINE_PAGING_MODE_AARCH64_4LVL 0
#define LIMINE_PAGING_MODE_AARCH64_5LVL 1
#define LIMINE_PAGING_MODE_MAX LIMINE_PAGING_MODE_AARCH64_5LVL
#define LIMINE_PAGING_MODE_MIN LIMINE_PAGING_MODE_AARCH64_4LVL
#define LIMINE_PAGING_MODE_DEFAULT LIMINE_PAGING_MODE_AARCH64_4LVL
#elif defined (__riscv) && (__riscv_xlen == 64)
#define LIMINE_PAGING_MODE_RISCV_SV39 0
#define LIMINE_PAGING_MODE_RISCV_SV48 1
#define LIMINE_PAGING_MODE_RISCV_SV57 2
#define LIMINE_PAGING_MODE_MAX LIMINE_PAGING_MODE_RISCV_SV57
#define LIMINE_PAGING_MODE_MIN LIMINE_PAGING_MODE_RISCV_SV39
#define LIMINE_PAGING_MODE_DEFAULT LIMINE_PAGING_MODE_RISCV_SV48
#else
#error Unknown architecture
@ -292,7 +295,6 @@ LIMINE_DEPRECATED_IGNORE_END
struct limine_paging_mode_response {
uint64_t revision;
uint64_t mode;
uint64_t flags;
};
struct limine_paging_mode_request {
@ -300,7 +302,8 @@ struct limine_paging_mode_request {
uint64_t revision;
LIMINE_PTR(struct limine_paging_mode_response *) response;
uint64_t mode;
uint64_t flags;
uint64_t max_mode;
uint64_t min_mode;
};
/* 5-level paging */

View File

@ -139,11 +139,14 @@ static volatile struct limine_dtb_request _dtb_request = {
__attribute__((section(".limine_requests")))
static volatile struct limine_paging_mode_request _pm_request = {
.id = LIMINE_PAGING_MODE_REQUEST,
.revision = 0, .response = NULL,
.revision = 1, .response = NULL,
#if defined (__x86_64__)
.mode = LIMINE_PAGING_MODE_X86_64_5LVL,
#else
.mode = LIMINE_PAGING_MODE_DEFAULT,
#endif
.flags = 0,
.max_mode = LIMINE_PAGING_MODE_MAX,
.min_mode = LIMINE_PAGING_MODE_MIN
};
__attribute__((used, section(".limine_requests_end_marker")))
@ -518,7 +521,6 @@ FEAT_START
struct limine_paging_mode_response *pm_response = _pm_request.response;
e9_printf("Paging mode feature, revision %d", pm_response->revision);
e9_printf(" mode: %d", pm_response->mode);
e9_printf(" flags: %x", pm_response->flags);
FEAT_END
for (;;);