From 3c6ead244061b5c42e562ce96fd5f567be752bc7 Mon Sep 17 00:00:00 2001 From: mintsuki Date: Thu, 18 Jul 2024 20:22:25 +0200 Subject: [PATCH] protos/limine: Specify and implement paging mode request revision 1 --- CONFIG.md | 3 +- PROTOCOL.md | 61 ++++++++++++++++++---------- common/menu.c | 2 + common/mm/vmm.h | 3 ++ common/protos/limine.c | 92 ++++++++++++++++++++++++++++++++++++++++-- limine.h | 7 +++- test/limine.c | 8 ++-- 7 files changed, 144 insertions(+), 32 deletions(-) diff --git a/CONFIG.md b/CONFIG.md index c4f22c23..1ed6a49e 100644 --- a/CONFIG.md +++ b/CONFIG.md @@ -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 `xx`. If the resolution is not available, Limine will pick another one automatically. Omitting `` 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. diff --git a/PROTOCOL.md b/PROTOCOL.md index f7cb4c26..e497ad62 100644 --- a/PROTOCOL.md +++ b/PROTOCOL.md @@ -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: diff --git a/common/menu.c b/common/menu.c index 934939f8..f0dd57d3 100644 --- a/common/menu.c +++ b/common/menu.c @@ -106,7 +106,9 @@ static const char *VALID_KEYS[] = { "RESOLUTION", "TEXTMODE", "KASLR", + "PAGING_MODE", "MAX_PAGING_MODE", + "MIN_PAGING_MODE", "DRIVE", "PARTITION", "MBR_ID", diff --git a/common/mm/vmm.h b/common/mm/vmm.h index 9c3eb2c2..6d27e8dc 100644 --- a/common/mm/vmm.h +++ b/common/mm/vmm.h @@ -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); diff --git a/common/protos/limine.c b/common/protos/limine.c index 2dc3ffc7..afc9846b 100644 --- a/common/protos/limine.c +++ b/common/protos/limine.c @@ -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; diff --git a/limine.h b/limine.h index 7d93730c..63e8422f 100644 --- a/limine.h +++ b/limine.h @@ -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 */ diff --git a/test/limine.c b/test/limine.c index 22d48b4e..38e6e71e 100644 --- a/test/limine.c +++ b/test/limine.c @@ -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 (;;);