console: Introduce Limine console
diff --git a/stage23/console.c b/stage23/console.c
new file mode 100644
index 00000000..ee4556de
--- /dev/null
+++ b/stage23/console.c
@@ -0,0 +1,50 @@
+#include <stddef.h>
+#include <stdint.h>
+#include <console.h>
+#include <mm/pmm.h>
+#include <lib/print.h>
+#include <lib/readline.h>
+#include <lib/libc.h>
+#include <lib/blib.h>
+#include <lib/term.h>
+
+static void console_help(void) {
+ print(
+ "Available commands:\n"
+ "exit -- Return to boot menu.\n"
+ "version -- Print version.\n"
+ "copyright -- Print copyright.\n"
+ "license -- Print license.\n"
+ "help -- Print this help message.\n"
+ );
+}
+
+void console(void) {
+ print("Welcome to the Limine console.\nType 'help' for more information.\n\n");
+
+ static char *prompt = NULL;
+ if (prompt == NULL) {
+ prompt = ext_mem_alloc(256);
+ }
+
+ for (;;) {
+ print(">>> ");
+ readline("", prompt, 256);
+
+ if (strcmp(prompt, "help") == 0) {
+ console_help();
+ } else if (strcmp(prompt, "exit") == 0) {
+ reset_term();
+ return;
+ } else if (strcmp(prompt, "version") == 0) {
+ print(LIMINE_VERSION "\n");
+ } else if (strcmp(prompt, "copyright") == 0) {
+ print(LIMINE_COPYRIGHT "\n");
+ print("Limine is distributed under the terms of the BSD-2-Clause license.\n");
+ } else if (strcmp(prompt, "license") == 0) {
+ print("%s", bsd_2_clause);
+ } else if (*prompt != 0) {
+ print("Invalid command: `%s`.\n", prompt);
+ }
+ }
+}
diff --git a/stage23/console.h b/stage23/console.h
new file mode 100644
index 00000000..4ea97c71
--- /dev/null
+++ b/stage23/console.h
@@ -0,0 +1,6 @@
+#ifndef __CONSOLE_H__
+#define __CONSOLE_H__
+
+void console(void);
+
+#endif
diff --git a/stage23/entry.s2.c b/stage23/entry.s2.c
index 29d0ae1d..663ea65f 100644
--- a/stage23/entry.s2.c
+++ b/stage23/entry.s2.c
@@ -81,8 +81,6 @@ void entry(uint8_t boot_drive, int boot_from) {
term_textmode();
- copyright_notice();
-
init_idt();
disk_create_index();
diff --git a/stage23/entry.s3.c b/stage23/entry.s3.c
index 26cae860..a5e50e6c 100644
--- a/stage23/entry.s3.c
+++ b/stage23/entry.s3.c
@@ -58,16 +58,13 @@ void uefi_entry(EFI_HANDLE ImageHandle, EFI_SYSTEM_TABLE *SystemTable) {
status = gBS->SetWatchdogTimer(0, 0x10000, 0, NULL);
if (status) {
+ term_vbe(0, 0);
+ early_term = true;
print("WARNING: Failed to disable watchdog timer!\n");
}
init_memmap();
- term_vbe(0, 0);
- early_term = true;
-
- copyright_notice();
-
disk_create_index();
boot_volume = NULL;
@@ -75,6 +72,9 @@ void uefi_entry(EFI_HANDLE ImageHandle, EFI_SYSTEM_TABLE *SystemTable) {
EFI_HANDLE current_handle = ImageHandle;
for (;;) {
if (current_handle == NULL) {
+ term_vbe(0, 0);
+ early_term = true;
+
print("WARNING: Could not meaningfully match the boot device handle with a volume.\n");
print(" Using the first volume containing a Limine configuration!\n");
@@ -111,6 +111,8 @@ void uefi_entry(EFI_HANDLE ImageHandle, EFI_SYSTEM_TABLE *SystemTable) {
(void **)&loaded_image);
if (status) {
+ term_vbe(0, 0);
+ early_term = true;
panic("HandleProtocol failure (%x)", status);
}
diff --git a/stage23/lib/blib.c b/stage23/lib/blib.c
index 12f2f7fb..32224ebd 100644
--- a/stage23/lib/blib.c
+++ b/stage23/lib/blib.c
@@ -268,3 +268,25 @@ fail:
}
#endif
+
+const char bsd_2_clause[] =
+"Redistribution and use in source and binary forms, with or without\n"
+"modification, are permitted provided that the following conditions are met:\n"
+"\n"
+"1. Redistributions of source code must retain the above copyright notice, this\n"
+" list of conditions and the following disclaimer.\n"
+"\n"
+"2. Redistributions in binary form must reproduce the above copyright notice,\n"
+" this list of conditions and the following disclaimer in the documentation\n"
+" and/or other materials provided with the distribution.\n"
+"\n"
+"THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS \"AS IS\"\n"
+"AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE\n"
+"IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE\n"
+"DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE\n"
+"FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL\n"
+"DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR\n"
+"SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER\n"
+"CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,\n"
+"OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE\n"
+"OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.\n";
diff --git a/stage23/lib/blib.h b/stage23/lib/blib.h
index e0caa157..1a4ca703 100644
--- a/stage23/lib/blib.h
+++ b/stage23/lib/blib.h
@@ -24,7 +24,7 @@ extern bool efi_boot_services_exited;
bool efi_exit_boot_services(void);
#endif
-void copyright_notice(void);
+extern const char bsd_2_clause[];
extern struct volume *boot_volume;
diff --git a/stage23/lib/blib.s2.c b/stage23/lib/blib.s2.c
index 19098c5f..9513fb13 100644
--- a/stage23/lib/blib.s2.c
+++ b/stage23/lib/blib.s2.c
@@ -5,12 +5,6 @@
bool verbose = true;
-void copyright_notice(void) {
- print("Limine " LIMINE_VERSION "\n");
- print(LIMINE_COPYRIGHT "\n");
- print("This bootloader is distributed under the terms of the BSD-2-Clause license.\n\n");
-}
-
uint8_t bcd_to_int(uint8_t val) {
return (val & 0x0f) + ((val & 0xf0) >> 4) * 10;
}
diff --git a/stage23/lib/gterm.h b/stage23/lib/gterm.h
index a4e16912..65a39282 100644
--- a/stage23/lib/gterm.h
+++ b/stage23/lib/gterm.h
@@ -7,7 +7,6 @@
#include <drivers/vbe.h>
extern struct fb_info fbinfo;
-extern bool term_autoflush;
bool gterm_init(size_t *_rows, size_t *_cols, size_t width, size_t height);
void gterm_deinit(void);
diff --git a/stage23/lib/print.s2.c b/stage23/lib/print.s2.c
index 62d4036e..52bde60d 100644
--- a/stage23/lib/print.s2.c
+++ b/stage23/lib/print.s2.c
@@ -9,7 +9,7 @@
static const char *base_digits = "0123456789abcdef";
-#define PRINT_BUF_MAX 512
+#define PRINT_BUF_MAX 4096
static void prn_str(char *print_buf, size_t *print_buf_i, const char *string) {
size_t i;
diff --git a/stage23/lib/readline.c b/stage23/lib/readline.c
index fdf8e67a..2979a6ab 100644
--- a/stage23/lib/readline.c
+++ b/stage23/lib/readline.c
@@ -232,3 +232,107 @@ again:
return ret;
}
#endif
+
+static void reprint_string(int x, int y, const char *s) {
+ size_t orig_x, orig_y;
+ disable_cursor();
+ get_cursor_pos(&orig_x, &orig_y);
+ set_cursor_pos(x, y);
+ term_write((uintptr_t)s, strlen(s));
+ set_cursor_pos(orig_x, orig_y);
+ enable_cursor();
+}
+
+static void cursor_back(void) {
+ size_t x, y;
+ get_cursor_pos(&x, &y);
+ if (x) {
+ x--;
+ } else if (y) {
+ y--;
+ x = term_cols - 1;
+ }
+ set_cursor_pos(x, y);
+}
+
+static void cursor_fwd(void) {
+ size_t x, y;
+ get_cursor_pos(&x, &y);
+ if (x < term_cols - 1) {
+ x++;
+ } else if (y < term_rows - 1) {
+ y++;
+ x = 0;
+ }
+ set_cursor_pos(x, y);
+}
+
+void readline(const char *orig_str, char *buf, size_t limit) {
+ bool prev_autoflush = term_autoflush;
+ term_autoflush = false;
+
+ size_t orig_str_len = strlen(orig_str);
+ memmove(buf, orig_str, orig_str_len);
+ buf[orig_str_len] = 0;
+
+ size_t orig_x, orig_y;
+ get_cursor_pos(&orig_x, &orig_y);
+
+ term_write((uintptr_t)orig_str, orig_str_len);
+
+ for (size_t i = orig_str_len; ; ) {
+ term_double_buffer_flush();
+ int c = getchar();
+ switch (c) {
+ case GETCHAR_CURSOR_LEFT:
+ if (i) {
+ i--;
+ cursor_back();
+ }
+ continue;
+ case GETCHAR_CURSOR_RIGHT:
+ if (i < strlen(buf)) {
+ i++;
+ cursor_fwd();
+ }
+ continue;
+ case '\b':
+ if (i) {
+ i--;
+ cursor_back();
+ case GETCHAR_DELETE:;
+ size_t j;
+ for (j = i; ; j++) {
+ buf[j] = buf[j+1];
+ if (!buf[j]) {
+ buf[j] = ' ';
+ break;
+ }
+ }
+ reprint_string(orig_x, orig_y, buf);
+ buf[j] = 0;
+ }
+ continue;
+ case '\n':
+ term_write((uintptr_t)"\n", 1);
+ goto out;
+ default: {
+ if (strlen(buf) < limit - 1) {
+ for (size_t j = strlen(buf); ; j--) {
+ buf[j+1] = buf[j];
+ if (j == i)
+ break;
+ }
+ buf[i] = c;
+ i++;
+ cursor_fwd();
+ reprint_string(orig_x, orig_y, buf);
+ }
+ }
+ }
+ }
+
+out:
+ term_double_buffer_flush();
+ term_autoflush = prev_autoflush;
+}
diff --git a/stage23/lib/term.h b/stage23/lib/term.h
index a2c8dfbb..0338e1d3 100644
--- a/stage23/lib/term.h
+++ b/stage23/lib/term.h
@@ -92,4 +92,13 @@ extern void (*term_full_refresh)(void);
extern void (*term_callback)(uint64_t, uint64_t, uint64_t, uint64_t);
+extern bool term_autoflush;
+
+inline void reset_term(void) {
+ term_autoflush = true;
+ enable_cursor();
+ clear(true);
+ term_double_buffer_flush();
+}
+
#endif
diff --git a/stage23/menu.c b/stage23/menu.c
index 5b70d82a..2acb0686 100644
--- a/stage23/menu.c
+++ b/stage23/menu.c
@@ -12,6 +12,7 @@
#include <lib/uri.h>
#include <mm/pmm.h>
#include <drivers/vbe.h>
+#include <console.h>
static char *menu_branding = NULL;
static char *menu_branding_colour = NULL;
@@ -573,11 +574,11 @@ char *menu(char **cmdline) {
term_vbe(req_width, req_height);
}
+refresh:
disable_cursor();
term_autoflush = false;
-refresh:
clear(true);
{
size_t x, y;
@@ -643,6 +644,8 @@ refresh:
print(" \e[32mARROWS\e[0m Select \e[32mENTER\e[0m %s",
selected_menu_entry->expanded ? "Collapse" : "Expand");
}
+ set_cursor_pos(term_cols - 12, 3);
+ print("\e[32mC\e[0m Console");
set_cursor_pos(x, y);
}
@@ -699,7 +702,6 @@ timeout_aborted:
selected_menu_entry->expanded = !selected_menu_entry->expanded;
goto refresh;
}
- enable_cursor();
*cmdline = config_get_value(selected_menu_entry->body, 0, "KERNEL_CMDLINE");
if (!*cmdline) {
*cmdline = config_get_value(selected_menu_entry->body, 0, "CMDLINE");
@@ -707,8 +709,7 @@ timeout_aborted:
if (!*cmdline) {
*cmdline = "";
}
- clear(true);
- term_autoflush = true;
+ reset_term();
return selected_menu_entry->body;
case 'e': {
if (editor_enabled) {
@@ -721,6 +722,12 @@ timeout_aborted:
selected_menu_entry->body = new_body;
goto autoboot;
}
+ break;
+ }
+ case 'c': {
+ reset_term();
+ console();
+ goto refresh;
}
}
}
diff --git a/test/limine.cfg b/test/limine.cfg
index 9d74e626..7123ab86 100644
--- a/test/limine.cfg
+++ b/test/limine.cfg
@@ -1,6 +1,6 @@
DEFAULT_ENTRY=1
TIMEOUT=3
-GRAPHICS=yes
+#GRAPHICS=yes
VERBOSE=yes
THEME_BACKGROUND=50000000
