diff --git a/modules.yaml b/modules.yaml index b37e675..ceff427 100644 --- a/modules.yaml +++ b/modules.yaml @@ -71,6 +71,7 @@ modules: - src/boot/loader.cpp - src/boot/memory.cpp - src/boot/paging.cpp + - src/boot/status.cpp - src/boot/support.cpp nulldrv: diff --git a/src/boot/console.cpp b/src/boot/console.cpp index c4ad4cd..b679735 100644 --- a/src/boot/console.cpp +++ b/src/boot/console.cpp @@ -17,23 +17,7 @@ namespace boot { size_t ROWS = 0; size_t COLS = 0; -static constexpr int level_ok = 0; -static constexpr int level_warn = 1; -static constexpr int level_fail = 2; - -static const wchar_t *level_tags[] = { - L" ok ", - L" warn ", - L"failed" -}; -static const uefi::attribute level_colors[] = { - uefi::attribute::green, - uefi::attribute::brown, - uefi::attribute::light_red -}; - console *console::s_console = nullptr; -status_line *status_line::s_current = nullptr; static const wchar_t digits[] = {u'0', u'1', u'2', u'3', u'4', u'5', u'6', u'7', u'8', u'9', u'a', u'b', u'c', u'd', u'e', u'f'}; @@ -317,119 +301,4 @@ console::print(const wchar_t *fmt, ...) return result; } -status_line::status_line(const wchar_t *message, const wchar_t *context) : - m_level(level_ok) -{ - auto out = console::get().m_out; - m_line = out->mode->cursor_row; - m_depth = (s_current ? 1 + s_current->m_depth : 0); - - int indent = 2 * m_depth; - out->set_cursor_position(indent, m_line); - out->set_attribute(uefi::attribute::light_gray); - out->output_string(message); - - if (context) { - out->output_string(L": "); - out->output_string(context); - } - - out->output_string(L"\r\n"); - - m_next = s_current; - s_current = this; -} - -status_line::~status_line() -{ - if (s_current != this) - error::raise(uefi::status::unsupported, L"Destroying non-current status_line"); - - finish(); - if (m_next && m_level > m_next->m_level) { - m_next->m_level = m_level; - m_next->print_status_tag(); - } - s_current = m_next; -} - -void -status_line::print_status_tag() -{ - auto out = console::get().m_out; - int row = out->mode->cursor_row; - int col = out->mode->cursor_column; - - uefi::attribute color = level_colors[m_level]; - const wchar_t *tag = level_tags[m_level]; - - out->set_cursor_position(50, m_line); - - out->set_attribute(uefi::attribute::light_gray); - out->output_string(L"["); - out->set_attribute(color); - out->output_string(tag); - out->set_attribute(uefi::attribute::light_gray); - out->output_string(L"]\r\n"); - - out->set_cursor_position(col, row); -} - -void -status_line::do_warn(const wchar_t *message, const wchar_t *error) -{ - auto out = console::get().m_out; - int row = out->mode->cursor_row; - - if (m_level < level_warn) { - m_level = level_warn; - print_status_tag(); - } - - int indent = 2 + 2 * m_depth; - out->set_cursor_position(indent, row); - out->set_attribute(uefi::attribute::yellow); - out->output_string(message); - - if (error) { - out->output_string(L": "); - out->output_string(error); - } - - out->set_attribute(uefi::attribute::light_gray); - out->output_string(L"\r\n"); -} - -void -status_line::do_fail(const wchar_t *message, const wchar_t *error) -{ - auto out = console::get().m_out; - int row = out->mode->cursor_row; - - if (s_current->m_level < level_fail) { - m_level = level_fail; - print_status_tag(); - } - - int indent = 2 + 2 * m_depth; - out->set_cursor_position(indent, row); - out->set_attribute(uefi::attribute::red); - out->output_string(message); - - if (error) { - out->output_string(L": "); - out->output_string(error); - } - - out->set_attribute(uefi::attribute::light_gray); - out->output_string(L"\r\n"); -} - -void -status_line::finish() -{ - if (m_level <= level_ok) - print_status_tag(); -} - } // namespace boot diff --git a/src/boot/console.h b/src/boot/console.h index 4fc852b..d1d5321 100644 --- a/src/boot/console.h +++ b/src/boot/console.h @@ -1,5 +1,5 @@ /// \file console.h -/// Text output and status message handling +/// Text output handler #pragma once #include #include @@ -42,46 +42,4 @@ private: static console *s_console; }; -/// Scoped status line reporter. Prints a message and an "OK" if no errors -/// or warnings were reported before destruction, otherwise reports the -/// error or warning. -class status_line -{ -public: - /// Constructor. - /// \arg message Description of the operation in progress - /// \arg context If non-null, printed after `message` and a colon - status_line(const wchar_t *message, const wchar_t *context = nullptr); - ~status_line(); - - /// Set the state to warning, and print a message. If the state is already at - /// warning or error, the state is unchanged but the message is still printed. - /// \arg message The warning message to print - /// \arg error If non-null, printed after `message` - inline static void warn(const wchar_t *message, const wchar_t *error = nullptr) { - if (s_current) s_current->do_warn(message, error); - } - - /// Set the state to error, and print a message. If the state is already at - /// error, the state is unchanged but the message is still printed. - /// \arg message The error message to print - /// \arg error If non-null, printed after `message` - inline static void fail(const wchar_t *message, const wchar_t *error = nullptr) { - if (s_current) s_current->do_fail(message, error); - } - -private: - void print_status_tag(); - void do_warn(const wchar_t *message, const wchar_t *error); - void do_fail(const wchar_t *message, const wchar_t *error); - void finish(); - - size_t m_line; - int m_level; - int m_depth; - - status_line *m_next; - static status_line *s_current; -}; - } // namespace boot diff --git a/src/boot/error.cpp b/src/boot/error.cpp index 92d623d..eaf220c 100644 --- a/src/boot/error.cpp +++ b/src/boot/error.cpp @@ -1,11 +1,11 @@ #include "error.h" #include "console.h" +#include "kernel_args.h" +#include "status.h" namespace boot { namespace error { -handler *handler::s_current = nullptr; - struct error_code_desc { uefi::status code; const wchar_t *name; @@ -20,8 +20,8 @@ struct error_code_desc error_table[] = { { uefi::status::success, nullptr } }; -static const wchar_t * -error_message(uefi::status status) +const wchar_t * +message(uefi::status status) { int32_t i = -1; while (error_table[++i].name != nullptr) { @@ -34,56 +34,31 @@ error_message(uefi::status status) return L"Unknown Warning"; } -[[ noreturn ]] void -raise(uefi::status status, const wchar_t *message) -{ - if (handler::s_current) { - handler::s_current->handle(status, message); - } - while (1) asm("hlt"); -} - -handler::handler() : - m_next(s_current) -{ - s_current = this; -} - -handler::~handler() -{ - if (s_current != this) - raise(uefi::status::warn_stale_data, - L"Non-current error handler destructing"); - s_current = m_next; -} - -uefi_handler::uefi_handler(console &con) : - handler(), - m_con(con) -{ -} - -void -uefi_handler::handle(uefi::status s, const wchar_t *message) -{ - status_line::fail(message, error_message(s)); -} - -cpu_assert_handler::cpu_assert_handler() : handler() {} - -void -cpu_assert_handler::handle(uefi::status s, const wchar_t *message) +[[ noreturn ]] static void +cpu_assert(uefi::status s, const wchar_t *message) { asm volatile ( "movq $0xeeeeeeebadbadbad, %%r8;" "movq %0, %%r9;" + "movq %1, %%r10;" "movq $0, %%rdx;" "divq %%rdx;" : - : "r"((uint64_t)s) - : "rax", "rdx", "r8", "r9"); + : "r"((uint64_t)s), "r"(message) + : "rax", "rdx", "r8", "r9", "r10"); + while (1) asm("hlt"); } +[[ noreturn ]] void +raise(uefi::status status, const wchar_t *message) +{ + if(status_line::fail(message, status)) + while (1) asm("hlt"); + else + cpu_assert(status, message); +} + + } // namespace error } // namespace boot diff --git a/src/boot/error.h b/src/boot/error.h index 9df8f21..a39ec13 100644 --- a/src/boot/error.h +++ b/src/boot/error.h @@ -14,48 +14,7 @@ namespace error { /// Halt or exit the program with the given error status/message [[ noreturn ]] void raise(uefi::status status, const wchar_t *message); -/// Interface for error-handling functors -class handler -{ -public: - /// Constructor must be called by implementing classes. - handler(); - virtual ~handler(); - - /// Interface for implementations of error handling. - virtual void handle(uefi::status, const wchar_t*) = 0; - -private: - friend void raise(uefi::status, const wchar_t *); - - handler *m_next; - static handler *s_current; -}; - -/// Error handler using UEFI boot services. Integrates with `status_line` -/// to print formatted error messages to the screen. -class uefi_handler : - public handler -{ -public: - uefi_handler(console &con); - virtual ~uefi_handler() {} - void handle(uefi::status, const wchar_t*) override; - -private: - console &m_con; -}; - -/// Error handler that doesn't rely on UEFI. Sets status into CPU -/// registers and then causes a CPU #DE exception. -class cpu_assert_handler : - public handler -{ -public: - cpu_assert_handler(); - virtual ~cpu_assert_handler() {} - void handle(uefi::status, const wchar_t*) override; -}; +const wchar_t * message(uefi::status status); } // namespace error } // namespace boot diff --git a/src/boot/fs.cpp b/src/boot/fs.cpp index dd1fcc5..3eac6b0 100644 --- a/src/boot/fs.cpp +++ b/src/boot/fs.cpp @@ -8,6 +8,7 @@ #include "console.h" #include "error.h" #include "memory.h" +#include "status.h" namespace boot { namespace fs { diff --git a/src/boot/hardware.cpp b/src/boot/hardware.cpp index fb3eca7..152b533 100644 --- a/src/boot/hardware.cpp +++ b/src/boot/hardware.cpp @@ -1,6 +1,7 @@ #include "hardware.h" #include "console.h" #include "error.h" +#include "status.h" namespace boot { namespace hw { diff --git a/src/boot/loader.cpp b/src/boot/loader.cpp index 94ef5c9..1418d0c 100644 --- a/src/boot/loader.cpp +++ b/src/boot/loader.cpp @@ -8,6 +8,7 @@ #include "fs.h" #include "memory.h" #include "paging.h" +#include "status.h" namespace args = kernel::args; diff --git a/src/boot/main.cpp b/src/boot/main.cpp index 16574c1..004087a 100644 --- a/src/boot/main.cpp +++ b/src/boot/main.cpp @@ -14,6 +14,7 @@ #include "loader.h" #include "memory.h" #include "paging.h" +#include "status.h" #include "kernel_args.h" @@ -38,7 +39,6 @@ const program_desc program_list[] = { {L"kernel", L"jsix.elf"}, {L"null driver", L"nulldrv.elf"}, {L"fb driver", L"fb.elf"}, - //{L"terminal driver", L"terminal.elf"}, }; /// Change a pointer to point to the higher-half linear-offset version @@ -57,7 +57,7 @@ allocate_args_structure( size_t max_modules, size_t max_programs) { - status_line status(L"Setting up kernel args memory"); + status_line status {L"Setting up kernel args memory"}; args::header *args = nullptr; @@ -98,13 +98,9 @@ add_module(args::header *args, args::mod_type type, buffer &data) /// UEFI is still in control of the machine. (ie, while the loader still /// has access to boot services. args::header * -bootloader_main_uefi( - uefi::handle image, - uefi::system_table *st, - console &con) +uefi_preboot(uefi::handle image, uefi::system_table *st) { - error::uefi_handler handler(con); - status_line status(L"Performing UEFI pre-boot"); + status_line status {L"Performing UEFI pre-boot"}; uefi::boot_services *bs = st->boot_services; uefi::runtime_services *rs = st->runtime_services; @@ -128,8 +124,6 @@ bootloader_main_uefi( memory::module_type); add_module(args, args::mod_type::symbol_table, symbols); - args->video = con.fb(); - for (auto &desc : program_list) { buffer buf = loader::load_file(disk, desc.name, desc.path); args::program &program = args->programs[args->num_programs++]; @@ -144,35 +138,48 @@ bootloader_main_uefi( return args; } +memory::efi_mem_map +uefi_exit(args::header *args, uefi::handle image, uefi::boot_services *bs) +{ + status_line status {L"Exiting UEFI"}; + + memory::efi_mem_map map = + memory::build_kernel_mem_map(args, bs); + + try_or_raise( + bs->exit_boot_services(image, map.key), + L"Failed to exit boot services"); + + return map; +} + } // namespace boot /// The UEFI entrypoint for the loader. extern "C" uefi::status -efi_main(uefi::handle image_handle, uefi::system_table *st) +efi_main(uefi::handle image, uefi::system_table *st) { using namespace boot; - - error::cpu_assert_handler handler; console con(st->boot_services, st->con_out); - args::header *args = - bootloader_main_uefi(image_handle, st, con); + args::header *args = uefi_preboot(image, st); + memory::efi_mem_map map = uefi_exit(args, image, st->boot_services); + + args->video = con.fb(); + status_bar status {con.fb()}; // Switch to fb status display args::program &kernel = args->programs[0]; paging::map_pages(args, kernel.phys_addr, kernel.virt_addr, kernel.size); kernel::entrypoint kentry = reinterpret_cast(kernel.entrypoint); - - memory::efi_mem_map map = - memory::build_kernel_mem_map(args, st->boot_services); - - try_or_raise( - st->boot_services->exit_boot_services(image_handle, map.key), - L"Failed to exit boot services"); + status.next(); memory::virtualize(args->pml4, map, st->runtime_services); + status.next(); + change_pointer(args->pml4); hw::setup_cr4(); + status.next(); kentry(args); debug_break(); diff --git a/src/boot/memory.cpp b/src/boot/memory.cpp index 7784bf2..4e3c3e3 100644 --- a/src/boot/memory.cpp +++ b/src/boot/memory.cpp @@ -7,6 +7,7 @@ #include "error.h" #include "memory.h" #include "paging.h" +#include "status.h" namespace boot { namespace memory { @@ -43,10 +44,12 @@ memory_type_name(uefi::memory_type t) } switch(t) { + /* case args_type: return L"jsix kernel args"; case module_type: return L"jsix bootloader module"; case program_type: return L"jsix kernel or program code"; case table_type: return L"jsix page tables"; + */ default: return L"Bad Type Value"; } } @@ -100,14 +103,15 @@ can_merge(mem_entry &prev, mem_type type, uefi::memory_descriptor *next) void get_uefi_mappings(efi_mem_map *map, bool allocate, uefi::boot_services *bs) { - status_line(L"Getting UEFI memory map"); - + size_t length = 0; uefi::status status = bs->get_memory_map( - &map->length, nullptr, &map->key, &map->size, &map->version); + &length, nullptr, &map->key, &map->size, &map->version); if (status != uefi::status::buffer_too_small) error::raise(status, L"Error getting memory map size"); + map->length = length; + if (allocate) { map->length += 10*map->size; @@ -126,12 +130,12 @@ get_uefi_mappings(efi_mem_map *map, bool allocate, uefi::boot_services *bs) efi_mem_map build_kernel_mem_map(kernel::args::header *args, uefi::boot_services *bs) { - status_line(L"Creating kernel memory map"); + status_line status {L"Creating kernel memory map"}; - efi_mem_map efi_map; - get_uefi_mappings(&efi_map, false, bs); + efi_mem_map map; + get_uefi_mappings(&map, false, bs); - size_t map_size = efi_map.num_entries() * sizeof(mem_entry); + size_t map_size = map.num_entries() * sizeof(mem_entry); kernel::args::mem_entry *kernel_map = nullptr; try_or_raise( @@ -143,11 +147,11 @@ build_kernel_mem_map(kernel::args::header *args, uefi::boot_services *bs) L"Error allocating kernel memory map module space"); bs->set_mem(kernel_map, map_size, 0); - get_uefi_mappings(&efi_map, true, bs); + get_uefi_mappings(&map, true, bs); size_t i = 0; bool first = true; - for (auto desc : efi_map) { + for (auto desc : map) { /* console::print(L" Range %lx (%lx) %x(%s) [%lu]\r\n", desc->physical_start, desc->attribute, desc->type, memory_type_name(desc->type), desc->number_of_pages); @@ -187,6 +191,7 @@ build_kernel_mem_map(kernel::args::header *args, uefi::boot_services *bs) type = mem_type::persistent; break; + /* case args_type: type = mem_type::args; break; @@ -202,6 +207,7 @@ build_kernel_mem_map(kernel::args::header *args, uefi::boot_services *bs) case table_type: type = mem_type::table; break; + */ default: error::raise( @@ -235,7 +241,7 @@ build_kernel_mem_map(kernel::args::header *args, uefi::boot_services *bs) args->mem_map = kernel_map; args->map_count = i; - return efi_map; + return map; } void diff --git a/src/boot/paging.cpp b/src/boot/paging.cpp index 948ff22..475b88a 100644 --- a/src/boot/paging.cpp +++ b/src/boot/paging.cpp @@ -6,6 +6,7 @@ #include "memory.h" #include "paging.h" #include "pointer_manipulation.h" +#include "status.h" namespace boot { namespace paging { diff --git a/src/boot/status.cpp b/src/boot/status.cpp new file mode 100644 index 0000000..b8170c3 --- /dev/null +++ b/src/boot/status.cpp @@ -0,0 +1,263 @@ +#include +#include + +#include "console.h" +#include "error.h" +#include "kernel_args.h" +#include "status.h" + +constexpr int num_boxes = 30; + +namespace boot { + +static constexpr int level_ok = 0; +static constexpr int level_warn = 1; +static constexpr int level_fail = 2; + +static const wchar_t *level_tags[] = { + L" ok ", + L" warn ", + L"failed" +}; + +static const uefi::attribute level_colors[] = { + uefi::attribute::green, + uefi::attribute::brown, + uefi::attribute::light_red +}; + +status *status::s_current = nullptr; +unsigned status::s_current_type = 0; +unsigned status_bar::s_count = 0; + +status_line::status_line(const wchar_t *message, const wchar_t *context) : + m_level(level_ok), + m_depth(0), + m_outer(nullptr) +{ + if (status::s_current_type == status_line::type) { + m_outer = static_cast(s_current); + m_depth = (m_outer ? 1 + m_outer->m_depth : 0); + } + s_current = this; + s_current_type = status_line::type; + + auto out = console::get().m_out; + m_line = out->mode->cursor_row; + + int indent = 2 * m_depth; + out->set_cursor_position(indent, m_line); + out->set_attribute(uefi::attribute::light_gray); + out->output_string(message); + + if (context) { + out->output_string(L": "); + out->output_string(context); + } + + out->output_string(L"\r\n"); + print_status_tag(); +} + +status_line::~status_line() +{ + if (s_current != this) + error::raise(uefi::status::unsupported, L"Destroying non-current status_line"); + + if (m_outer && m_level > m_outer->m_level) { + m_outer->m_level = m_level; + m_outer->print_status_tag(); + } + s_current = m_outer; +} + +void +status_line::print_status_tag() +{ + auto out = console::get().m_out; + int row = out->mode->cursor_row; + int col = out->mode->cursor_column; + + uefi::attribute color = level_colors[m_level]; + const wchar_t *tag = level_tags[m_level]; + + out->set_cursor_position(50, m_line); + + out->set_attribute(uefi::attribute::light_gray); + out->output_string(L"["); + out->set_attribute(color); + out->output_string(tag); + out->set_attribute(uefi::attribute::light_gray); + out->output_string(L"]\r\n"); + + out->set_cursor_position(col, row); +} + +void +status_line::do_warn(const wchar_t *message, uefi::status status) +{ + auto out = console::get().m_out; + int row = out->mode->cursor_row; + + if (m_level < level_warn) { + m_level = level_warn; + print_status_tag(); + } + + int indent = 2 + 2 * m_depth; + out->set_cursor_position(indent, row); + out->set_attribute(uefi::attribute::yellow); + out->output_string(message); + + const wchar_t *error = error::message(status); + if (error) { + out->output_string(L": "); + out->output_string(error); + } + + out->set_attribute(uefi::attribute::light_gray); + out->output_string(L"\r\n"); +} + +void +status_line::do_fail(const wchar_t *message, uefi::status status) +{ + auto out = console::get().m_out; + int row = out->mode->cursor_row; + + if (m_level < level_fail) { + m_level = level_fail; + print_status_tag(); + } + + int indent = 2 + 2 * m_depth; + out->set_cursor_position(indent, row); + out->set_attribute(uefi::attribute::red); + out->output_string(message); + + const wchar_t *error = error::message(status); + if (error) { + out->output_string(L": "); + out->output_string(error); + } + + out->set_attribute(uefi::attribute::light_gray); + out->output_string(L"\r\n"); +} + + + +status_bar::status_bar(kernel::args::framebuffer const &fb) : + m_outer(nullptr) +{ + m_size = (fb.vertical / num_boxes) - 1; + m_top = fb.vertical - m_size; + m_horiz = fb.horizontal; + m_fb = reinterpret_cast(fb.phys_addr); + m_type = static_cast(fb.type); + next(); + + if (status::s_current_type == status_bar::type) + m_outer = static_cast(s_current); + s_current = this; + s_current_type = status_bar::type; +} + +status_bar::~status_bar() +{ + if (s_current != this) + error::raise(uefi::status::unsupported, L"Destroying non-current status_bar"); + draw_box(); + s_current = m_outer; +} + +void +status_bar::do_warn(const wchar_t *message, uefi::status status) +{ + m_status = status; + if (m_level < level_warn) { + m_level = level_warn; + draw_box(); + } +} + +void +status_bar::do_fail(const wchar_t *message, uefi::status status) +{ + m_status = status; + if (m_level < level_fail) { + m_level = level_fail; + draw_box(); + } +} + +static uint32_t +make_color(uint8_t r, uint8_t g, uint8_t b, uint16_t type) +{ + switch (static_cast(type)) { + case kernel::args::fb_type::bgr8: + return + (static_cast(b) << 0) | + (static_cast(g) << 8) | + (static_cast(r) << 16); + + case kernel::args::fb_type::rgb8: + return + (static_cast(r) << 0) | + (static_cast(g) << 8) | + (static_cast(b) << 16); + + default: + return 0; + } +} + +void +status_bar::draw_box() +{ + static const uint32_t colors[] = {0x909090, 0xf0f0f0}; + constexpr unsigned ncolors = sizeof(colors) / sizeof(uint32_t); + + if (m_fb == nullptr) + return; + + unsigned x0 = m_current * m_size; + unsigned x1 = x0 + m_size - 3; + unsigned y0 = m_top; + unsigned y1 = m_top + m_size - 3; + + uint32_t color = 0; + switch (m_level) { + case level_ok: + color = colors[m_current % ncolors]; + break; + case level_warn: + color = make_color(0xff, 0xb2, 0x34, m_type); + break; + case level_fail: + color = make_color(0xfb, 0x0a, 0x1e, m_type); + break; + default: + color = 0; + } + + for (unsigned y = y0; y < y1; ++y) + for (unsigned x = x0; x < x1; ++x) + m_fb[y * m_horiz + x] = color; + + if (m_level > level_ok) { + unsigned nbars = static_cast(m_status) & 0xffff; + constexpr unsigned bar_height = 4; + + for (unsigned i = 1; i <= nbars; ++i) { + y0 = m_top - 2 * i * bar_height; + y1 = y0 + bar_height; + + for (unsigned y = y0; y < y1; ++y) + for (unsigned x = x0; x < x1; ++x) + m_fb[y * m_horiz + x] = color; + } + } +} + +} // namespace boot diff --git a/src/boot/status.h b/src/boot/status.h new file mode 100644 index 0000000..04132e1 --- /dev/null +++ b/src/boot/status.h @@ -0,0 +1,122 @@ +/// \file status.h +/// Status message and indicator handling +#pragma once + +#include +#include + +namespace kernel { +namespace args { + class framebuffer; +} +} + +namespace boot { + +// Abstract base class for status reporters. +class status +{ +public: + virtual void do_warn(const wchar_t *message, uefi::status status) = 0; + virtual void do_fail(const wchar_t *message, uefi::status status) = 0; + + /// Set the state to warning, and print a message. If the state is already at + /// warning or error, the state is unchanged but the message is still printed. + /// \arg message The warning message to print, if text is supported + /// \arg status If set, the error or warning code that should be represented + /// \returns True if there was a status handler to display the warning + inline static bool warn(const wchar_t *message, uefi::status status = uefi::status::success) { + if (!s_current) return false; + s_current->do_warn(message, status); + return true; + } + + /// Set the state to error, and print a message. If the state is already at + /// error, the state is unchanged but the message is still printed. + /// \arg message The error message to print, if text is supported + /// \arg status The error or warning code that should be represented + /// \returns True if there was a status handler to display the failure + inline static bool fail(const wchar_t *message, uefi::status status) { + if (!s_current) return false; + s_current->do_fail(message, status); + return true; + } + +protected: + static status *s_current; + static unsigned s_current_type; +}; + +/// Scoped status line reporter. Prints a message and an "OK" if no errors +/// or warnings were reported before destruction, otherwise reports the +/// error or warning. +class status_line : + public status +{ +public: + constexpr static unsigned type = 1; + + /// Constructor. + /// \arg message Description of the operation in progress + /// \arg context If non-null, printed after `message` and a colon + status_line(const wchar_t *message, const wchar_t *context = nullptr); + ~status_line(); + + virtual void do_warn(const wchar_t *message, uefi::status status) override; + virtual void do_fail(const wchar_t *message, uefi::status status) override; + +private: + void print_status_tag(); + + size_t m_line; + int m_level; + int m_depth; + + status_line *m_outer; +}; + +/// Scoped status bar reporter. Draws a row of boxes along the bottom of +/// the screen, turning one red if there's an error in that step. +class status_bar : + public status +{ +public: + constexpr static unsigned type = 2; + + using framebuffer = kernel::args::framebuffer; + + /// Constructor. + /// \arg fb The framebuffer descriptor to draw to + status_bar(kernel::args::framebuffer const &fb); + ~status_bar(); + + virtual void do_warn(const wchar_t *message, uefi::status status) override; + virtual void do_fail(const wchar_t *message, uefi::status status) override; + + inline void next() { + m_current = s_count++; + m_level = 0; + m_status = uefi::status::success; + draw_box(); + } + +private: + void draw_box(); + + uint32_t *m_fb; + uint32_t m_size; + uint32_t m_top; + uint32_t m_horiz; + + int m_level; + uefi::status m_status; + + uint16_t m_type; + uint16_t m_current; + + status_bar *m_outer; + + static unsigned s_count; +}; + +} // namespace boot