:: commit 3c6ead244061b5c42e562ce96fd5f567be752bc7

mintsuki <mintsuki@protonmail.com> — 2024-07-18 18:22

parents: 4a47c98d3e

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

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 `<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.
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.
+
+`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` macro is provided by all architectures to select
-the default paging mode (see below).
+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 (;;);
tab: 248 wrap: offon